From 2d82a430c449402283d0cb773ad083bdb574ca0a Mon Sep 17 00:00:00 2001 From: tristan Date: Thu, 19 Sep 2024 14:56:45 +1000 Subject: [PATCH] Issue-5662-kotlinstyle (#5833) * *.kt: bulk correction of formatting using ktlint --format * *.kt: replace wildcard imports and second stage auto format ktlint --format * QuizQuestionTest.kt: modified property names to camel case to meet ktlint standard * LevelControllerTest.kt: modified property names to camel case to meet ktlint standard * QuizActivityUnitTest.kt: modified property names to camel case to meet ktlint standard * MediaDetailFragmentUnitTests.kt: modified property names to camel case to meet ktlint standard * UploadWorker.kt: modified property names to camel case to meet ktlint standard * UploadClient.kt: modified property names to camel case to meet ktlint standard * BasePagingPresenter.kt: modified property names to camel case to meet ktlint standard * DescriptionEditActivity.kt: modified property names to camel case to meet ktlint standard * OnSwipeTouchListener.kt: modified property names to camel case to meet ktlint standard * MediaDetailFragmentUnitTests.kt: corrected excessive line length to meet ktlint standard * DepictedItem.kt: corrected property name format and catch format to for ktlint standard * UploadCategoryAdapter.kt: corrected class definition format to meet ktlint standard * CustomSelectorActivity.kt: reformatted function names to first letter lowercase to meet ktlint standard * MediaDetailFragmentUnitTests.kt: fix string literal indentation to meet ktlint standard * NotForUploadDao.kt: file renamed to match class name, new file NotForUploadStatusDao.kt * UploadedDao.kt: file renamed to match class name, new file UploadedStatusDao.kt * Urls.kt: fixed excessive line length for ktLint standard * Snak_partial.kt & Statement_partial.kt: refactored to remove underscores in class names to meet ktLint standard * *.kt: fixed consecutive KDOC error for ktLint * PageableBaseDataSourceTest.kt & UploadPresenterTest.kt: fixed excessive line lengths to meet ktLint standard * CheckboxTriStatesTest.kt: renamed file to match class name to meet ktLint standard * .kt: resolved backing-property-naming error in ktLint, made matching properties public, matched names and refactored * TestConnectionFactory.kt: fixed property naming to adhere to ktLint standard --- .../fr/free/nrw/commons/AboutActivityTest.kt | 48 +- .../fr/free/nrw/commons/LoginActivityTest.kt | 12 +- .../fr/free/nrw/commons/MainActivityTest.kt | 184 +++--- .../free/nrw/commons/ProfileActivityTest.kt | 18 +- .../fr/free/nrw/commons/ReviewActivityTest.kt | 4 +- .../fr/free/nrw/commons/SearchActivityTest.kt | 26 +- .../commons/SettingsActivityLoggedInTest.kt | 30 +- .../free/nrw/commons/SettingsActivityTest.kt | 29 +- .../java/fr/free/nrw/commons/UITestHelper.kt | 100 +-- .../free/nrw/commons/UploadCancelledTest.kt | 159 ++--- .../java/fr/free/nrw/commons/UploadTest.kt | 105 +-- .../free/nrw/commons/WelcomeActivityTest.kt | 4 +- .../ui/PasteSensitiveTextInputEditTextTest.kt | 13 +- .../fr/free/nrw/commons/util/MyViewAction.kt | 66 +- .../java/fr/free/nrw/commons/BaseMarker.kt | 39 +- .../java/fr/free/nrw/commons/BetaConstants.kt | 3 +- .../fr/free/nrw/commons/CameraPosition.kt | 26 +- .../main/java/fr/free/nrw/commons/Media.kt | 57 +- .../fr/free/nrw/commons/MediaDataExtractor.kt | 82 +-- app/src/main/java/fr/free/nrw/commons/Urls.kt | 4 +- .../nrw/commons/actions/PageEditClient.kt | 130 ++-- .../nrw/commons/actions/PageEditInterface.kt | 25 +- .../free/nrw/commons/actions/ThanksClient.kt | 62 +- .../nrw/commons/actions/ThanksInterface.kt | 2 +- .../nrw/commons/auth/csrf/CsrfTokenClient.kt | 146 +++-- .../nrw/commons/auth/csrf/LogoutClient.kt | 10 +- .../nrw/commons/auth/login/LoginCallback.kt | 8 +- .../nrw/commons/auth/login/LoginClient.kt | 257 +++++--- .../auth/login/LoginFailedException.kt | 4 +- .../nrw/commons/auth/login/LoginInterface.kt | 12 +- .../nrw/commons/auth/login/LoginResponse.kt | 6 +- .../nrw/commons/auth/login/LoginResult.kt | 10 +- .../bookmarks/items/BookmarkItemsAdapter.kt | 37 +- .../nrw/commons/bookmarks/models/Bookmark.kt | 18 +- .../nrw/commons/campaigns/CampaignConfig.kt | 3 +- .../commons/campaigns/CampaignResponseDTO.kt | 4 +- .../nrw/commons/campaigns/models/Campaign.kt | 14 +- .../nrw/commons/category/CategoriesModel.kt | 474 +++++++------- .../nrw/commons/category/CategoryClient.kt | 206 +++--- .../nrw/commons/category/CategoryInterface.kt | 22 +- .../free/nrw/commons/category/CategoryItem.kt | 17 +- .../commons/category/ContinuationClient.kt | 25 +- .../nrw/commons/contributions/ChunkInfo.kt | 23 +- .../nrw/commons/contributions/Contribution.kt | 44 +- .../ContributionBoundaryCallback.kt | 148 ++--- .../ContributionsRemoteDataSource.kt | 103 ++- .../WikipediaInstructionsDialogFragment.kt | 42 +- .../database/NotForUploadStatus.kt | 6 +- ...rUploadDao.kt => NotForUploadStatusDao.kt} | 26 +- .../customselector/database/UploadedStatus.kt | 17 +- .../{UploadedDao.kt => UploadedStatusDao.kt} | 38 +- .../helper/CustomSelectorConstants.kt | 4 +- .../customselector/helper/ImageHelper.kt | 37 +- .../helper/OnSwipeTouchListener.kt | 45 +- .../listeners/FolderClickListener.kt | 9 +- .../listeners/ImageLoaderListener.kt | 3 +- .../listeners/ImageSelectListener.kt | 11 +- .../listeners/PassDataListener.kt | 7 +- .../listeners/RefreshUIListener.kt | 2 +- .../customselector/model/CallbackStatus.kt | 8 +- .../commons/customselector/model/Folder.kt | 15 +- .../nrw/commons/customselector/model/Image.kt | 100 ++- .../commons/customselector/model/Result.kt | 7 +- .../ui/adapter/FolderAdapter.kt | 71 +- .../customselector/ui/adapter/ImageAdapter.kt | 265 ++++---- .../ui/adapter/RecyclerViewAdapter.kt | 6 +- .../ui/selector/CustomSelectorActivity.kt | 241 ++++--- .../ui/selector/CustomSelectorViewModel.kt | 28 +- .../CustomSelectorViewModelFactory.kt | 14 +- .../ui/selector/FolderFragment.kt | 29 +- .../ui/selector/ImageFileLoader.kt | 69 +- .../ui/selector/ImageFragment.kt | 76 ++- .../customselector/ui/selector/ImageLoader.kt | 611 +++++++++--------- .../fr/free/nrw/commons/db/AppDatabase.kt | 20 +- .../description/DescriptionEditActivity.kt | 144 +++-- .../description/EditDescriptionConstants.kt | 4 +- .../commons/di/ExploreMapFragmentModule.kt | 3 +- .../commons/di/NearbyParentFragmentModule.kt | 3 +- .../fr/free/nrw/commons/edit/EditActivity.kt | 191 +++--- .../fr/free/nrw/commons/edit/EditViewModel.kt | 12 +- .../free/nrw/commons/edit/TransformImage.kt | 8 +- .../nrw/commons/edit/TransformImageImpl.kt | 69 +- .../free/nrw/commons/explore/SearchModule.kt | 9 +- .../explore/categories/CategoriesModule.kt | 11 +- .../categories/PageableCategoryFragment.kt | 1 - .../categories/PagedCategoriesAdapter.kt | 37 +- .../media/CategoriesMediaFragment.kt | 7 +- .../media/CategoryMediaPresenterImpl.kt | 12 +- .../PageableCategoriesMediaDataSource.kt | 20 +- .../PageableParentCategoriesDataSource.kt | 21 +- .../parent/ParentCategoriesFragment.kt | 8 +- .../parent/ParentCategoriesPresenterImpl.kt | 13 +- .../PageableSearchCategoriesDataSource.kt | 21 +- .../SearchCategoriesFragmentPresenterImpl.kt | 12 +- .../sub/PageableSubCategoriesDataSource.kt | 21 +- .../categories/sub/SubCategoriesFragment.kt | 7 +- .../sub/SubCategoriesPresenterImpl.kt | 12 +- .../explore/depictions/DepictionAdapter.kt | 37 +- .../explore/depictions/DepictionModule.kt | 10 +- .../explore/depictions/DepictsClient.kt | 162 ++--- .../depictions/PageableDepictionsFragment.kt | 1 - .../child/ChildDepictionsFragment.kt | 13 +- .../child/ChildDepictionsPresenterImpl.kt | 12 +- .../PageableChildDepictionsDataSource.kt | 17 +- .../media/DepictedImagesFragment.kt | 6 +- .../media/DepictedImagesPresenterImpl.kt | 12 +- .../media/PageableDepictedMediaDataSource.kt | 16 +- .../PageableParentDepictionsDataSource.kt | 17 +- .../parent/ParentDepictionsFragment.kt | 9 +- .../parent/ParentDepictionsPresenterImpl.kt | 14 +- .../search/PageableDepictionsDataSource.kt | 20 +- .../SearchDepictionsFragmentPresenterImpl.kt | 13 +- .../commons/explore/media/MediaConverter.kt | 154 ++--- .../explore/media/PageableMediaDataSource.kt | 18 +- .../explore/media/PageableMediaFragment.kt | 14 +- .../explore/media/PagedMediaAdapter.kt | 50 +- .../explore/media/SearchMediaFragment.kt | 1 - .../media/SearchMediaFragmentPresenterImpl.kt | 14 +- .../explore/media/SimpleDataObserver.kt | 31 +- .../commons/explore/models/RecentSearch.kt | 36 +- .../explore/paging/BasePagingFragment.kt | 28 +- .../explore/paging/BasePagingPresenter.kt | 50 +- .../commons/explore/paging/BaseViewHolder.kt | 6 +- .../commons/explore/paging/FooterAdapter.kt | 72 ++- .../commons/explore/paging/PagingContract.kt | 7 + .../explore/paging/PagingDataSource.kt | 45 +- .../explore/paging/PagingDataSourceFactory.kt | 71 +- .../free/nrw/commons/media/IdAndCaptions.kt | 6 +- .../fr/free/nrw/commons/media/MediaClient.kt | 381 +++++------ .../nrw/commons/media/MediaDetailInterface.kt | 12 +- .../free/nrw/commons/media/MediaInterface.kt | 62 +- .../nrw/commons/media/PageMediaInterface.kt | 4 +- .../nrw/commons/media/WikidataMediaClient.kt | 142 ++-- .../commons/media/WikidataMediaInterface.kt | 9 +- .../nrw/commons/media/ZoomableActivity.kt | 495 +++++++------- .../media/model/PageMediaListResponse.kt | 6 +- .../free/nrw/commons/mwapi/SparqlResponses.kt | 15 +- .../fr/free/nrw/commons/mwapi/UserClient.kt | 49 +- .../free/nrw/commons/mwapi/UserInterface.kt | 2 +- .../nrw/commons/nearby/BottomSheetAdapter.kt | 71 +- .../commons/nearby/PlaceAdapterDelegate.kt | 36 +- .../nrw/commons/nearby/WikidataFeedback.kt | 45 +- .../nearby/fragments/AdvanceQueryFragment.kt | 14 +- .../fragments/CommonPlaceClickActions.kt | 201 +++--- .../commons/nearby/fragments/PlaceAdapter.kt | 9 +- .../commons/nearby/model/BottomSheetItem.kt | 5 +- .../commons/nearby/model/NearbyResponse.kt | 4 +- .../commons/nearby/model/NearbyResultItem.kt | 122 ++-- .../nrw/commons/nearby/model/NearbyResults.kt | 4 +- .../nrw/commons/nearby/model/PlaceBindings.kt | 19 +- .../nrw/commons/nearby/model/ResultTuple.kt | 3 +- .../notification/NotificatinAdapter.kt | 7 +- .../NotificationAdapterDelegates.kt | 14 +- .../notification/NotificationClient.kt | 90 +-- .../notification/NotificationInterface.kt | 9 +- .../notification/models/Notification.kt | 4 +- .../profile/achievements/Achievements.kt | 89 ++- .../profile/achievements/FeaturedImages.kt | 4 +- .../profile/achievements/FeedbackResponse.kt | 14 +- .../profile/achievements/LevelController.kt | 57 +- .../fr/free/nrw/commons/quiz/QuizQuestion.kt | 15 +- .../nrw/commons/recentlanguages/Language.kt | 5 +- .../recentlanguages/RecentLanguagesAdapter.kt | 28 +- .../free/nrw/commons/review/ReviewHelper.kt | 229 +++---- .../nrw/commons/review/ReviewInterface.kt | 12 +- .../nrw/commons/upload/CountingRequestBody.kt | 31 +- .../fr/free/nrw/commons/upload/Description.kt | 36 +- .../commons/upload/FailedUploadsAdapter.kt | 37 +- .../commons/upload/FailedUploadsFragment.kt | 43 +- .../free/nrw/commons/upload/FileProcessor.kt | 357 +++++----- .../nrw/commons/upload/GpsCategoryModel.kt | 18 +- .../nrw/commons/upload/ImageCoordinates.kt | 33 +- .../fr/free/nrw/commons/upload/Language.kt | 9 +- .../nrw/commons/upload/LanguagesAdapter.kt | 57 +- .../commons/upload/PendingUploadsAdapter.kt | 85 ++- .../commons/upload/PendingUploadsFragment.kt | 60 +- .../nrw/commons/upload/StashUploadResult.kt | 4 +- .../free/nrw/commons/upload/UploadClient.kt | 457 ++++++------- .../nrw/commons/upload/UploadInterface.kt | 4 +- .../nrw/commons/upload/UploadMediaDetail.kt | 35 +- .../commons/upload/UploadProgressActivity.kt | 121 ++-- .../free/nrw/commons/upload/UploadResponse.kt | 4 +- .../free/nrw/commons/upload/UploadResult.kt | 29 +- .../nrw/commons/upload/WikiBaseInterface.kt | 18 +- .../free/nrw/commons/upload/WikidataItem.kt | 4 +- .../free/nrw/commons/upload/WikidataPlace.kt | 16 +- .../upload/categories/BaseDelegateAdapter.kt | 27 +- .../upload/categories/CategoriesPresenter.kt | 482 +++++++------- .../categories/UploadCategoryAdapter.kt | 9 +- .../UploadCategoryAdapterDelegates.kt | 71 +- .../free/nrw/commons/upload/depicts/Claims.kt | 6 +- .../upload/depicts/DepictEditHelper.kt | 197 +++--- .../nrw/commons/upload/depicts/Depicts.kt | 7 +- .../nrw/commons/upload/depicts/DepictsDao.kt | 37 +- .../upload/depicts/DepictsInterface.kt | 6 +- .../upload/depicts/DepictsPresenter.kt | 438 +++++++------ .../upload/depicts/UploadDepictsAdapter.kt | 8 +- .../depicts/UploadDepictsAdapterDelegates.kt | 61 +- .../structure/depictions/DepictModel.kt | 139 ++-- .../structure/depictions/DepictedItem.kt | 79 +-- .../nrw/commons/upload/worker/UploadWorker.kt | 460 +++++++------ .../upload/worker/WorkRequestHelper.kt | 39 +- .../fr/free/nrw/commons/utils/ConfigUtils.kt | 11 +- .../nrw/commons/utils/CustomSelectorUtils.kt | 48 +- .../fr/free/nrw/commons/utils/DialogUtil.kt | 81 +-- .../free/nrw/commons/utils/DownloadUtils.kt | 43 +- .../nrw/commons/utils/model/ConnectionType.kt | 17 +- .../utils/model/NetworkConnectionType.kt | 8 +- .../commons/wikidata/CommonsServiceFactory.kt | 25 +- .../nrw/commons/wikidata/WikiBaseClient.kt | 129 ++-- .../nrw/commons/wikidata/WikidataClient.kt | 47 +- .../wikidata/WikidataDisambiguationItems.kt | 10 +- .../commons/wikidata/WikidataEditService.java | 12 +- .../nrw/commons/wikidata/WikidataInterface.kt | 2 +- .../commons/wikidata/WikidataProperties.kt | 6 +- .../wikidata/cookies/CommonsCookieJar.kt | 20 +- .../wikidata/cookies/CommonsCookieStorage.kt | 49 +- .../wikidata/model/AddEditTagResponse.kt | 2 +- .../nrw/commons/wikidata/model/BaseModel.kt | 12 +- .../nrw/commons/wikidata/model/DataValue.kt | 31 +- .../wikidata/model/DepictSearchItem.kt | 2 +- .../nrw/commons/wikidata/model/EditClaim.kt | 44 +- .../nrw/commons/wikidata/model/EditTag.kt | 8 +- .../nrw/commons/wikidata/model/Entities.java | 4 +- .../nrw/commons/wikidata/model/EnumCodeMap.kt | 12 +- .../model/GetWikidataEditCountResponse.kt | 4 +- .../nrw/commons/wikidata/model/PageInfo.kt | 4 +- .../nrw/commons/wikidata/model/RemoveClaim.kt | 11 +- .../model/{Snak_partial.kt => SnakPartial.kt} | 4 +- ...atement_partial.kt => StatementPartial.kt} | 8 +- .../wikidata/model/WbCreateClaimResponse.kt | 5 +- .../wikidata/model/WikiBaseEntityValue.kt | 2 +- .../model/WikiBaseMonolingualTextValue.kt | 7 +- .../nrw/commons/AboutActivityUnitTests.kt | 5 +- .../fr/free/nrw/commons/FakeContextWrapper.kt | 19 +- .../FakeContextWrapperWithException.kt | 19 +- .../kotlin/fr/free/nrw/commons/LatLngTests.kt | 6 +- .../nrw/commons/MediaDataExtractorTest.kt | 18 +- .../kotlin/fr/free/nrw/commons/MediaTest.kt | 2 - .../fr/free/nrw/commons/ModelFunctions.kt | 59 +- .../free/nrw/commons/NearbyControllerTest.kt | 16 +- .../nrw/commons/OkHttpJsonApiClientTests.kt | 21 +- .../nrw/commons/TestCommonsApplication.kt | 25 +- .../free/nrw/commons/TestConnectionFactory.kt | 25 +- .../kotlin/fr/free/nrw/commons/TestUtility.kt | 7 +- .../kotlin/fr/free/nrw/commons/UtilsTest.kt | 4 +- .../nrw/commons/WelcomeActivityUnitTest.kt | 10 +- .../nrw/commons/actions/PageEditClientTest.kt | 35 +- .../nrw/commons/actions/ThanksClientTest.kt | 5 +- .../nrw/commons/auth/AccountUtilUnitTest.kt | 3 +- .../commons/auth/LoginActivityUnitTests.kt | 128 ++-- .../commons/auth/SessionManagerUnitTests.kt | 55 +- .../nrw/commons/auth/SignupActivityTest.kt | 4 +- ...WikiAccountAuthenticatorServiceUnitTest.kt | 4 +- .../auth/WikiAccountAuthenticatorUnitTest.kt | 8 +- .../commons/auth/csrf/CsrfTokenClientTest.kt | 4 +- .../auth/login/UserExtendedInfoClientTest.kt | 62 +- .../BookmarkListRootFragmentUnitTest.kt | 4 +- .../bookmarks/BookmarksPagerAdapterTests.kt | 2 +- .../LoggedOutBookmarksPagerAdapterTests.kt | 2 +- .../items/BookmarkItemsControllerTest.kt | 21 +- .../bookmarks/items/BookmarkItemsDaoTest.kt | 143 ++-- .../items/BookmarkItemsFragmentUnitTest.kt | 34 +- .../locations/BookMarkLocationDaoTest.kt | 88 ++- .../BookmarkLocationControllerTest.kt | 31 +- .../BookmarkLocationFragmentUnitTests.kt | 19 +- .../pictures/BookmarkPictureDaoTest.kt | 45 +- .../BookmarkPicturesControllerTest.kt | 10 +- .../BookmarkPicturesFragmentUnitTests.kt | 39 +- .../campaigns/CampaignViewUnitTests.kt | 13 +- .../campaigns/CampaignsPresenterTest.kt | 21 +- .../commons/category/CategoriesModelTest.kt | 195 ++++-- .../commons/category/CategoryClientTest.kt | 138 ++-- .../nrw/commons/category/CategoryDaoTest.kt | 87 ++- .../CategoryDetailsActivityUnitTests.kt | 5 +- .../category/CategoryEditHelperUnitTests.kt | 47 +- .../category/GridViewAdapterUnitTest.kt | 21 +- .../ContributionBoundaryCallbackTest.kt | 30 +- .../ContributionViewHolderUnitTests.kt | 46 +- .../ContributionsFragmentUnitTests.kt | 58 +- .../ContributionsListFragmentUnitTests.kt | 75 ++- .../ContributionsListPresenterTest.kt | 14 +- .../ContributionsPresenterTest.kt | 7 +- .../ContributionsRepositoryTest.kt | 16 +- .../contributions/MainActivityUnitTests.kt | 214 +++--- .../CoordinateEditHelperUnitTest.kt | 112 ++-- .../customselector/helper/ImageHelperTest.kt | 8 +- .../helper/OnSwipeTouchListenerTest.kt | 13 +- .../ui/adapter/FolderAdapterTest.kt | 9 +- .../ui/adapter/ImageAdapterTest.kt | 68 +- .../ui/selector/CustomSelectorActivityTest.kt | 98 +-- .../selector/CustomSelectorViewModelTest.kt | 12 +- .../ui/selector/FolderFragmentTest.kt | 12 +- .../ui/selector/ImageFileLoaderTest.kt | 74 +-- .../ui/selector/ImageFragmentTest.kt | 22 +- .../ui/selector/ImageLoaderTest.kt | 214 +++--- .../nrw/commons/delete/DeleteHelperTest.kt | 48 +- .../nrw/commons/delete/ReasonBuilderTest.kt | 18 +- .../DescriptionEditActivityUnitTest.kt | 73 ++- .../DescriptionEditHelperUnitTest.kt | 62 +- .../explore/BasePagingPresenterTest.kt | 12 +- .../explore/ExploreFragmentUnitTest.kt | 14 +- .../ExploreListRootFragmentUnitTest.kt | 5 +- .../explore/PageableBaseDataSourceTest.kt | 34 +- .../explore/PagingDataSourceFactoryTest.kt | 11 +- .../commons/explore/PagingDataSourceTest.kt | 31 +- .../PageableParentCategoriesDataSourceTest.kt | 3 +- .../PageableSubCategoriesDataSourceTest.kt | 3 +- .../explore/depictions/DepictsClientTest.kt | 113 ++-- .../PageableDepictionsDataSourceTest.kt | 4 +- .../WikidataItemDetailsActivityUnitTests.kt | 29 +- .../PageableChildDepictionsDataSourceTest.kt | 1 + .../PageableDepictedMediaDataSourceTest.kt | 7 +- .../PageableParentDepictionsDataSourceTest.kt | 1 - .../explore/media/MediaConverterTest.kt | 18 +- .../media/PageableMediaDataSourceTest.kt | 3 +- .../recentsearches/RecentSearchesDaoTest.kt | 60 +- .../RecentSearchesFragmentUnitTest.kt | 52 +- .../explore/search/SearchActivityUnitTests.kt | 25 +- .../FeedbackContentCreatorUnitTests.kt | 5 +- .../commons/feedback/FeedbackDialogTests.kt | 15 +- .../nrw/commons/feedback/FeedbackUnitTests.kt | 3 +- .../nrw/commons/filepicker/FilePickerTest.kt | 114 ++-- .../LeaderboardFragmentUnitTests.kt | 100 +-- .../LeaderboardListAdapterUnitTests.kt | 4 +- .../free/nrw/commons/location/LatLngTest.kt | 7 +- .../LocationPickerActivityUnitTests.kt | 40 +- .../LocationPickerViewModelUnitTests.kt | 4 +- .../commons/login/LoginActivityUnitTests.kt | 6 +- .../CustomOkHttpNetworkFetcherUnitTest.kt | 109 ++-- .../free/nrw/commons/media/MediaClientTest.kt | 50 +- .../media/MediaDetailFragmentUnitTests.kt | 320 +++++---- .../MediaDetailPagerFragmentUnitTests.kt | 46 +- .../media/ZoomableActivityUnitTests.kt | 20 +- .../MultiPointerGestureDetectorUnitTest.kt | 33 +- .../TransformGestureDetectorUnitTest.kt | 14 +- .../free/nrw/commons/mwapi/UserClientTest.kt | 35 +- .../MoreBottomSheetFragmentUnitTests.kt | 16 +- ...reBottomSheetLoggedOutFragmentUnitTests.kt | 17 +- .../nearby/AdvanceQueryFragmentUnitTests.kt | 2 - ...StatesTest.kt => CheckBoxTriStatesTest.kt} | 5 +- .../nearby/CommonPlaceClickActionsUnitTest.kt | 34 +- .../fr/free/nrw/commons/nearby/LabelTest.kt | 2 - .../commons/nearby/NearbyControllerTest.kt | 372 ++++++----- ...ilterSearchRecyclerViewAdapterUnitTests.kt | 5 +- .../NearbyParentFragmentPresenterTest.kt | 47 +- .../nearby/NearbyParentFragmentUnitTest.kt | 83 +-- .../nrw/commons/nearby/NearbyPlacesTest.kt | 4 +- .../NotificationActivityUnitTests.kt | 119 ++-- .../notification/NotificationClientTest.kt | 64 +- .../NotificationControllerTest.kt | 6 +- .../NotificationHelperUnitTests.kt | 8 +- .../NotificationWorkerFragmentUnitTests.kt | 4 +- .../commons/profile/ProfileActivityTest.kt | 13 +- .../AchievementsFragmentUnitTests.kt | 115 ++-- .../achievements/LevelControllerTest.kt | 20 +- .../nrw/commons/quiz/QuizActivityUnitTest.kt | 19 +- .../nrw/commons/quiz/QuizCheckerUnitTest.kt | 52 +- .../nrw/commons/quiz/QuizControllerTest.kt | 7 +- .../free/nrw/commons/quiz/QuizQuestionTest.kt | 37 +- .../quiz/QuizResultActivityUnitTest.kt | 4 +- .../commons/quiz/RadioGroupHelperUnitTest.kt | 6 +- .../RecentLanguagesAdapterUnitTest.kt | 14 +- .../RecentLanguagesContentProviderUnitTest.kt | 5 +- .../RecentLanguagesDaoUnitTest.kt | 46 +- .../nrw/commons/review/ReviewActivityTest.kt | 25 +- .../commons/review/ReviewControllerTest.kt | 59 +- .../free/nrw/commons/review/ReviewDaoTest.kt | 13 +- .../nrw/commons/review/ReviewHelperTest.kt | 34 +- .../commons/review/ReviewImageFragmentTest.kt | 9 +- .../settings/SettingsActivityUnitTests.kt | 11 +- .../settings/SettingsFragmentUnitTests.kt | 188 +++--- .../commons/upload/CategoriesPresenterTest.kt | 53 +- .../commons/upload/DepictsPresenterTest.kt | 52 +- .../commons/upload/FileMetadataUtilsTest.kt | 37 +- .../commons/upload/GpsCategoryModelTest.kt | 12 +- .../upload/ImageProcessingServiceTest.kt | 43 +- .../commons/upload/LanguagesAdapterTest.kt | 47 +- .../upload/MediaLicensePresenterTest.kt | 3 - .../commons/upload/UploadActivityUnitTests.kt | 86 +-- .../nrw/commons/upload/UploadClientTest.kt | 82 ++- .../UploadMediaDetailAdapterUnitTest.kt | 93 +-- .../UploadMediaDetailInputFilterTest.kt | 15 +- .../upload/UploadMediaPresenterTest.kt | 46 +- .../nrw/commons/upload/UploadModelUnitTest.kt | 74 ++- .../nrw/commons/upload/UploadPresenterTest.kt | 38 +- .../upload/UploadRepositoryUnitTest.kt | 109 ++-- .../UploadCategoriesFragmentUnitTests.kt | 92 ++- .../depicts/DepictEditHelperUnitTest.kt | 37 +- .../depicts/DepictsFragmentUnitTests.kt | 55 +- .../UploadMediaDetailFragmentUnitTest.kt | 57 +- .../structure/depictions/DepictedItemTest.kt | 87 +-- .../nrw/commons/utils/CommonsDateUtilTest.kt | 13 +- .../free/nrw/commons/utils/FileUtilsTest.kt | 21 +- .../free/nrw/commons/utils/ImageUtilsTest.kt | 72 +-- .../free/nrw/commons/utils/LengthUtilsTest.kt | 39 +- .../nrw/commons/utils/LocationUtilsTest.kt | 5 +- .../free/nrw/commons/utils/PagedListMock.kt | 60 +- .../commons/utils/StringSortingUtilsTest.kt | 187 +++--- .../commons/utils/UtilsFixExtensionTest.kt | 1 - .../HeightLimitedRecyclerViewUnitTests.kt | 16 +- .../widget/PicOfDayAppWidgetUnitTests.kt | 36 +- .../wikidata/WikiBaseClientUnitTest.kt | 15 +- .../commons/wikidata/WikidataClientTest.kt | 23 +- .../wikidata/WikidataEditServiceTest.kt | 18 +- 405 files changed, 10930 insertions(+), 9035 deletions(-) rename app/src/main/java/fr/free/nrw/commons/customselector/database/{NotForUploadDao.kt => NotForUploadStatusDao.kt} (70%) rename app/src/main/java/fr/free/nrw/commons/customselector/database/{UploadedDao.kt => UploadedStatusDao.kt} (62%) rename app/src/main/java/fr/free/nrw/commons/wikidata/model/{Snak_partial.kt => SnakPartial.kt} (84%) rename app/src/main/java/fr/free/nrw/commons/wikidata/model/{Statement_partial.kt => StatementPartial.kt} (75%) rename app/src/test/kotlin/fr/free/nrw/commons/nearby/{CheckboxTriStatesTest.kt => CheckBoxTriStatesTest.kt} (97%) diff --git a/app/src/androidTest/java/fr/free/nrw/commons/AboutActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/AboutActivityTest.kt index 9425a1d476..b5a752ef90 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/AboutActivityTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/AboutActivityTest.kt @@ -25,7 +25,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class AboutActivityTest { - @get:Rule var activityRule: ActivityTestRule<*> = ActivityTestRule(AboutActivity::class.java) @@ -36,7 +35,8 @@ class AboutActivityTest { device.setOrientationNatural() device.freezeRotation() Intents.init() - Intents.intending(CoreMatchers.not(IntentMatchers.isInternal())) + Intents + .intending(CoreMatchers.not(IntentMatchers.isInternal())) .respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, null)) } @@ -47,11 +47,12 @@ class AboutActivityTest { @Test fun testBuildNumber() { - Espresso.onView(ViewMatchers.withId(R.id.about_version)) + Espresso + .onView(ViewMatchers.withId(R.id.about_version)) .check( ViewAssertions.matches( - withText(getApplicationContext().getVersionNameWithSha()) - ) + withText(getApplicationContext().getVersionNameWithSha()), + ), ) } @@ -61,8 +62,8 @@ class AboutActivityTest { Intents.intended( CoreMatchers.allOf( IntentMatchers.hasAction(Intent.ACTION_VIEW), - IntentMatchers.hasData(Urls.WEBSITE_URL) - ) + IntentMatchers.hasData(Urls.WEBSITE_URL), + ), ) } @@ -73,8 +74,8 @@ class AboutActivityTest { CoreMatchers.anyOf( IntentMatchers.hasAction(Intent.ACTION_VIEW), IntentMatchers.hasData(Urls.FACEBOOK_WEB_URL), - IntentMatchers.hasPackage(Urls.FACEBOOK_PACKAGE_NAME) - ) + IntentMatchers.hasPackage(Urls.FACEBOOK_PACKAGE_NAME), + ), ) } @@ -84,8 +85,8 @@ class AboutActivityTest { Intents.intended( CoreMatchers.allOf( IntentMatchers.hasAction(Intent.ACTION_VIEW), - IntentMatchers.hasData(Urls.GITHUB_REPO_URL) - ) + IntentMatchers.hasData(Urls.GITHUB_REPO_URL), + ), ) } @@ -95,8 +96,8 @@ class AboutActivityTest { Intents.intended( CoreMatchers.allOf( IntentMatchers.hasAction(Intent.ACTION_VIEW), - IntentMatchers.hasData(BuildConfig.PRIVACY_POLICY_URL) - ) + IntentMatchers.hasData(BuildConfig.PRIVACY_POLICY_URL), + ), ) } @@ -108,8 +109,8 @@ class AboutActivityTest { Intents.intended( CoreMatchers.allOf( IntentMatchers.hasAction(Intent.ACTION_VIEW), - IntentMatchers.hasData("${Urls.TRANSLATE_WIKI_URL}$langCode") - ) + IntentMatchers.hasData("${Urls.TRANSLATE_WIKI_URL}$langCode"), + ), ) } @@ -119,27 +120,30 @@ class AboutActivityTest { Intents.intended( CoreMatchers.allOf( IntentMatchers.hasAction(Intent.ACTION_VIEW), - IntentMatchers.hasData(Urls.CREDITS_URL) - ) + IntentMatchers.hasData(Urls.CREDITS_URL), + ), ) } @Test fun testLaunchUserGuide() { Espresso.onView(ViewMatchers.withId(R.id.about_user_guide)).perform(ViewActions.click()) - Intents.intended(CoreMatchers.allOf(IntentMatchers.hasAction(Intent.ACTION_VIEW), - IntentMatchers.hasData(Urls.USER_GUIDE_URL))) + Intents.intended( + CoreMatchers.allOf( + IntentMatchers.hasAction(Intent.ACTION_VIEW), + IntentMatchers.hasData(Urls.USER_GUIDE_URL), + ), + ) } - @Test fun testLaunchAboutFaq() { Espresso.onView(ViewMatchers.withId(R.id.about_faq)).perform(ViewActions.click()) Intents.intended( CoreMatchers.allOf( IntentMatchers.hasAction(Intent.ACTION_VIEW), - IntentMatchers.hasData(Urls.FAQ_URL) - ) + IntentMatchers.hasData(Urls.FAQ_URL), + ), ) } } diff --git a/app/src/androidTest/java/fr/free/nrw/commons/LoginActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/LoginActivityTest.kt index 5039a24954..9bfc9321b4 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/LoginActivityTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/LoginActivityTest.kt @@ -18,12 +18,14 @@ import fr.free.nrw.commons.auth.LoginActivity import fr.free.nrw.commons.auth.SignupActivity import org.hamcrest.CoreMatchers import org.hamcrest.CoreMatchers.not -import org.junit.* +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class LoginActivityTest { - @get:Rule var activityRule = ActivityTestRule(LoginActivity::class.java) @@ -49,8 +51,8 @@ class LoginActivityTest { Intents.intended( CoreMatchers.allOf( IntentMatchers.hasAction(Intent.ACTION_VIEW), - IntentMatchers.hasData(BuildConfig.FORGOT_PASSWORD_URL) - ) + IntentMatchers.hasData(BuildConfig.FORGOT_PASSWORD_URL), + ), ) } @@ -64,4 +66,4 @@ class LoginActivityTest { fun orientationChange() { UITestHelper.changeOrientation(activityRule) } -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/fr/free/nrw/commons/MainActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/MainActivityTest.kt index 458d8de3ea..3d2fc9e481 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/MainActivityTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/MainActivityTest.kt @@ -21,20 +21,23 @@ import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.notification.NotificationActivity import org.hamcrest.CoreMatchers import org.hamcrest.Matchers -import org.junit.* +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test import org.junit.runner.RunWith @LargeTest @RunWith(AndroidJUnit4::class) class MainActivityTest { - @get:Rule var activityRule: ActivityTestRule<*> = ActivityTestRule(LoginActivity::class.java) @get:Rule - var mGrantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant( - "android.permission.ACCESS_FINE_LOCATION" - ) + var mGrantPermissionRule: GrantPermissionRule = + GrantPermissionRule.grant( + "android.permission.ACCESS_FINE_LOCATION", + ) private val device: UiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) @@ -48,7 +51,8 @@ class MainActivityTest { UITestHelper.loginUser() UITestHelper.skipWelcome() Intents.init() - Intents.intending(CoreMatchers.not(IntentMatchers.isInternal())) + Intents + .intending(CoreMatchers.not(IntentMatchers.isInternal())) .respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, null)) val context = InstrumentationRegistry.getInstrumentation().targetContext val storeName = context.packageName + "_preferences" @@ -62,137 +66,149 @@ class MainActivityTest { @Test fun testNearby() { - Espresso.onView( - Matchers.allOf( - childAtPosition( + Espresso + .onView( + Matchers.allOf( childAtPosition( - ViewMatchers.withId(R.id.fragment_main_nav_tab_layout), - 0 + childAtPosition( + ViewMatchers.withId(R.id.fragment_main_nav_tab_layout), + 0, + ), + 1, ), - 1 + ViewMatchers.isDisplayed(), ), - ViewMatchers.isDisplayed() - ) - ).perform(ViewActions.click()) - Espresso.onView(ViewMatchers.withId(R.id.fragmentContainer)) + ).perform(ViewActions.click()) + Espresso + .onView(ViewMatchers.withId(R.id.fragmentContainer)) .check(matches(ViewMatchers.isDisplayed())) UITestHelper.sleep(10000) - val actionMenuItemView2 = Espresso.onView( - Matchers.allOf( - ViewMatchers.withId(R.id.list_sheet), ViewMatchers.withContentDescription("List"), - childAtPosition( + val actionMenuItemView2 = + Espresso.onView( + Matchers.allOf( + ViewMatchers.withId(R.id.list_sheet), + ViewMatchers.withContentDescription("List"), childAtPosition( - ViewMatchers.withId(R.id.toolbar), - 1 + childAtPosition( + ViewMatchers.withId(R.id.toolbar), + 1, + ), + 0, ), - 0 + ViewMatchers.isDisplayed(), ), - ViewMatchers.isDisplayed() ) - ) actionMenuItemView2.perform(ViewActions.click()) UITestHelper.sleep(1000) } @Test fun testExplore() { - Espresso.onView( - Matchers.allOf( - childAtPosition( + Espresso + .onView( + Matchers.allOf( childAtPosition( - ViewMatchers.withId(R.id.fragment_main_nav_tab_layout), - 0 + childAtPosition( + ViewMatchers.withId(R.id.fragment_main_nav_tab_layout), + 0, + ), + 2, ), - 2 + ViewMatchers.isDisplayed(), ), - ViewMatchers.isDisplayed() - ) - ).perform(ViewActions.click()) - Espresso.onView(ViewMatchers.withId(R.id.fragmentContainer)) + ).perform(ViewActions.click()) + Espresso + .onView(ViewMatchers.withId(R.id.fragmentContainer)) .check(matches(ViewMatchers.isDisplayed())) UITestHelper.sleep(1000) } @Test fun testContributions() { - Espresso.onView( - Matchers.allOf( - childAtPosition( + Espresso + .onView( + Matchers.allOf( childAtPosition( - ViewMatchers.withId(R.id.fragment_main_nav_tab_layout), - 0 + childAtPosition( + ViewMatchers.withId(R.id.fragment_main_nav_tab_layout), + 0, + ), + 0, ), - 0 + ViewMatchers.isDisplayed(), ), - ViewMatchers.isDisplayed() - ) - ).perform(ViewActions.click()) - Espresso.onView(ViewMatchers.withId(R.id.fragmentContainer)) + ).perform(ViewActions.click()) + Espresso + .onView(ViewMatchers.withId(R.id.fragmentContainer)) .check(matches(ViewMatchers.isDisplayed())) - Espresso.onView( - Matchers.allOf( - ViewMatchers.withId(R.id.contributionImage), - childAtPosition( + Espresso + .onView( + Matchers.allOf( + ViewMatchers.withId(R.id.contributionImage), childAtPosition( - ViewMatchers.withId(R.id.contributionsList), - 0 + childAtPosition( + ViewMatchers.withId(R.id.contributionsList), + 0, + ), + 1, ), - 1 + ViewMatchers.isDisplayed(), ), - ViewMatchers.isDisplayed() - ) - ).perform(ViewActions.click()) - val actionMenuItemView = Espresso.onView( - Matchers.allOf( - ViewMatchers.withId(R.id.menu_bookmark_current_image), - childAtPosition( + ).perform(ViewActions.click()) + val actionMenuItemView = + Espresso.onView( + Matchers.allOf( + ViewMatchers.withId(R.id.menu_bookmark_current_image), childAtPosition( - ViewMatchers.withId(R.id.toolbar), - 1 + childAtPosition( + ViewMatchers.withId(R.id.toolbar), + 1, + ), + 0, ), - 0 + ViewMatchers.isDisplayed(), ), - ViewMatchers.isDisplayed() ) - ) actionMenuItemView.perform(ViewActions.click()) UITestHelper.sleep(3000) } @Test fun testBookmarks() { - Espresso.onView( - Matchers.allOf( - childAtPosition( + Espresso + .onView( + Matchers.allOf( childAtPosition( - ViewMatchers.withId(R.id.fragment_main_nav_tab_layout), - 0 + childAtPosition( + ViewMatchers.withId(R.id.fragment_main_nav_tab_layout), + 0, + ), + 3, ), - 3 + ViewMatchers.isDisplayed(), ), - ViewMatchers.isDisplayed() - ) - ).perform(ViewActions.click()) + ).perform(ViewActions.click()) UITestHelper.sleep(1000) } @Test fun testNotifications() { - Espresso.onView( - Matchers.allOf( - ViewMatchers.withId(R.id.notifications), - childAtPosition( + Espresso + .onView( + Matchers.allOf( + ViewMatchers.withId(R.id.notifications), childAtPosition( - ViewMatchers.withId(R.id.toolbar), - 1 + childAtPosition( + ViewMatchers.withId(R.id.toolbar), + 1, + ), + 1, ), - 1 + ViewMatchers.isDisplayed(), ), - ViewMatchers.isDisplayed() - ) - ).perform(ViewActions.click()) + ).perform(ViewActions.click()) Intents.intended(IntentMatchers.hasComponent(NotificationActivity::class.java.name)) Espresso.pressBack() UITestHelper.sleep(1000) } -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/fr/free/nrw/commons/ProfileActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/ProfileActivityTest.kt index 524274d544..003fc06745 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/ProfileActivityTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/ProfileActivityTest.kt @@ -4,7 +4,6 @@ import android.app.Activity import android.app.Instrumentation import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions -import androidx.test.espresso.action.ViewActions.swipeRight import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.matcher.IntentMatchers import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent @@ -26,7 +25,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ProfileActivityTest { - @get:Rule var activityRule = IntentsTestRule(LoginActivity::class.java) @@ -38,7 +36,8 @@ class ProfileActivityTest { device.freezeRotation() UITestHelper.loginUser() UITestHelper.skipWelcome() - Intents.intending(CoreMatchers.not(IntentMatchers.isInternal())) + Intents + .intending(CoreMatchers.not(IntentMatchers.isInternal())) .respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, null)) } @@ -50,20 +49,19 @@ class ProfileActivityTest { childAtPosition( childAtPosition( withId(R.id.fragment_main_nav_tab_layout), - 0 + 0, ), - 4 + 4, ), - ViewMatchers.isDisplayed() - ) + ViewMatchers.isDisplayed(), + ), ).perform(ViewActions.click()) onView(Matchers.allOf(withId(R.id.more_profile))).perform( ViewActions.scrollTo(), - ViewActions.click() + ViewActions.click(), ) - device.swipe(1033,1346,531,1346,20) + device.swipe(1033, 1346, 531, 1346, 20) UITestHelper.sleep(5000) Intents.intended(hasComponent(ProfileActivity::class.java.name)) } - } diff --git a/app/src/androidTest/java/fr/free/nrw/commons/ReviewActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/ReviewActivityTest.kt index dd52dbc3b2..3f6487e470 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/ReviewActivityTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/ReviewActivityTest.kt @@ -9,7 +9,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ReviewActivityTest { - @get:Rule var activityRule: ActivityTestRule<*> = ActivityTestRule(ReviewActivity::class.java) @@ -17,5 +16,4 @@ class ReviewActivityTest { fun orientationChange() { UITestHelper.changeOrientation(activityRule) } - -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/fr/free/nrw/commons/SearchActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/SearchActivityTest.kt index c42b49e92b..69ce412b94 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/SearchActivityTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/SearchActivityTest.kt @@ -16,7 +16,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class SearchActivityTest { - @get:Rule var activityRule = ActivityTestRule(SearchActivity::class.java) @@ -31,21 +30,22 @@ class SearchActivityTest { @Test fun exploreActivityTest() { - val searchAutoComplete = Espresso.onView( - Matchers.allOf( - UITestHelper.childAtPosition( - Matchers.allOf( - ViewMatchers.withClassName(Matchers.`is`("android.widget.LinearLayout")), - UITestHelper.childAtPosition( + val searchAutoComplete = + Espresso.onView( + Matchers.allOf( + UITestHelper.childAtPosition( + Matchers.allOf( ViewMatchers.withClassName(Matchers.`is`("android.widget.LinearLayout")), - 1 - ) + UITestHelper.childAtPosition( + ViewMatchers.withClassName(Matchers.`is`("android.widget.LinearLayout")), + 1, + ), + ), + 0, ), - 0 + ViewMatchers.isDisplayed(), ), - ViewMatchers.isDisplayed() ) - ) searchAutoComplete.perform(ViewActions.replaceText("cat"), ViewActions.closeSoftKeyboard()) UITestHelper.sleep(5000) device.swipe(1000, 1400, 500, 1400, 20) @@ -56,4 +56,4 @@ class SearchActivityTest { device.swipe(800, 1400, 600, 1400, 20) UITestHelper.sleep(1000) } -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityLoggedInTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityLoggedInTest.kt index 3d464fb5a1..ec132b4473 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityLoggedInTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityLoggedInTest.kt @@ -22,7 +22,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class SettingsActivityLoggedInTest { - @get:Rule var activityRule: ActivityTestRule<*> = ActivityTestRule(LoginActivity::class.java) @@ -35,31 +34,32 @@ class SettingsActivityLoggedInTest { device.freezeRotation() UITestHelper.loginUser() UITestHelper.skipWelcome() - Intents.intending(CoreMatchers.not(IntentMatchers.isInternal())) + Intents + .intending(CoreMatchers.not(IntentMatchers.isInternal())) .respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, null)) } @Test fun testSettings() { - Espresso.onView( - Matchers.allOf( - ViewMatchers.withContentDescription("More"), - UITestHelper.childAtPosition( + Espresso + .onView( + Matchers.allOf( + ViewMatchers.withContentDescription("More"), UITestHelper.childAtPosition( - ViewMatchers.withId(R.id.fragment_main_nav_tab_layout), - 0 + UITestHelper.childAtPosition( + ViewMatchers.withId(R.id.fragment_main_nav_tab_layout), + 0, + ), + 4, ), - 4 + ViewMatchers.isDisplayed(), ), - ViewMatchers.isDisplayed() - ) - ).perform(ViewActions.click()) + ).perform(ViewActions.click()) Espresso.onView(Matchers.allOf(ViewMatchers.withId(R.id.more_settings))).perform( ViewActions.scrollTo(), - ViewActions.click() + ViewActions.click(), ) Intents.intended(IntentMatchers.hasComponent(SettingsActivity::class.java.name)) UITestHelper.sleep(1000) } - -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.kt index 211483a83b..c5a91cd568 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.kt @@ -23,7 +23,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class SettingsActivityTest { - private lateinit var defaultKvStore: JsonKvStore @get:Rule @@ -44,22 +43,24 @@ class SettingsActivityTest { fun useAuthorNameTogglesOn() { // Turn on "Use author name" preference if currently off if (!defaultKvStore.getBoolean("useAuthorName", false)) { - Espresso.onView( - allOf( - withId(R.id.recycler_view), - childAtPosition(withId(android.R.id.list_container), 0) + Espresso + .onView( + allOf( + withId(R.id.recycler_view), + childAtPosition(withId(android.R.id.list_container), 0), + ), + ).perform( + RecyclerViewActions.actionOnItemAtPosition(6, click()), ) - ).perform( - RecyclerViewActions.actionOnItemAtPosition(6, click()) - ) } // Check authorName preference is enabled - Espresso.onView( - allOf( - withId(R.id.recycler_view), - childAtPosition(withId(android.R.id.list_container), 0) - ) - ).check(matches(isEnabled())) + Espresso + .onView( + allOf( + withId(R.id.recycler_view), + childAtPosition(withId(android.R.id.list_container), 0), + ), + ).check(matches(isEnabled())) } @Test diff --git a/app/src/androidTest/java/fr/free/nrw/commons/UITestHelper.kt b/app/src/androidTest/java/fr/free/nrw/commons/UITestHelper.kt index 26d6f92463..ebb06e4af1 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/UITestHelper.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/UITestHelper.kt @@ -10,17 +10,20 @@ import androidx.test.espresso.action.ViewActions import androidx.test.espresso.matcher.ViewMatchers import androidx.test.rule.ActivityTestRule import org.apache.commons.lang3.StringUtils -import org.hamcrest.* +import org.hamcrest.BaseMatcher +import org.hamcrest.Description +import org.hamcrest.Matcher +import org.hamcrest.Matchers +import org.hamcrest.TypeSafeMatcher import timber.log.Timber - class UITestHelper { companion object { fun skipWelcome() { try { onView(ViewMatchers.withId(R.id.button_ok)) .perform(ViewActions.click()) - //Skip tutorial + // Skip tutorial onView(ViewMatchers.withId(R.id.finishTutorialButton)) .perform(ViewActions.click()) } catch (ignored: NoMatchingViewException) { @@ -29,27 +32,31 @@ class UITestHelper { fun skipLogin() { try { - //Skip Login - val htmlTextView = onView( - Matchers.allOf( - ViewMatchers.withId(R.id.skip_login), ViewMatchers.withText("Skip"), - ViewMatchers.isDisplayed() + // Skip Login + val htmlTextView = + onView( + Matchers.allOf( + ViewMatchers.withId(R.id.skip_login), + ViewMatchers.withText("Skip"), + ViewMatchers.isDisplayed(), + ), ) - ) htmlTextView.perform(ViewActions.click()) - val appCompatButton = onView( - Matchers.allOf( - ViewMatchers.withId(android.R.id.button1), ViewMatchers.withText("Yes"), - childAtPosition( + val appCompatButton = + onView( + Matchers.allOf( + ViewMatchers.withId(android.R.id.button1), + ViewMatchers.withText("Yes"), childAtPosition( - ViewMatchers.withId(R.id.buttonPanel), - 0 + childAtPosition( + ViewMatchers.withId(R.id.buttonPanel), + 0, + ), + 3, ), - 3 - ) + ), ) - ) appCompatButton.perform(ViewActions.scrollTo(), ViewActions.click()) } catch (ignored: NoMatchingViewException) { } @@ -57,18 +64,18 @@ class UITestHelper { fun loginUser() { try { - //Perform Login + // Perform Login sleep(3000) onView(ViewMatchers.withId(R.id.login_username)) .perform( ViewActions.replaceText(getTestUsername()), - ViewActions.closeSoftKeyboard() + ViewActions.closeSoftKeyboard(), ) sleep(2000) onView(ViewMatchers.withId(R.id.login_password)) .perform( ViewActions.replaceText(getTestUserPassword()), - ViewActions.closeSoftKeyboard() + ViewActions.closeSoftKeyboard(), ) sleep(2000) onView(ViewMatchers.withId(R.id.login_button)) @@ -76,7 +83,6 @@ class UITestHelper { sleep(10000) } catch (ignored: NoMatchingViewException) { } - } fun logoutUser() { @@ -87,36 +93,38 @@ class UITestHelper { childAtPosition( childAtPosition( ViewMatchers.withId(R.id.fragment_main_nav_tab_layout), - 0 + 0, ), - 4 + 4, ), - ViewMatchers.isDisplayed() - ) + ViewMatchers.isDisplayed(), + ), ).perform(ViewActions.click()) onView( Matchers.allOf( - ViewMatchers.withId(R.id.more_logout), ViewMatchers.withText("Logout"), + ViewMatchers.withId(R.id.more_logout), + ViewMatchers.withText("Logout"), childAtPosition( childAtPosition( ViewMatchers.withId(R.id.scroll_view_more_bottom_sheet), - 0 + 0, ), - 6 - ) - ) + 6, + ), + ), ).perform(ViewActions.scrollTo(), ViewActions.click()) onView( Matchers.allOf( - ViewMatchers.withId(android.R.id.button1), ViewMatchers.withText("Yes"), + ViewMatchers.withId(android.R.id.button1), + ViewMatchers.withText("Yes"), childAtPosition( childAtPosition( ViewMatchers.withId(R.id.buttonPanel), - 0 + 0, ), - 3 - ) - ) + 3, + ), + ), ).perform(ViewActions.scrollTo(), ViewActions.click()) sleep(5000) } catch (ignored: NoMatchingViewException) { @@ -124,9 +132,9 @@ class UITestHelper { } fun childAtPosition( - parentMatcher: Matcher, position: Int + parentMatcher: Matcher, + position: Int, ): Matcher { - return object : TypeSafeMatcher() { override fun describeTo(description: Description) { description.appendText("Child at position $position in parent ") @@ -135,8 +143,9 @@ class UITestHelper { public override fun matchesSafely(view: View): Boolean { val parent = view.parent - return parent is ViewGroup && parentMatcher.matches(parent) - && view == parent.getChildAt(position) + return parent is ViewGroup && + parentMatcher.matches(parent) && + view == parent.getChildAt(position) } } } @@ -154,14 +163,18 @@ class UITestHelper { val username = BuildConfig.TEST_USERNAME if (StringUtils.isEmpty(username) || username == "null") { throw NotImplementedError("Configure your beta account's username") - } else return username + } else { + return username + } } private fun getTestUserPassword(): String { val password = BuildConfig.TEST_PASSWORD if (StringUtils.isEmpty(password) || password == "null") { throw NotImplementedError("Configure your beta account's password") - } else return password + } else { + return password + } } fun changeOrientation(activityRule: ActivityTestRule) { @@ -174,6 +187,7 @@ class UITestHelper { fun first(matcher: Matcher): Matcher? { return object : BaseMatcher() { var isFirst = true + override fun matches(item: Any): Boolean { if (isFirst && matcher.matches(item)) { isFirst = false @@ -188,4 +202,4 @@ class UITestHelper { } } } -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/fr/free/nrw/commons/UploadCancelledTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/UploadCancelledTest.kt index 4041b92e54..0277b83248 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/UploadCancelledTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/UploadCancelledTest.kt @@ -4,7 +4,10 @@ import android.app.Activity import android.app.Instrumentation import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions.* +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.action.ViewActions.scrollTo import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.matcher.IntentMatchers @@ -28,7 +31,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class UploadCancelledTest { - @Rule @JvmField var mActivityTestRule = ActivityTestRule(LoginActivity::class.java) @@ -37,7 +39,7 @@ class UploadCancelledTest { @JvmField var mGrantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant( - "android.permission.WRITE_EXTERNAL_STORAGE" + "android.permission.WRITE_EXTERNAL_STORAGE", ) private val device: UiDevice = @@ -48,14 +50,14 @@ class UploadCancelledTest { try { Intents.init() } catch (ex: IllegalStateException) { - } device.unfreezeRotation() device.setOrientationNatural() device.freezeRotation() UITestHelper.loginUser() UITestHelper.skipWelcome() - Intents.intending(CoreMatchers.not(IntentMatchers.isInternal())) + Intents + .intending(CoreMatchers.not(IntentMatchers.isInternal())) .respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, null)) } @@ -64,130 +66,137 @@ class UploadCancelledTest { try { Intents.release() } catch (ex: IllegalStateException) { - } } @Test fun uploadCancelledAfterLocationPickedTest() { - - val bottomNavigationItemView = onView( - allOf( - childAtPosition( + val bottomNavigationItemView = + onView( + allOf( childAtPosition( - withId(R.id.fragment_main_nav_tab_layout), - 0 + childAtPosition( + withId(R.id.fragment_main_nav_tab_layout), + 0, + ), + 1, ), - 1 + isDisplayed(), ), - isDisplayed() ) - ) bottomNavigationItemView.perform(click()) UITestHelper.sleep(12000) - val actionMenuItemView = onView( - allOf( - withId(R.id.list_sheet), - childAtPosition( + val actionMenuItemView = + onView( + allOf( + withId(R.id.list_sheet), childAtPosition( - withId(R.id.toolbar), - 1 + childAtPosition( + withId(R.id.toolbar), + 1, + ), + 0, ), - 0 + isDisplayed(), ), - isDisplayed() ) - ) actionMenuItemView.perform(click()) - val recyclerView = onView( - allOf( - withId(R.id.rv_nearby_list), + val recyclerView = + onView( + allOf( + withId(R.id.rv_nearby_list), + ), ) - ) recyclerView.perform( RecyclerViewActions.actionOnItemAtPosition( 0, - click() - ) + click(), + ), ) - val linearLayout3 = onView( - allOf( - withId(R.id.cameraButton), - childAtPosition( - allOf( - withId(R.id.nearby_button_layout), + val linearLayout3 = + onView( + allOf( + withId(R.id.cameraButton), + childAtPosition( + allOf( + withId(R.id.nearby_button_layout), + ), + 0, ), - 0 + isDisplayed(), ), - isDisplayed() ) - ) linearLayout3.perform(click()) - val pasteSensitiveTextInputEditText = onView( - allOf( - withId(R.id.caption_item_edit_text), - childAtPosition( + val pasteSensitiveTextInputEditText = + onView( + allOf( + withId(R.id.caption_item_edit_text), childAtPosition( - withId(R.id.caption_item_edit_text_input_layout), - 0 + childAtPosition( + withId(R.id.caption_item_edit_text_input_layout), + 0, + ), + 0, ), - 0 + isDisplayed(), ), - isDisplayed() ) - ) pasteSensitiveTextInputEditText.perform(replaceText("test"), closeSoftKeyboard()) - val pasteSensitiveTextInputEditText2 = onView( - allOf( - withId(R.id.description_item_edit_text), - childAtPosition( + val pasteSensitiveTextInputEditText2 = + onView( + allOf( + withId(R.id.description_item_edit_text), childAtPosition( - withId(R.id.description_item_edit_text_input_layout), - 0 + childAtPosition( + withId(R.id.description_item_edit_text_input_layout), + 0, + ), + 0, ), - 0 + isDisplayed(), ), - isDisplayed() ) - ) pasteSensitiveTextInputEditText2.perform(replaceText("test"), closeSoftKeyboard()) - val appCompatButton2 = onView( - allOf( - withId(R.id.btn_next), - childAtPosition( + val appCompatButton2 = + onView( + allOf( + withId(R.id.btn_next), childAtPosition( - withId(R.id.ll_container_media_detail), - 2 + childAtPosition( + withId(R.id.ll_container_media_detail), + 2, + ), + 1, ), - 1 + isDisplayed(), ), - isDisplayed() ) - ) appCompatButton2.perform(click()) - val appCompatButton3 = onView( - allOf( - withId(android.R.id.button1), + val appCompatButton3 = + onView( + allOf( + withId(android.R.id.button1), + ), ) - ) appCompatButton3.perform(scrollTo(), click()) Intents.intended(IntentMatchers.hasComponent(LocationPickerActivity::class.java.name)) - val floatingActionButton3 = onView( - allOf( - withId(R.id.location_chosen_button), - isDisplayed() + val floatingActionButton3 = + onView( + allOf( + withId(R.id.location_chosen_button), + isDisplayed(), + ), ) - ) UITestHelper.sleep(2000) floatingActionButton3.perform(click()) } diff --git a/app/src/androidTest/java/fr/free/nrw/commons/UploadTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/UploadTest.kt index 8370e9848a..88c7e5d3d5 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/UploadTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/UploadTest.kt @@ -19,7 +19,10 @@ import androidx.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.Intents.intending import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction import androidx.test.espresso.intent.matcher.IntentMatchers.hasType -import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withParent +import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest import androidx.test.rule.ActivityTestRule @@ -29,21 +32,29 @@ import fr.free.nrw.commons.upload.UploadMediaDetailAdapter import fr.free.nrw.commons.util.MyViewAction import fr.free.nrw.commons.utils.ConfigUtils import org.hamcrest.core.AllOf.allOf -import org.junit.* +import org.junit.After +import org.junit.Before +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test import org.junit.runner.RunWith import timber.log.Timber import java.io.File import java.io.FileOutputStream import java.io.IOException import java.text.SimpleDateFormat -import java.util.* +import java.util.Date +import java.util.Random @LargeTest @RunWith(AndroidJUnit4::class) class UploadTest { @get:Rule - var permissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.ACCESS_FINE_LOCATION)!! + var permissionRule = + GrantPermissionRule.grant( + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.ACCESS_FINE_LOCATION, + )!! @get:Rule var activityRule = ActivityTestRule(LoginActivity::class.java) @@ -61,7 +72,6 @@ class UploadTest { try { Intents.init() } catch (ex: IllegalStateException) { - } UITestHelper.loginUser() UITestHelper.skipWelcome() @@ -94,14 +104,13 @@ class UploadTest { dismissWarning("Yes") onView(allOf(isDisplayed(), withId(R.id.tv_title))) - .perform(replaceText(commonsFileName)) + .perform(replaceText(commonsFileName)) onView(allOf(isDisplayed(), withId(R.id.description_item_edit_text))) - .perform(replaceText(commonsFileName)) - + .perform(replaceText(commonsFileName)) onView(allOf(isDisplayed(), withId(R.id.btn_next))) - .perform(click()) + .perform(click()) UITestHelper.sleep(5000) dismissWarning("Yes") @@ -109,29 +118,30 @@ class UploadTest { UITestHelper.sleep(3000) onView(allOf(isDisplayed(), withId(R.id.et_search))) - .perform(replaceText("Uploaded with Mobile/Android Tests")) + .perform(replaceText("Uploaded with Mobile/Android Tests")) UITestHelper.sleep(3000) try { onView(allOf(isDisplayed(), UITestHelper.first(withParent(withId(R.id.rv_categories))))) - .perform(click()) + .perform(click()) } catch (ignored: NoMatchingViewException) { } onView(allOf(isDisplayed(), withId(R.id.btn_next))) - .perform(click()) + .perform(click()) dismissWarning("Yes, Submit") UITestHelper.sleep(500) onView(allOf(isDisplayed(), withId(R.id.btn_submit))) - .perform(click()) + .perform(click()) UITestHelper.sleep(10000) - val fileUrl = "https://commons.wikimedia.beta.wmflabs.org/wiki/File:" + + val fileUrl = + "https://commons.wikimedia.beta.wmflabs.org/wiki/File:" + commonsFileName.replace(' ', '_') + ".jpg" Timber.i("File should be uploaded to $fileUrl") } @@ -139,8 +149,8 @@ class UploadTest { private fun dismissWarning(warningText: String) { try { onView(withText(warningText)) - .check(matches(isDisplayed())) - .perform(click()) + .check(matches(isDisplayed())) + .perform(click()) } catch (ignored: NoMatchingViewException) { } } @@ -167,10 +177,10 @@ class UploadTest { dismissWarning("Yes") onView(allOf(isDisplayed(), withId(R.id.tv_title))) - .perform(replaceText(commonsFileName)) + .perform(replaceText(commonsFileName)) onView(allOf(isDisplayed(), withId(R.id.btn_next))) - .perform(click()) + .perform(click()) UITestHelper.sleep(10000) dismissWarning("Yes") @@ -178,29 +188,30 @@ class UploadTest { UITestHelper.sleep(3000) onView(allOf(isDisplayed(), withId(R.id.et_search))) - .perform(replaceText("Test")) + .perform(replaceText("Test")) UITestHelper.sleep(3000) try { onView(allOf(isDisplayed(), UITestHelper.first(withParent(withId(R.id.rv_categories))))) - .perform(click()) + .perform(click()) } catch (ignored: NoMatchingViewException) { } onView(allOf(isDisplayed(), withId(R.id.btn_next))) - .perform(click()) + .perform(click()) dismissWarning("Yes, Submit") UITestHelper.sleep(500) onView(allOf(isDisplayed(), withId(R.id.btn_submit))) - .perform(click()) + .perform(click()) UITestHelper.sleep(10000) - val fileUrl = "https://commons.wikimedia.beta.wmflabs.org/wiki/File:" + + val fileUrl = + "https://commons.wikimedia.beta.wmflabs.org/wiki/File:" + commonsFileName.replace(' ', '_') + ".jpg" Timber.i("File should be uploaded to $fileUrl") } @@ -227,23 +238,29 @@ class UploadTest { dismissWarningDialog() onView(allOf(isDisplayed(), withId(R.id.tv_title))) - .perform(replaceText(commonsFileName)) + .perform(replaceText(commonsFileName)) onView(withId(R.id.rv_descriptions)).perform( - RecyclerViewActions - .actionOnItemAtPosition(0, - MyViewAction.typeTextInChildViewWithId(R.id.description_item_edit_text, "Test description"))) + RecyclerViewActions + .actionOnItemAtPosition( + 0, + MyViewAction.typeTextInChildViewWithId(R.id.description_item_edit_text, "Test description"), + ), + ) onView(withId(R.id.btn_add)) - .perform(click()) + .perform(click()) onView(withId(R.id.rv_descriptions)).perform( - RecyclerViewActions - .actionOnItemAtPosition(1, - MyViewAction.typeTextInChildViewWithId(R.id.description_item_edit_text, "Description"))) + RecyclerViewActions + .actionOnItemAtPosition( + 1, + MyViewAction.typeTextInChildViewWithId(R.id.description_item_edit_text, "Description"), + ), + ) onView(allOf(isDisplayed(), withId(R.id.btn_next))) - .perform(click()) + .perform(click()) UITestHelper.sleep(5000) dismissWarning("Yes") @@ -251,29 +268,30 @@ class UploadTest { UITestHelper.sleep(3000) onView(allOf(isDisplayed(), withId(R.id.et_search))) - .perform(replaceText("Test")) + .perform(replaceText("Test")) UITestHelper.sleep(3000) try { onView(allOf(isDisplayed(), UITestHelper.first(withParent(withId(R.id.rv_categories))))) - .perform(click()) + .perform(click()) } catch (ignored: NoMatchingViewException) { } onView(allOf(isDisplayed(), withId(R.id.btn_next))) - .perform(click()) + .perform(click()) dismissWarning("Yes, Submit") UITestHelper.sleep(500) onView(allOf(isDisplayed(), withId(R.id.btn_submit))) - .perform(click()) + .perform(click()) UITestHelper.sleep(10000) - val fileUrl = "https://commons.wikimedia.beta.wmflabs.org/wiki/File:" + + val fileUrl = + "https://commons.wikimedia.beta.wmflabs.org/wiki/File:" + commonsFileName.replace(' ', '_') + ".jpg" Timber.i("File should be uploaded to $fileUrl") } @@ -306,7 +324,6 @@ class UploadTest { } catch (e: IOException) { e.printStackTrace() } - } } @@ -328,8 +345,8 @@ class UploadTest { private fun dismissWarningDialog() { try { onView(withText("Yes")) - .check(matches(isDisplayed())) - .perform(click()) + .check(matches(isDisplayed())) + .perform(click()) } catch (ignored: NoMatchingViewException) { } } @@ -337,10 +354,10 @@ class UploadTest { private fun openGallery() { // Open FAB onView(allOf(withId(R.id.fab_plus), isDisplayed())) - .perform(click()) + .perform(click()) // Click gallery onView(allOf(withId(R.id.fab_gallery), isDisplayed())) - .perform(click()) + .perform(click()) } -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt index 777a1859a6..d48a75b91e 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt @@ -3,7 +3,6 @@ package fr.free.nrw.commons 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.withId import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -22,7 +21,6 @@ import org.junit.runner.RunWith @LargeTest @RunWith(AndroidJUnit4::class) class WelcomeActivityTest { - @get:Rule var activityRule: ActivityTestRule<*> = ActivityTestRule(WelcomeActivity::class.java) @@ -130,4 +128,4 @@ class WelcomeActivityTest { fun orientationChange() { UITestHelper.changeOrientation(activityRule) } -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/fr/free/nrw/commons/ui/PasteSensitiveTextInputEditTextTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/ui/PasteSensitiveTextInputEditTextTest.kt index 37ea557d1b..aedbcb133b 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/ui/PasteSensitiveTextInputEditTextTest.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/ui/PasteSensitiveTextInputEditTextTest.kt @@ -11,7 +11,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class PasteSensitiveTextInputEditTextTest { - private var context: Context? = null private var textView: PasteSensitiveTextInputEditText? = null @@ -23,9 +22,13 @@ class PasteSensitiveTextInputEditTextTest { // this test has no real value, just % for test code coverage @Test - fun extractFormattingAttributeSet(){ - val methodExtractFormattingAttribute = textView!!.javaClass.getDeclaredMethod( - "extractFormattingAttribute", Context::class.java, AttributeSet::class.java) + fun extractFormattingAttributeSet() { + val methodExtractFormattingAttribute = + textView!!.javaClass.getDeclaredMethod( + "extractFormattingAttribute", + Context::class.java, + AttributeSet::class.java, + ) methodExtractFormattingAttribute.isAccessible = true methodExtractFormattingAttribute.invoke(textView, context, null) } @@ -40,4 +43,4 @@ class PasteSensitiveTextInputEditTextTest { textView!!.setFormattingAllowed(false) Assert.assertFalse(fieldFormattingAllowed.getBoolean(textView)) } -} \ No newline at end of file +} diff --git a/app/src/androidTest/java/fr/free/nrw/commons/util/MyViewAction.kt b/app/src/androidTest/java/fr/free/nrw/commons/util/MyViewAction.kt index 955c712b9e..52ac18e4dc 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/util/MyViewAction.kt +++ b/app/src/androidTest/java/fr/free/nrw/commons/util/MyViewAction.kt @@ -9,56 +9,58 @@ import org.hamcrest.Matcher class MyViewAction { companion object { - fun typeTextInChildViewWithId(id: Int, textToBeTyped: String): ViewAction { - return object : ViewAction { - override fun getConstraints(): Matcher? { - return null - } + fun typeTextInChildViewWithId( + id: Int, + textToBeTyped: String, + ): ViewAction = + object : ViewAction { + override fun getConstraints(): Matcher? = null - override fun getDescription(): String { - return "Click on a child view with specified id." - } + override fun getDescription(): String = "Click on a child view with specified id." - override fun perform(uiController: UiController, view: View) { + override fun perform( + uiController: UiController, + view: View, + ) { val v = view.findViewById(id) as EditText v.setText(textToBeTyped) } } - } - fun selectSpinnerItemInChildViewWithId(id: Int, position: Int): ViewAction { - return object : ViewAction { - override fun getConstraints(): Matcher? { - return null - } + fun selectSpinnerItemInChildViewWithId( + id: Int, + position: Int, + ): ViewAction = + object : ViewAction { + override fun getConstraints(): Matcher? = null - override fun getDescription(): String { - return "Click on a child view with specified id." - } + override fun getDescription(): String = "Click on a child view with specified id." - override fun perform(uiController: UiController, view: View) { + override fun perform( + uiController: UiController, + view: View, + ) { val v = view.findViewById(id) as AppCompatSpinner v.setSelection(position) } } - } - fun clickItemWithId(id: Int, position: Int): ViewAction { - return object : ViewAction { - override fun getConstraints(): Matcher? { - return null - } + fun clickItemWithId( + id: Int, + position: Int, + ): ViewAction = + object : ViewAction { + override fun getConstraints(): Matcher? = null - override fun getDescription(): String { - return "Click on a child view with specified id." - } + override fun getDescription(): String = "Click on a child view with specified id." - override fun perform(uiController: UiController, view: View) { + override fun perform( + uiController: UiController, + view: View, + ) { val v = view.findViewById(id) as View v.performClick() } } - } - } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/BaseMarker.kt b/app/src/main/java/fr/free/nrw/commons/BaseMarker.kt index 277faea841..1daadb5a16 100644 --- a/app/src/main/java/fr/free/nrw/commons/BaseMarker.kt +++ b/app/src/main/java/fr/free/nrw/commons/BaseMarker.kt @@ -39,26 +39,25 @@ class BaseMarker { constructor() { } - fun fromResource(context: Context, drawableResId: Int) { + fun fromResource( + context: Context, + drawableResId: Int, + ) { val drawable: Drawable = context.resources.getDrawable(drawableResId) - icon = if (drawable is BitmapDrawable) { - (drawable as BitmapDrawable).bitmap - } else { - val bitmap = Bitmap.createBitmap( - drawable.intrinsicWidth, - drawable.intrinsicHeight, Bitmap.Config.ARGB_8888 - ) - val canvas = Canvas(bitmap) - drawable.setBounds(0, 0, canvas.width, canvas.height) - drawable.draw(canvas) - bitmap - } + icon = + if (drawable is BitmapDrawable) { + (drawable as BitmapDrawable).bitmap + } else { + val bitmap = + Bitmap.createBitmap( + drawable.intrinsicWidth, + drawable.intrinsicHeight, + Bitmap.Config.ARGB_8888, + ) + val canvas = Canvas(bitmap) + drawable.setBounds(0, 0, canvas.width, canvas.height) + drawable.draw(canvas) + bitmap + } } } - - - - - - - diff --git a/app/src/main/java/fr/free/nrw/commons/BetaConstants.kt b/app/src/main/java/fr/free/nrw/commons/BetaConstants.kt index 018d787f9c..c0c0b9a61b 100644 --- a/app/src/main/java/fr/free/nrw/commons/BetaConstants.kt +++ b/app/src/main/java/fr/free/nrw/commons/BetaConstants.kt @@ -10,9 +10,10 @@ object BetaConstants { * production server where beta server does not work */ const val COMMONS_URL = "https://commons.wikimedia.org/" + /** * Commons production's depicts property which is used in beta for some specific GET calls on * production server where beta server does not work */ const val DEPICTS_PROPERTY = "P180" -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/CameraPosition.kt b/app/src/main/java/fr/free/nrw/commons/CameraPosition.kt index 31136e61b3..e3a644c6a9 100644 --- a/app/src/main/java/fr/free/nrw/commons/CameraPosition.kt +++ b/app/src/main/java/fr/free/nrw/commons/CameraPosition.kt @@ -3,31 +3,31 @@ package fr.free.nrw.commons import android.os.Parcel import android.os.Parcelable -class CameraPosition(val latitude: Double, val longitude: Double, val zoom: Double) : Parcelable { - +class CameraPosition( + val latitude: Double, + val longitude: Double, + val zoom: Double, +) : Parcelable { constructor(parcel: Parcel) : this( parcel.readDouble(), parcel.readDouble(), - parcel.readDouble() + parcel.readDouble(), ) - override fun writeToParcel(parcel: Parcel, flags: Int) { + override fun writeToParcel( + parcel: Parcel, + flags: Int, + ) { parcel.writeDouble(latitude) parcel.writeDouble(longitude) parcel.writeDouble(zoom) } - override fun describeContents(): Int { - return 0 - } + override fun describeContents(): Int = 0 companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): CameraPosition { - return CameraPosition(parcel) - } + override fun createFromParcel(parcel: Parcel): CameraPosition = CameraPosition(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/fr/free/nrw/commons/Media.kt b/app/src/main/java/fr/free/nrw/commons/Media.kt index 65e66e2793..93efac7b23 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.kt +++ b/app/src/main/java/fr/free/nrw/commons/Media.kt @@ -2,9 +2,11 @@ package fr.free.nrw.commons import android.os.Parcelable import fr.free.nrw.commons.location.LatLng -import kotlinx.parcelize.Parcelize import fr.free.nrw.commons.wikidata.model.page.PageTitle -import java.util.* +import kotlinx.parcelize.Parcelize +import java.util.Date +import java.util.Locale +import java.util.UUID @Parcelize class Media constructor( @@ -14,7 +16,6 @@ class Media constructor( */ var pageId: String = UUID.randomUUID().toString(), var thumbUrl: String? = null, - /** * Gets image URL * @return Image URL @@ -26,16 +27,11 @@ class Media constructor( */ var filename: String? = null, /** - * Gets the file description. + * Gets or sets the file description. * @return file description as a string - */ - // monolingual description on input... - /** - * Sets the file description. * @param fallbackDescription the new description of the file */ var fallbackDescription: String? = null, - /** * Gets the upload date of the file. * Can be null. @@ -43,28 +39,19 @@ class Media constructor( */ var dateUploaded: Date? = null, /** - * Gets the license name of the file. + * Gets or sets the license name of the file. * @return license as a String - */ - /** - * Sets the license name of the file. - * * @param license license name as a String */ var license: String? = null, var licenseUrl: String? = null, /** - * Gets the name of the creator of the file. + * Gets or sets the name of the creator of the file. * @return author name as a String - */ - /** - * Sets the author name of the file. * @param author creator name as a string */ var author: String? = null, - - var user:String?=null, - + var user: String? = null, /** * Gets the categories the file falls under. * @return file categories as an ArrayList of Strings @@ -83,23 +70,23 @@ class Media constructor( * Stores the mapping of category title to hidden attribute * Example: "Mountains" => false, "CC-BY-SA-2.0" => true */ - var categoriesHiddenStatus: Map = emptyMap() + var categoriesHiddenStatus: Map = emptyMap(), ) : Parcelable { - constructor( captions: Map, categories: List?, filename: String?, fallbackDescription: String?, - author: String?, user:String? + author: String?, + user: String?, ) : this( filename = filename, fallbackDescription = fallbackDescription, dateUploaded = Date(), author = author, - user=user, + user = user, categories = categories, - captions = captions + captions = captions, ) /** @@ -108,10 +95,11 @@ class Media constructor( */ val displayTitle: String get() = - if (filename != null) + if (filename != null) { pageTitle.displayTextWithoutNamespace.replaceFirst("[.][^.]+$".toRegex(), "") - else + } else { "" + } /** * Gets file page title @@ -127,9 +115,10 @@ class Media constructor( get() = String.format("[[%s|thumb|%s]]", filename, mostRelevantCaption) val mostRelevantCaption: String - get() = captions[Locale.getDefault().language] - ?: captions.values.firstOrNull() - ?: displayTitle + get() = + captions[Locale.getDefault().language] + ?: captions.values.firstOrNull() + ?: displayTitle /** * Gets the categories the file falls under. @@ -138,6 +127,8 @@ class Media constructor( var addedCategories: List? = null // TODO added categories should be removed. It is added for a short fix. On category update, // categories should be re-fetched instead - get() = field // getter - set(value) { field = value } // setter + get() = field // getter + set(value) { + field = value + } // setter } diff --git a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.kt b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.kt index b18c343e00..2ff54959db 100644 --- a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.kt +++ b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.kt @@ -1,9 +1,9 @@ package fr.free.nrw.commons import androidx.core.text.HtmlCompat -import fr.free.nrw.commons.media.PAGE_ID_PREFIX import fr.free.nrw.commons.media.IdAndCaptions import fr.free.nrw.commons.media.MediaClient +import fr.free.nrw.commons.media.PAGE_ID_PREFIX import io.reactivex.Single import timber.log.Timber import javax.inject.Inject @@ -17,42 +17,46 @@ import javax.inject.Singleton * to the media and may change due to editing. */ @Singleton -class MediaDataExtractor @Inject constructor(private val mediaClient: MediaClient) { - - fun fetchDepictionIdsAndLabels(media: Media) = - mediaClient.getEntities(media.depictionIds) - .map { - it.entities() - .mapValues { entry -> entry.value.labels().mapValues { it.value.value() } } - } - .map { it.map { (key, value) -> IdAndCaptions(key, value) } } - .onErrorReturn { emptyList() } - - fun checkDeletionRequestExists(media: Media) = - mediaClient.checkPageExistsUsingTitle("Commons:Deletion_requests/" + media.filename) - - fun fetchDiscussion(media: Media) = - mediaClient.getPageHtml(media.filename!!.replace("File", "File talk")) - .map { HtmlCompat.fromHtml(it, HtmlCompat.FROM_HTML_MODE_LEGACY).toString() } - .onErrorReturn { - Timber.d("Error occurred while fetching discussion") - "" - } - - fun refresh(media: Media): Single { - return Single.ambArray( - mediaClient.getMediaById(PAGE_ID_PREFIX + media.pageId) - .onErrorResumeNext { Single.never() }, - mediaClient.getMediaSuppressingErrors(media.filename) - .onErrorResumeNext { Single.never() } - ) - +class MediaDataExtractor + @Inject + constructor( + private val mediaClient: MediaClient, + ) { + fun fetchDepictionIdsAndLabels(media: Media) = + mediaClient + .getEntities(media.depictionIds) + .map { + it + .entities() + .mapValues { entry -> entry.value.labels().mapValues { it.value.value() } } + }.map { it.map { (key, value) -> IdAndCaptions(key, value) } } + .onErrorReturn { emptyList() } + + fun checkDeletionRequestExists(media: Media) = mediaClient.checkPageExistsUsingTitle("Commons:Deletion_requests/" + media.filename) + + fun fetchDiscussion(media: Media) = + mediaClient + .getPageHtml(media.filename!!.replace("File", "File talk")) + .map { HtmlCompat.fromHtml(it, HtmlCompat.FROM_HTML_MODE_LEGACY).toString() } + .onErrorReturn { + Timber.d("Error occurred while fetching discussion") + "" + } + + fun refresh(media: Media): Single = + Single.ambArray( + mediaClient + .getMediaById(PAGE_ID_PREFIX + media.pageId) + .onErrorResumeNext { Single.never() }, + mediaClient + .getMediaSuppressingErrors(media.filename) + .onErrorResumeNext { Single.never() }, + ) + + fun getHtmlOfPage(title: String) = mediaClient.getPageHtml(title) + + /** + * Fetches wikitext from mediaClient + */ + fun getCurrentWikiText(title: String) = mediaClient.getCurrentWikiText(title) } - - fun getHtmlOfPage(title: String) = mediaClient.getPageHtml(title); - - /** - * Fetches wikitext from mediaClient - */ - fun getCurrentWikiText(title: String) = mediaClient.getCurrentWikiText(title); -} diff --git a/app/src/main/java/fr/free/nrw/commons/Urls.kt b/app/src/main/java/fr/free/nrw/commons/Urls.kt index d2f6f4caab..3eb7ee243d 100644 --- a/app/src/main/java/fr/free/nrw/commons/Urls.kt +++ b/app/src/main/java/fr/free/nrw/commons/Urls.kt @@ -10,7 +10,9 @@ internal object Urls { const val FAQ_URL = "https://github.com/commons-app/commons-app-documentation/blob/master/android/Frequently-Asked-Questions.md" const val PLAY_STORE_PREFIX = "market://details?id=" const val PLAY_STORE_URL_PREFIX = "https://play.google.com/store/apps/details?id=" - const val TRANSLATE_WIKI_URL = "https://translatewiki.net/w/i.php?title=Special:Translate&group=commons-android-strings&filter=%21translated&action=translate&language=" + const val TRANSLATE_WIKI_URL = + "https://translatewiki.net/w/i.php?title=Special:Translate" + + "&group=commons-android-strings&filter=%21translated&action=translate&language=" const val FACEBOOK_WEB_URL = "https://www.facebook.com/1921335171459985" const val FACEBOOK_APP_URL = "fb://page/1921335171459985" const val FACEBOOK_PACKAGE_NAME = "com.facebook.katana" diff --git a/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt b/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt index 93e833c15d..a3d6de2577 100644 --- a/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt @@ -1,10 +1,9 @@ package fr.free.nrw.commons.actions +import fr.free.nrw.commons.auth.csrf.CsrfTokenClient import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException import io.reactivex.Observable import io.reactivex.Single -import fr.free.nrw.commons.auth.csrf.CsrfTokenClient -import timber.log.Timber /** * This class acts as a Client to facilitate wiki page editing @@ -15,9 +14,8 @@ import timber.log.Timber */ class PageEditClient( private val csrfTokenClient: CsrfTokenClient, - private val pageEditInterface: PageEditInterface + private val pageEditInterface: PageEditInterface, ) { - /** * Replace the content of a wiki page * @param pageTitle Title of the page to edit @@ -25,12 +23,17 @@ class PageEditClient( * @param summary Edit summary * @return whether the edit was successful */ - fun edit(pageTitle: String, text: String, summary: String): Observable { - return try { - pageEditInterface.postEdit(pageTitle, summary, text, csrfTokenClient.getTokenBlocking()) + fun edit( + pageTitle: String, + text: String, + summary: String, + ): Observable = + try { + pageEditInterface + .postEdit(pageTitle, summary, text, csrfTokenClient.getTokenBlocking()) .map { editResponse -> - editResponse.edit()!!.editSucceeded() - } + editResponse.edit()!!.editSucceeded() + } } catch (throwable: Throwable) { if (throwable is InvalidLoginTokenException) { throw throwable @@ -38,7 +41,6 @@ class PageEditClient( Observable.just(false) } } - } /** * Creates a new page with the given title, text, and summary. @@ -49,20 +51,25 @@ class PageEditClient( * @return An observable that emits true if the page creation succeeded, false otherwise. * @throws InvalidLoginTokenException If an invalid login token is encountered during the process. */ - fun postCreate(pageTitle: String, text: String, summary: String): Observable { - return try { - pageEditInterface.postCreate( - pageTitle, - summary, - text, - "text/x-wiki", - "wikitext", - true, - true, - csrfTokenClient.getTokenBlocking() - ).map { editResponse -> - editResponse.edit()!!.editSucceeded() - } + fun postCreate( + pageTitle: String, + text: String, + summary: String, + ): Observable = + try { + pageEditInterface + .postCreate( + pageTitle, + summary, + text, + "text/x-wiki", + "wikitext", + true, + true, + csrfTokenClient.getTokenBlocking(), + ).map { editResponse -> + editResponse.edit()!!.editSucceeded() + } } catch (throwable: Throwable) { if (throwable is InvalidLoginTokenException) { throw throwable @@ -70,7 +77,6 @@ class PageEditClient( Observable.just(false) } } - } /** * Append text to the end of a wiki page @@ -79,9 +85,14 @@ class PageEditClient( * @param summary Edit summary * @return whether the edit was successful */ - fun appendEdit(pageTitle: String, appendText: String, summary: String): Observable { - return try { - pageEditInterface.postAppendEdit(pageTitle, summary, appendText, csrfTokenClient.getTokenBlocking()) + fun appendEdit( + pageTitle: String, + appendText: String, + summary: String, + ): Observable = + try { + pageEditInterface + .postAppendEdit(pageTitle, summary, appendText, csrfTokenClient.getTokenBlocking()) .map { editResponse -> editResponse.edit()!!.editSucceeded() } } catch (throwable: Throwable) { if (throwable is InvalidLoginTokenException) { @@ -90,7 +101,6 @@ class PageEditClient( Observable.just(false) } } - } /** * Prepend text to the beginning of a wiki page @@ -99,9 +109,14 @@ class PageEditClient( * @param summary Edit summary * @return whether the edit was successful */ - fun prependEdit(pageTitle: String, prependText: String, summary: String): Observable { - return try { - pageEditInterface.postPrependEdit(pageTitle, summary, prependText, csrfTokenClient.getTokenBlocking()) + fun prependEdit( + pageTitle: String, + prependText: String, + summary: String, + ): Observable = + try { + pageEditInterface + .postPrependEdit(pageTitle, summary, prependText, csrfTokenClient.getTokenBlocking()) .map { editResponse -> editResponse.edit()?.editSucceeded() ?: false } } catch (throwable: Throwable) { if (throwable is InvalidLoginTokenException) { @@ -110,8 +125,6 @@ class PageEditClient( Observable.just(false) } } - } - /** * Appends a new section to the wiki page @@ -121,9 +134,15 @@ class PageEditClient( * @param summary Edit summary * @return whether the edit was successful */ - fun createNewSection(pageTitle: String, sectionTitle: String, sectionText: String, summary: String): Observable { - return try { - pageEditInterface.postNewSection(pageTitle, summary, sectionTitle, sectionText, csrfTokenClient.getTokenBlocking()) + fun createNewSection( + pageTitle: String, + sectionTitle: String, + sectionText: String, + summary: String, + ): Observable = + try { + pageEditInterface + .postNewSection(pageTitle, summary, sectionTitle, sectionText, csrfTokenClient.getTokenBlocking()) .map { editResponse -> editResponse.edit()!!.editSucceeded() } } catch (throwable: Throwable) { if (throwable is InvalidLoginTokenException) { @@ -132,8 +151,6 @@ class PageEditClient( Observable.just(false) } } - } - /** * Set new labels to Wikibase server of commons @@ -143,12 +160,21 @@ class PageEditClient( * @param value label * @return 1 when the edit was successful */ - fun setCaptions(summary: String, title: String, - language: String, value: String) : Observable{ - return try { - pageEditInterface.postCaptions(summary, title, language, - value, csrfTokenClient.getTokenBlocking() - ).map { it.success } + fun setCaptions( + summary: String, + title: String, + language: String, + value: String, + ): Observable = + try { + pageEditInterface + .postCaptions( + summary, + title, + language, + value, + csrfTokenClient.getTokenBlocking(), + ).map { it.success } } catch (throwable: Throwable) { if (throwable is InvalidLoginTokenException) { throw throwable @@ -156,16 +182,20 @@ class PageEditClient( Observable.just(0) } } - } /** * Get whole WikiText of required file * @param title : Name of the file * @return Observable */ - fun getCurrentWikiText(title: String): Single { - return pageEditInterface.getWikiText(title).map { - it.query()?.pages()?.get(0)?.revisions()?.get(0)?.content() + fun getCurrentWikiText(title: String): Single = + pageEditInterface.getWikiText(title).map { + it + .query() + ?.pages() + ?.get(0) + ?.revisions() + ?.get(0) + ?.content() } - } } diff --git a/app/src/main/java/fr/free/nrw/commons/actions/PageEditInterface.kt b/app/src/main/java/fr/free/nrw/commons/actions/PageEditInterface.kt index 1aa0bbfce3..db43bb6200 100644 --- a/app/src/main/java/fr/free/nrw/commons/actions/PageEditInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/actions/PageEditInterface.kt @@ -3,10 +3,15 @@ package fr.free.nrw.commons.actions import fr.free.nrw.commons.wikidata.WikidataConstants.MW_API_PREFIX import fr.free.nrw.commons.wikidata.model.Entities import fr.free.nrw.commons.wikidata.model.edit.Edit +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse import io.reactivex.Observable import io.reactivex.Single -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse -import retrofit2.http.* +import retrofit2.http.Field +import retrofit2.http.FormUrlEncoded +import retrofit2.http.GET +import retrofit2.http.Headers +import retrofit2.http.POST +import retrofit2.http.Query /** * This interface facilitates wiki commons page editing services to the Networking module @@ -33,7 +38,7 @@ interface PageEditInterface { @Field("summary") summary: String, @Field("text") text: String, // NOTE: This csrf shold always be sent as the last field of form data - @Field("token") token: String + @Field("token") token: String, ): Observable /** @@ -60,7 +65,7 @@ interface PageEditInterface { @Field("minor") minor: Boolean, @Field("recreate") recreate: Boolean, // NOTE: This csrf shold always be sent as the last field of form data - @Field("token") token: String + @Field("token") token: String, ): Observable /** @@ -79,7 +84,7 @@ interface PageEditInterface { @Field("title") title: String, @Field("summary") summary: String, @Field("appendtext") appendText: String, - @Field("token") token: String + @Field("token") token: String, ): Observable /** @@ -98,7 +103,7 @@ interface PageEditInterface { @Field("title") title: String, @Field("summary") summary: String, @Field("prependtext") prependText: String, - @Field("token") token: String + @Field("token") token: String, ): Observable @FormUrlEncoded @@ -109,7 +114,7 @@ interface PageEditInterface { @Field("summary") summary: String, @Field("sectiontitle") sectionTitle: String, @Field("text") sectionText: String, - @Field("token") token: String + @Field("token") token: String, ): Observable @FormUrlEncoded @@ -120,7 +125,7 @@ interface PageEditInterface { @Field("title") title: String, @Field("language") language: String, @Field("value") value: String, - @Field("token") token: String + @Field("token") token: String, ): Observable /** @@ -130,6 +135,6 @@ interface PageEditInterface { */ @GET(MW_API_PREFIX + "action=query&prop=revisions&rvprop=content|timestamp&rvlimit=1&converttitles=") fun getWikiText( - @Query("titles") title: String + @Query("titles") title: String, ): Single -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt b/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt index 06ad63d256..de716db99e 100644 --- a/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt @@ -1,11 +1,10 @@ package fr.free.nrw.commons.actions import fr.free.nrw.commons.CommonsApplication -import fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF -import io.reactivex.Observable import fr.free.nrw.commons.auth.csrf.CsrfTokenClient import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException -import fr.free.nrw.commons.auth.login.LoginFailedException +import fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF +import io.reactivex.Observable import javax.inject.Inject import javax.inject.Named import javax.inject.Singleton @@ -15,34 +14,33 @@ import javax.inject.Singleton * Thanks are used by a user to show gratitude to another user for their contributions */ @Singleton -class ThanksClient @Inject constructor( - @param:Named(NAMED_COMMONS_CSRF) private val csrfTokenClient: CsrfTokenClient, - private val service: ThanksInterface -) { - /** - * Thanks a user for a particular revision - * @param revisionId The revision ID the user would like to thank someone for - * @return if thanks was successfully sent to intended recipient - */ - fun thank(revisionId: Long): Observable { - return try { - service.thank( - revisionId.toString(), // Rev - null, // Log - csrfTokenClient.getTokenBlocking(), // Token - CommonsApplication.getInstance().userAgent // Source - ).map { - mwThankPostResponse -> mwThankPostResponse.result?.success == 1 +class ThanksClient + @Inject + constructor( + @param:Named(NAMED_COMMONS_CSRF) private val csrfTokenClient: CsrfTokenClient, + private val service: ThanksInterface, + ) { + /** + * Thanks a user for a particular revision + * @param revisionId The revision ID the user would like to thank someone for + * @return if thanks was successfully sent to intended recipient + */ + fun thank(revisionId: Long): Observable = + try { + service + .thank( + revisionId.toString(), // Rev + null, // Log + csrfTokenClient.getTokenBlocking(), // Token + CommonsApplication.getInstance().userAgent, // Source + ).map { mwThankPostResponse -> + mwThankPostResponse.result?.success == 1 + } + } catch (throwable: Throwable) { + if (throwable is InvalidLoginTokenException) { + Observable.error(throwable) + } else { + Observable.just(false) + } } - } - catch (throwable: Throwable) { - if (throwable is InvalidLoginTokenException) { - Observable.error(throwable) - } - else { - Observable.just(false) - } - } } - -} diff --git a/app/src/main/java/fr/free/nrw/commons/actions/ThanksInterface.kt b/app/src/main/java/fr/free/nrw/commons/actions/ThanksInterface.kt index bc7d224af5..62934d0f2f 100644 --- a/app/src/main/java/fr/free/nrw/commons/actions/ThanksInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/actions/ThanksInterface.kt @@ -19,6 +19,6 @@ interface ThanksInterface { @Field("rev") rev: String?, @Field("log") log: String?, @Field("token") token: String, - @Field("source") source: String? + @Field("source") source: String?, ): Observable } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt b/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt index 88cc6b9530..f35e5f0035 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/csrf/CsrfTokenClient.kt @@ -2,11 +2,11 @@ package fr.free.nrw.commons.auth.csrf import androidx.annotation.VisibleForTesting import fr.free.nrw.commons.auth.SessionManager -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse -import fr.free.nrw.commons.auth.login.LoginClient import fr.free.nrw.commons.auth.login.LoginCallback +import fr.free.nrw.commons.auth.login.LoginClient import fr.free.nrw.commons.auth.login.LoginFailedException import fr.free.nrw.commons.auth.login.LoginResult +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse import retrofit2.Call import retrofit2.Response import timber.log.Timber @@ -17,12 +17,11 @@ class CsrfTokenClient( private val sessionManager: SessionManager, private val csrfTokenInterface: CsrfTokenInterface, private val loginClient: LoginClient, - private val logoutClient: LogoutClient + private val logoutClient: LogoutClient, ) { private var retries = 0 private var csrfTokenCall: Call? = null - @Throws(Throwable::class) fun getTokenBlocking(): String { var token = "" @@ -37,11 +36,20 @@ class CsrfTokenClient( } // Get CSRFToken response off the main thread. - val response = newSingleThreadExecutor().submit(Callable { - csrfTokenInterface.getCsrfTokenCall().execute() - }).get() - - if (response.body()?.query()?.csrfToken().isNullOrEmpty()) { + val response = + newSingleThreadExecutor() + .submit( + Callable { + csrfTokenInterface.getCsrfTokenCall().execute() + }, + ).get() + + if (response + .body() + ?.query() + ?.csrfToken() + .isNullOrEmpty() + ) { continue } @@ -51,9 +59,8 @@ class CsrfTokenClient( } break } catch (e: LoginFailedException) { - throw InvalidLoginTokenException(ANONYMOUS_TOKEN_MESSAGE) - } - catch (t: Throwable) { + throw InvalidLoginTokenException(ANONYMOUS_TOKEN_MESSAGE) + } catch (t: Throwable) { Timber.w(t) } } @@ -65,45 +72,65 @@ class CsrfTokenClient( } @VisibleForTesting - fun request(service: CsrfTokenInterface, cb: Callback): Call = - requestToken(service, object : Callback { - override fun success(token: String?) { - if (sessionManager.isUserLoggedIn && token == ANON_TOKEN) { - retryWithLogin(cb) { - InvalidLoginTokenException(ANONYMOUS_TOKEN_MESSAGE) + fun request( + service: CsrfTokenInterface, + cb: Callback, + ): Call = + requestToken( + service, + object : Callback { + override fun success(token: String?) { + if (sessionManager.isUserLoggedIn && token == ANON_TOKEN) { + retryWithLogin(cb) { + InvalidLoginTokenException(ANONYMOUS_TOKEN_MESSAGE) + } + } else { + cb.success(token) } - } else { - cb.success(token) } - } - override fun failure(caught: Throwable?) = retryWithLogin(cb) { caught } + override fun failure(caught: Throwable?) = retryWithLogin(cb) { caught } - override fun twoFactorPrompt() = cb.twoFactorPrompt() - }) + override fun twoFactorPrompt() = cb.twoFactorPrompt() + }, + ) @VisibleForTesting - fun requestToken(service: CsrfTokenInterface, cb: Callback): Call { + fun requestToken( + service: CsrfTokenInterface, + cb: Callback, + ): Call { val call = service.getCsrfTokenCall() - call.enqueue(object : retrofit2.Callback { - override fun onResponse(call: Call, response: Response) { - if (call.isCanceled) { - return + call.enqueue( + object : retrofit2.Callback { + override fun onResponse( + call: Call, + response: Response, + ) { + if (call.isCanceled) { + return + } + cb.success(response.body()!!.query()!!.csrfToken()) } - cb.success(response.body()!!.query()!!.csrfToken()) - } - override fun onFailure(call: Call, t: Throwable) { - if (call.isCanceled) { - return + override fun onFailure( + call: Call, + t: Throwable, + ) { + if (call.isCanceled) { + return + } + cb.failure(t) } - cb.failure(t) - } - }) + }, + ) return call } - private fun retryWithLogin(callback: Callback, caught: () -> Throwable?) { + private fun retryWithLogin( + callback: Callback, + caught: () -> Throwable?, + ) { val userName = sessionManager.userName val password = sessionManager.password if (retries < MAX_RETRIES && !userName.isNullOrEmpty() && !password.isNullOrEmpty()) { @@ -123,26 +150,31 @@ class CsrfTokenClient( username: String, password: String, callback: Callback, - retryCallback: () -> Unit - ) = loginClient.request(username, password, object : LoginCallback { - override fun success(loginResult: LoginResult) { - if (loginResult.pass) { - sessionManager.updateAccount(loginResult) - retryCallback() - } else { - callback.failure(LoginFailedException(loginResult.message)) + retryCallback: () -> Unit, + ) = loginClient.request( + username, + password, + object : LoginCallback { + override fun success(loginResult: LoginResult) { + if (loginResult.pass) { + sessionManager.updateAccount(loginResult) + retryCallback() + } else { + callback.failure(LoginFailedException(loginResult.message)) + } } - } - override fun twoFactorPrompt(caught: Throwable, token: String?) = - callback.twoFactorPrompt() + override fun twoFactorPrompt( + caught: Throwable, + token: String?, + ) = callback.twoFactorPrompt() - // Should not happen here, but call the callback just in case. - override fun passwordResetPrompt(token: String?) = - callback.failure(LoginFailedException("Logged in with temporary password.")) + // Should not happen here, but call the callback just in case. + override fun passwordResetPrompt(token: String?) = callback.failure(LoginFailedException("Logged in with temporary password.")) - override fun error(caught: Throwable) = callback.failure(caught) - }) + override fun error(caught: Throwable) = callback.failure(caught) + }, + ) private fun cancel() { loginClient.cancel() @@ -154,7 +186,9 @@ class CsrfTokenClient( interface Callback { fun success(token: String?) + fun failure(caught: Throwable?) + fun twoFactorPrompt() } @@ -166,5 +200,7 @@ class CsrfTokenClient( const val ANONYMOUS_TOKEN_MESSAGE = "App believes we're logged in, but got anonymous token." } } -class InvalidLoginTokenException(message: String) : Exception(message) +class InvalidLoginTokenException( + message: String, +) : Exception(message) diff --git a/app/src/main/java/fr/free/nrw/commons/auth/csrf/LogoutClient.kt b/app/src/main/java/fr/free/nrw/commons/auth/csrf/LogoutClient.kt index eb462beaf4..84481c918d 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/csrf/LogoutClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/csrf/LogoutClient.kt @@ -3,6 +3,10 @@ package fr.free.nrw.commons.auth.csrf import fr.free.nrw.commons.wikidata.cookies.CommonsCookieStorage import javax.inject.Inject -class LogoutClient @Inject constructor(private val store: CommonsCookieStorage) { - fun logout() = store.clear() -} \ No newline at end of file +class LogoutClient + @Inject + constructor( + private val store: CommonsCookieStorage, + ) { + fun logout() = store.clear() + } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginCallback.kt b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginCallback.kt index cfaf90b58d..8092f73ae7 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginCallback.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginCallback.kt @@ -2,7 +2,13 @@ package fr.free.nrw.commons.auth.login interface LoginCallback { fun success(loginResult: LoginResult) - fun twoFactorPrompt(caught: Throwable, token: String?) + + fun twoFactorPrompt( + caught: Throwable, + token: String?, + ) + fun passwordResetPrompt(token: String?) + fun error(caught: Throwable) } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginClient.kt b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginClient.kt index 44bb684488..48ca37cf0e 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginClient.kt @@ -4,9 +4,9 @@ import android.text.TextUtils import fr.free.nrw.commons.auth.login.LoginResult.OAuthResult import fr.free.nrw.commons.auth.login.LoginResult.ResetPasswordResult import fr.free.nrw.commons.wikidata.WikidataConstants.WIKIPEDIA_URL +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse import retrofit2.Call import retrofit2.Callback import retrofit2.Response @@ -16,7 +16,9 @@ import java.io.IOException /** * Responsible for making login related requests to the server. */ -class LoginClient(private val loginInterface: LoginInterface) { +class LoginClient( + private val loginInterface: LoginInterface, +) { private var tokenCall: Call? = null private var loginCall: Call? = null @@ -30,80 +32,116 @@ class LoginClient(private val loginInterface: LoginInterface) { private fun getLoginToken() = loginInterface.getLoginToken() - fun request(userName: String, password: String, cb: LoginCallback) { + fun request( + userName: String, + password: String, + cb: LoginCallback, + ) { cancel() tokenCall = getLoginToken() - tokenCall!!.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - login( - userName, password, null, null, response.body()!!.query()!!.loginToken(), - userLanguage, cb - ) - } + tokenCall!!.enqueue( + object : Callback { + override fun onResponse( + call: Call, + response: Response, + ) { + login( + userName, + password, + null, + null, + response.body()!!.query()!!.loginToken(), + userLanguage, + cb, + ) + } - override fun onFailure(call: Call, caught: Throwable) { - if (call.isCanceled) { - return + override fun onFailure( + call: Call, + caught: Throwable, + ) { + if (call.isCanceled) { + return + } + cb.error(caught) } - cb.error(caught) - } - }) + }, + ) } fun login( - userName: String, password: String, retypedPassword: String?, twoFactorCode: String?, - loginToken: String?, userLanguage: String, cb: LoginCallback + userName: String, + password: String, + retypedPassword: String?, + twoFactorCode: String?, + loginToken: String?, + userLanguage: String, + cb: LoginCallback, ) { this.userLanguage = userLanguage - loginCall = if (twoFactorCode.isNullOrEmpty() && retypedPassword.isNullOrEmpty()) { - loginInterface.postLogIn(userName, password, loginToken, userLanguage, WIKIPEDIA_URL) - } else { - loginInterface.postLogIn( - userName, password, retypedPassword, twoFactorCode, loginToken, userLanguage, true - ) - } + loginCall = + if (twoFactorCode.isNullOrEmpty() && retypedPassword.isNullOrEmpty()) { + loginInterface.postLogIn(userName, password, loginToken, userLanguage, WIKIPEDIA_URL) + } else { + loginInterface.postLogIn( + userName, + password, + retypedPassword, + twoFactorCode, + loginToken, + userLanguage, + true, + ) + } + + loginCall!!.enqueue( + object : Callback { + override fun onResponse( + call: Call, + response: Response, + ) { + val loginResult = response.body()?.toLoginResult(password) + if (loginResult != null) { + if (loginResult.pass && !loginResult.userName.isNullOrEmpty()) { + // The server could do some transformations on user names, e.g. on some + // wikis is uppercases the first letter. + getExtendedInfo(loginResult.userName, loginResult, cb) + } else if ("UI" == loginResult.status) { + when (loginResult) { + is OAuthResult -> + cb.twoFactorPrompt( + LoginFailedException(loginResult.message), + loginToken, + ) - loginCall!!.enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response - ) { - val loginResult = response.body()?.toLoginResult(password) - if (loginResult != null) { - if (loginResult.pass && !loginResult.userName.isNullOrEmpty()) { - // The server could do some transformations on user names, e.g. on some - // wikis is uppercases the first letter. - getExtendedInfo(loginResult.userName, loginResult, cb) - } else if ("UI" == loginResult.status) { - when (loginResult) { - is OAuthResult -> cb.twoFactorPrompt( - LoginFailedException(loginResult.message), - loginToken - ) - - is ResetPasswordResult -> cb.passwordResetPrompt(loginToken) - - is LoginResult.Result -> cb.error( - LoginFailedException(loginResult.message) - ) + is ResetPasswordResult -> cb.passwordResetPrompt(loginToken) + + is LoginResult.Result -> + cb.error( + LoginFailedException(loginResult.message), + ) + } + } else { + cb.error(LoginFailedException(loginResult.message)) } } else { - cb.error(LoginFailedException(loginResult.message)) + cb.error(IOException("Login failed. Unexpected response.")) } - } else { - cb.error(IOException("Login failed. Unexpected response.")) } - } - override fun onFailure(call: Call, t: Throwable) { - if (call.isCanceled) { - return + override fun onFailure( + call: Call, + t: Throwable, + ) { + if (call.isCanceled) { + return + } + cb.error(t) } - cb.error(t) - } - }) + }, + ) } fun doLogin( @@ -111,43 +149,65 @@ class LoginClient(private val loginInterface: LoginInterface) { password: String, twoFactorCode: String, userLanguage: String, - loginCallback: LoginCallback + loginCallback: LoginCallback, ) { - getLoginToken().enqueue(object :Callback{ - override fun onResponse( - call: Call, - response: Response - ) = if (response.isSuccessful){ - val loginToken = response.body()?.query()?.loginToken() - loginToken?.let { - login(username, password, null, twoFactorCode, it, userLanguage, loginCallback) - } ?: run { + getLoginToken().enqueue( + object : Callback { + override fun onResponse( + call: Call, + response: Response, + ) = if (response.isSuccessful) { + val loginToken = response.body()?.query()?.loginToken() + loginToken?.let { + login(username, password, null, twoFactorCode, it, userLanguage, loginCallback) + } ?: run { + loginCallback.error(IOException("Failed to retrieve login token")) + } + } else { loginCallback.error(IOException("Failed to retrieve login token")) } - } else { - loginCallback.error(IOException("Failed to retrieve login token")) - } - override fun onFailure(call: Call, t: Throwable) { - loginCallback.error(t) - } - }) + override fun onFailure( + call: Call, + t: Throwable, + ) { + loginCallback.error(t) + } + }, + ) } + @Throws(Throwable::class) - fun loginBlocking(userName: String, password: String, twoFactorCode: String?) { + fun loginBlocking( + userName: String, + password: String, + twoFactorCode: String?, + ) { val tokenResponse = getLoginToken().execute() - if (tokenResponse.body()?.query()?.loginToken().isNullOrEmpty()) { + if (tokenResponse + .body() + ?.query() + ?.loginToken() + .isNullOrEmpty() + ) { throw IOException("Unexpected response when getting login token.") } val loginToken = tokenResponse.body()?.query()?.loginToken() - val tempLoginCall = if (twoFactorCode.isNullOrEmpty()) { - loginInterface.postLogIn(userName, password, loginToken, userLanguage, WIKIPEDIA_URL) - } else { - loginInterface.postLogIn( - userName, password, null, twoFactorCode, loginToken, userLanguage, true - ) - } + val tempLoginCall = + if (twoFactorCode.isNullOrEmpty()) { + loginInterface.postLogIn(userName, password, loginToken, userLanguage, WIKIPEDIA_URL) + } else { + loginInterface.postLogIn( + userName, + password, + null, + twoFactorCode, + loginToken, + userLanguage, + true, + ) + } val response = tempLoginCall.execute() val loginResponse = response.body() ?: throw IOException("Unexpected response when logging in.") @@ -166,18 +226,23 @@ class LoginClient(private val loginInterface: LoginInterface) { } } - private fun getExtendedInfo(userName: String, loginResult: LoginResult, cb: LoginCallback) = - loginInterface.getUserInfo(userName) - .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) - .subscribe({ response: MwQueryResponse? -> - loginResult.userId = response?.query()?.userInfo()?.id() ?: 0 - loginResult.groups = - response?.query()?.getUserResponse(userName)?.groups ?: emptySet() - cb.success(loginResult) - }, { caught: Throwable -> - Timber.e(caught, "Login succeeded but getting group information failed. ") - cb.error(caught) - }) + private fun getExtendedInfo( + userName: String, + loginResult: LoginResult, + cb: LoginCallback, + ) = loginInterface + .getUserInfo(userName) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ response: MwQueryResponse? -> + loginResult.userId = response?.query()?.userInfo()?.id() ?: 0 + loginResult.groups = + response?.query()?.getUserResponse(userName)?.groups ?: emptySet() + cb.success(loginResult) + }, { caught: Throwable -> + Timber.e(caught, "Login succeeded but getting group information failed. ") + cb.error(caught) + }) fun cancel() { tokenCall?.let { diff --git a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginFailedException.kt b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginFailedException.kt index 2f60b2071c..fb5ad14c69 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginFailedException.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginFailedException.kt @@ -1,3 +1,5 @@ package fr.free.nrw.commons.auth.login -class LoginFailedException(message: String?) : Throwable(message) +class LoginFailedException( + message: String?, +) : Throwable(message) diff --git a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginInterface.kt b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginInterface.kt index 0502cf8c7f..07e1cd45cc 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginInterface.kt @@ -1,8 +1,8 @@ package fr.free.nrw.commons.auth.login import fr.free.nrw.commons.wikidata.WikidataConstants.MW_API_PREFIX -import io.reactivex.Observable import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse +import io.reactivex.Observable import retrofit2.Call import retrofit2.http.Field import retrofit2.http.FormUrlEncoded @@ -24,7 +24,7 @@ interface LoginInterface { @Field("password") pass: String?, @Field("logintoken") token: String?, @Field("uselang") userLanguage: String?, - @Field("loginreturnurl") url: String? + @Field("loginreturnurl") url: String?, ): Call @Headers("Cache-Control: no-cache") @@ -37,9 +37,11 @@ interface LoginInterface { @Field("OATHToken") twoFactorCode: String?, @Field("logintoken") token: String?, @Field("uselang") userLanguage: String?, - @Field("logincontinue") loginContinue: Boolean + @Field("logincontinue") loginContinue: Boolean, ): Call @GET(MW_API_PREFIX + "action=query&meta=userinfo&list=users&usprop=groups|cancreate") - fun getUserInfo(@Query("ususers") userName: String): Observable -} \ No newline at end of file + fun getUserInfo( + @Query("ususers") userName: String, + ): Observable +} diff --git a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResponse.kt b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResponse.kt index 7e3a80c9ee..a96778e380 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResponse.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResponse.kt @@ -13,9 +13,7 @@ class LoginResponse { @SerializedName("clientlogin") private val clientLogin: ClientLogin? = null - fun toLoginResult(password: String): LoginResult? { - return clientLogin?.toLoginResult(password) - } + fun toLoginResult(password: String): LoginResult? = clientLogin?.toLoginResult(password) } internal class ClientLogin { @@ -39,7 +37,7 @@ internal class ClientLogin { } } } else if ("PASS" != status && "FAIL" != status) { - //TODO: String resource -- Looks like needed for others in this class too + // TODO: String resource -- Looks like needed for others in this class too userMessage = "An unknown error occurred." } return Result(status ?: "", userName, password, userMessage) diff --git a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResult.kt b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResult.kt index 69e4a7f895..6a7594ec02 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResult.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/login/LoginResult.kt @@ -4,7 +4,7 @@ sealed class LoginResult( val status: String, val userName: String?, val password: String?, - val message: String? + val message: String?, ) { var userId = 0 var groups = emptySet() @@ -14,20 +14,20 @@ sealed class LoginResult( status: String, userName: String?, password: String?, - message: String? - ): LoginResult(status, userName, password, message) + message: String?, + ) : LoginResult(status, userName, password, message) class OAuthResult( status: String, userName: String?, password: String?, - message: String? + message: String?, ) : LoginResult(status, userName, password, message) class ResetPasswordResult( status: String, userName: String?, password: String?, - message: String? + message: String?, ) : LoginResult(status, userName, password, message) } diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsAdapter.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsAdapter.kt index 64bdf5315e..4233d95087 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsAdapter.kt @@ -15,25 +15,34 @@ import fr.free.nrw.commons.upload.structure.depictions.DepictedItem /** * Helps to inflate Wikidata Items into Items tab */ -class BookmarkItemsAdapter (val list: List, val context: Context) : - RecyclerView.Adapter() { - - class BookmarkItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - +class BookmarkItemsAdapter( + val list: List, + val context: Context, +) : RecyclerView.Adapter() { + class BookmarkItemViewHolder( + itemView: View, + ) : RecyclerView.ViewHolder(itemView) { var depictsLabel: TextView = itemView.findViewById(R.id.depicts_label) var description: TextView = itemView.findViewById(R.id.description) var depictsImage: SimpleDraweeView = itemView.findViewById(R.id.depicts_image) - var layout : ConstraintLayout = itemView.findViewById(R.id.layout_item) + var layout: ConstraintLayout = itemView.findViewById(R.id.layout_item) } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookmarkItemViewHolder { - val v: View = LayoutInflater.from(context) - .inflate(R.layout.item_depictions, parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): BookmarkItemViewHolder { + val v: View = + LayoutInflater + .from(context) + .inflate(R.layout.item_depictions, parent, false) return BookmarkItemViewHolder(v) } - override fun onBindViewHolder(holder: BookmarkItemViewHolder, position: Int) { - + override fun onBindViewHolder( + holder: BookmarkItemViewHolder, + position: Int, + ) { val depictedItem = list[position] holder.depictsLabel.text = depictedItem.name holder.description.text = depictedItem.description @@ -48,7 +57,5 @@ class BookmarkItemsAdapter (val list: List, val context: Context) } } - override fun getItemCount(): Int { - return list.size - } -} \ No newline at end of file + override fun getItemCount(): Int = list.size +} diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/models/Bookmark.kt b/app/src/main/java/fr/free/nrw/commons/bookmarks/models/Bookmark.kt index 541baeb690..a33638e723 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/models/Bookmark.kt +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/models/Bookmark.kt @@ -2,25 +2,25 @@ package fr.free.nrw.commons.bookmarks.models import android.net.Uri -class Bookmark(mediaName: String?, mediaCreator: String?, - /** - * Modifies the content URI - marking this bookmark as already saved in the database - * @param contentUri the content URI - */ - var contentUri: Uri?) { +class Bookmark( + mediaName: String?, + mediaCreator: String?, /** - * Gets the content URI for this bookmark + * Gets or Sets the content URI - marking this bookmark as already saved in the database * @return content URI + * @param contentUri the content URI */ + var contentUri: Uri?, +) { /** * Gets the media name * @return the media name */ val mediaName: String = mediaName ?: "" + /** * Gets media creator * @return creator name */ val mediaCreator: String = mediaCreator ?: "" - -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt index dd82e181d3..6bf0bc0ed6 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignConfig.kt @@ -8,6 +8,7 @@ import com.google.gson.annotations.SerializedName class CampaignConfig { @SerializedName("showOnlyLiveCampaigns") private val showOnlyLiveCampaigns = false + @SerializedName("sortBy") private val sortBy: String? = null -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt index cb37850556..767732eb78 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignResponseDTO.kt @@ -9,7 +9,7 @@ import fr.free.nrw.commons.campaigns.models.Campaign class CampaignResponseDTO { @SerializedName("config") val campaignConfig: CampaignConfig? = null + @SerializedName("campaigns") val campaigns: List? = null - -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/models/Campaign.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/models/Campaign.kt index 006415a136..cd68797e0d 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/models/Campaign.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/models/Campaign.kt @@ -3,9 +3,11 @@ package fr.free.nrw.commons.campaigns.models /** * A data class to hold a campaign */ -data class Campaign(var title: String? = null, - var description: String? = null, - var startDate: String? = null, - var endDate: String? = null, - var link: String? = null, - var isWLMCampaign: Boolean = false) \ No newline at end of file +data class Campaign( + var title: String? = null, + var description: String? = null, + var startDate: String? = null, + var endDate: String? = null, + var link: String? = null, + var isWLMCampaign: Boolean = false, +) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.kt b/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.kt index c69789c21c..1cb50b8f3b 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.kt +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.kt @@ -8,275 +8,287 @@ import fr.free.nrw.commons.utils.StringSortingUtils import io.reactivex.Observable import io.reactivex.functions.Function4 import timber.log.Timber -import java.util.* +import java.util.Calendar +import java.util.Date import javax.inject.Inject /** * The model class for categories in upload */ -class CategoriesModel @Inject constructor( - private val categoryClient: CategoryClient, - private val categoryDao: CategoryDao, - private val gpsCategoryModel: GpsCategoryModel -) { - private val selectedCategories: MutableList = mutableListOf() +class CategoriesModel + @Inject + constructor( + private val categoryClient: CategoryClient, + private val categoryDao: CategoryDao, + private val gpsCategoryModel: GpsCategoryModel, + ) { + private val selectedCategories: MutableList = mutableListOf() - /** - * Existing categories which are selected - */ - private var selectedExistingCategories: MutableList = mutableListOf() + /** + * Existing categories which are selected + */ + private var selectedExistingCategories: MutableList = mutableListOf() - /** - * Returns true if an item is considered to be a spammy category which should be ignored - * - * @param item a category item that needs to be validated to know if it is spammy or not - * @return - */ - fun isSpammyCategory(item: String): Boolean { - //Check for current and previous year to exclude these categories from removal - val now = Calendar.getInstance() - val curYear = now[Calendar.YEAR] - val curYearInString = curYear.toString() - val prevYear = curYear - 1 - val prevYearInString = prevYear.toString() - Timber.d("Previous year: %s", prevYearInString) + /** + * Returns true if an item is considered to be a spammy category which should be ignored + * + * @param item a category item that needs to be validated to know if it is spammy or not + * @return + */ + fun isSpammyCategory(item: String): Boolean { + // Check for current and previous year to exclude these categories from removal + val now = Calendar.getInstance() + val curYear = now[Calendar.YEAR] + val curYearInString = curYear.toString() + val prevYear = curYear - 1 + val prevYearInString = prevYear.toString() + Timber.d("Previous year: %s", prevYearInString) - val mentionsDecade = item.matches(".*0s.*".toRegex()) - val recentDecade = item.matches(".*20[0-2]0s.*".toRegex()) - val spammyCategory = item.matches("(.*)needing(.*)".toRegex()) - || item.matches("(.*)taken on(.*)".toRegex()) + val mentionsDecade = item.matches(".*0s.*".toRegex()) + val recentDecade = item.matches(".*20[0-2]0s.*".toRegex()) + val spammyCategory = + item.matches("(.*)needing(.*)".toRegex()) || + item.matches("(.*)taken on(.*)".toRegex()) - // always skip irrelevant categories such as Media_needing_categories_as_of_16_June_2017(Issue #750) - if (spammyCategory) { - return true - } + // always skip irrelevant categories such as Media_needing_categories_as_of_16_June_2017(Issue #750) + if (spammyCategory) { + return true + } - if (mentionsDecade) { - // Check if the year in the form of XX(X)0s is recent/relevant, i.e. in the 2000s or 2010s/2020s as stated in Issue #1029 - // Example: "2020s" is OK, but "1920s" is not (and should be skipped) - return !recentDecade - } else { - // If it is not an year in decade form (e.g. 19xxs/20xxs), then check if item contains a 4-digit year - // anywhere within the string (.* is wildcard) (Issue #47) - // And that item does not equal the current year or previous year - return item.matches(".*(19|20)\\d{2}.*".toRegex()) - && !item.contains(curYearInString) - && !item.contains(prevYearInString) + if (mentionsDecade) { + // Check if the year in the form of XX(X)0s is recent/relevant, i.e. in the 2000s or 2010s/2020s as stated in Issue #1029 + // Example: "2020s" is OK, but "1920s" is not (and should be skipped) + return !recentDecade + } else { + // If it is not an year in decade form (e.g. 19xxs/20xxs), then check if item contains a 4-digit year + // anywhere within the string (.* is wildcard) (Issue #47) + // And that item does not equal the current year or previous year + return item.matches(".*(19|20)\\d{2}.*".toRegex()) && + !item.contains(curYearInString) && + !item.contains(prevYearInString) + } } - } - /** - * Updates category count in category dao - * @param item - */ - fun updateCategoryCount(item: CategoryItem) { - var category = categoryDao.find(item.name) + /** + * Updates category count in category dao + * @param item + */ + fun updateCategoryCount(item: CategoryItem) { + var category = categoryDao.find(item.name) - // Newly used category... - if (category == null) { - category = Category(null, item.name, item.description, item.thumbnail, Date(), 0) + // Newly used category... + if (category == null) { + category = Category(null, item.name, item.description, item.thumbnail, Date(), 0) + } + category.incTimesUsed() + categoryDao.save(category) } - category.incTimesUsed() - categoryDao.save(category) - } - - /** - * Regional category search - * @param term - * @param imageTitleList - * @return - */ - fun searchAll( - term: String, - imageTitleList: List, - selectedDepictions: List - ): Observable> { - return suggestionsOrSearch(term, imageTitleList, selectedDepictions) - .map { it.map { CategoryItem(it.name, it.description, it.thumbnail, false) } } - } - - private fun suggestionsOrSearch( - term: String, - imageTitleList: List, - selectedDepictions: List - ): Observable> { - return if (TextUtils.isEmpty(term)) - Observable.combineLatest( - categoriesFromDepiction(selectedDepictions), - gpsCategoryModel.categoriesFromLocation, - titleCategories(imageTitleList), - Observable.just(categoryDao.recentCategories(SEARCH_CATS_LIMIT)), - Function4(::combine) - ) - else - categoryClient.searchCategoriesForPrefix(term, SEARCH_CATS_LIMIT) - .map { it.sortedWith(StringSortingUtils.sortBySimilarity(term)) } - .toObservable() - } - - /** - * Fetches details of every category associated with selected depictions, converts them into - * CategoryItem and returns them in a list. - * - * @param selectedDepictions selected DepictItems - * @return List of CategoryItem associated with selected depictions - */ - private fun categoriesFromDepiction(selectedDepictions: List): - Observable>? { - return Observable.fromIterable( - selectedDepictions.map { it.commonsCategories }.flatten()) - .map { categoryItem -> - categoryClient.getCategoriesByName(categoryItem.name, - categoryItem.name, SEARCH_CATS_LIMIT).map { - - CategoryItem(it[0].name, it[0].description, - it[0].thumbnail, it[0].isSelected) - }.blockingGet() - }.toList().toObservable() - } + /** + * Regional category search + * @param term + * @param imageTitleList + * @return + */ + fun searchAll( + term: String, + imageTitleList: List, + selectedDepictions: List, + ): Observable> = + suggestionsOrSearch(term, imageTitleList, selectedDepictions) + .map { it.map { CategoryItem(it.name, it.description, it.thumbnail, false) } } - /** - * Fetches details of every category by their name, converts them into - * CategoryItem and returns them in a list. - * - * @param categoryNames selected Categories - * @return List of CategoryItem - */ - fun getCategoriesByName(categoryNames: List): - Observable>? { - return Observable.fromIterable(categoryNames) - .map { categoryName -> - buildCategories(categoryName) - } - .filter { categoryItem -> - categoryItem.name != "Hidden" - } - .toList().toObservable() - } - - /** - * Fetches the categories and converts them into CategoryItem - */ - fun buildCategories(categoryName: String): CategoryItem { - return categoryClient.getCategoriesByName(categoryName, - categoryName, SEARCH_CATS_LIMIT).map { - if(it.isNotEmpty()) { - CategoryItem( - it[0].name, it[0].description, - it[0].thumbnail, it[0].isSelected + private fun suggestionsOrSearch( + term: String, + imageTitleList: List, + selectedDepictions: List, + ): Observable> = + if (TextUtils.isEmpty(term)) { + Observable.combineLatest( + categoriesFromDepiction(selectedDepictions), + gpsCategoryModel.categoriesFromLocation, + titleCategories(imageTitleList), + Observable.just(categoryDao.recentCategories(SEARCH_CATS_LIMIT)), + Function4(::combine), ) } else { - CategoryItem( - "Hidden", "Hidden", - "hidden", false - ) + categoryClient + .searchCategoriesForPrefix(term, SEARCH_CATS_LIMIT) + .map { it.sortedWith(StringSortingUtils.sortBySimilarity(term)) } + .toObservable() } - }.blockingGet() - } - - private fun combine( - depictionCategories: List, - locationCategories: List, - titles: List, - recents: List - ) = depictionCategories + locationCategories + titles + recents + /** + * Fetches details of every category associated with selected depictions, converts them into + * CategoryItem and returns them in a list. + * + * @param selectedDepictions selected DepictItems + * @return List of CategoryItem associated with selected depictions + */ + private fun categoriesFromDepiction(selectedDepictions: List): Observable>? = + Observable + .fromIterable( + selectedDepictions.map { it.commonsCategories }.flatten(), + ).map { categoryItem -> + categoryClient + .getCategoriesByName( + categoryItem.name, + categoryItem.name, + SEARCH_CATS_LIMIT, + ).map { + CategoryItem( + it[0].name, + it[0].description, + it[0].thumbnail, + it[0].isSelected, + ) + }.blockingGet() + }.toList() + .toObservable() - /** - * Returns title based categories - * @param titleList - * @return - */ - private fun titleCategories(titleList: List) = - if (titleList.isNotEmpty()) - Observable.combineLatest(titleList.map { getTitleCategories(it) }) { searchResults -> - searchResults.map { it as List }.flatten() - } - else - Observable.just(emptyList()) + /** + * Fetches details of every category by their name, converts them into + * CategoryItem and returns them in a list. + * + * @param categoryNames selected Categories + * @return List of CategoryItem + */ + fun getCategoriesByName(categoryNames: List): Observable>? = + Observable + .fromIterable(categoryNames) + .map { categoryName -> + buildCategories(categoryName) + }.filter { categoryItem -> + categoryItem.name != "Hidden" + }.toList() + .toObservable() - /** - * Return category for single title - * @param title - * @return - */ - private fun getTitleCategories(title: String): Observable> { - return categoryClient.searchCategories(title, SEARCH_CATS_LIMIT).toObservable() - } + /** + * Fetches the categories and converts them into CategoryItem + */ + fun buildCategories(categoryName: String): CategoryItem = + categoryClient + .getCategoriesByName( + categoryName, + categoryName, + SEARCH_CATS_LIMIT, + ).map { + if (it.isNotEmpty()) { + CategoryItem( + it[0].name, + it[0].description, + it[0].thumbnail, + it[0].isSelected, + ) + } else { + CategoryItem( + "Hidden", + "Hidden", + "hidden", + false, + ) + } + }.blockingGet() + private fun combine( + depictionCategories: List, + locationCategories: List, + titles: List, + recents: List, + ) = depictionCategories + locationCategories + titles + recents - /** - * Handles category item selection - * @param item - */ - fun onCategoryItemClicked(item: CategoryItem, media: Media?) { - if (media == null) { - if (item.isSelected) { - selectedCategories.add(item) - updateCategoryCount(item) + /** + * Returns title based categories + * @param titleList + * @return + */ + private fun titleCategories(titleList: List) = + if (titleList.isNotEmpty()) { + Observable.combineLatest(titleList.map { getTitleCategories(it) }) { searchResults -> + searchResults.map { it as List }.flatten() + } } else { - selectedCategories.remove(item) + Observable.just(emptyList()) } - } else { - if (item.isSelected) { - if (media.categories?.contains(item.name) == true) { - selectedExistingCategories.add(item.name) - } else { + + /** + * Return category for single title + * @param title + * @return + */ + private fun getTitleCategories(title: String): Observable> = + categoryClient.searchCategories(title, SEARCH_CATS_LIMIT).toObservable() + + /** + * Handles category item selection + * @param item + */ + fun onCategoryItemClicked( + item: CategoryItem, + media: Media?, + ) { + if (media == null) { + if (item.isSelected) { selectedCategories.add(item) updateCategoryCount(item) + } else { + selectedCategories.remove(item) } } else { - if (media.categories?.contains(item.name) == true) { - selectedExistingCategories.remove(item.name) - if (!media.categories?.contains(item.name)!!) { - val categoriesList: MutableList = ArrayList() - categoriesList.add(item.name) - categoriesList.addAll(media.categories!!) - media.categories = categoriesList + if (item.isSelected) { + if (media.categories?.contains(item.name) == true) { + selectedExistingCategories.add(item.name) + } else { + selectedCategories.add(item) + updateCategoryCount(item) } } else { - selectedCategories.remove(item) + if (media.categories?.contains(item.name) == true) { + selectedExistingCategories.remove(item.name) + if (!media.categories?.contains(item.name)!!) { + val categoriesList: MutableList = ArrayList() + categoriesList.add(item.name) + categoriesList.addAll(media.categories!!) + media.categories = categoriesList + } + } else { + selectedCategories.remove(item) + } } } } - } - /** - * Get Selected Categories - * @return - */ - fun getSelectedCategories(): List { - return selectedCategories - } + /** + * Get Selected Categories + * @return + */ + fun getSelectedCategories(): List = selectedCategories - /** - * Cleanup the existing in memory cache's - */ - fun cleanUp() { - selectedCategories.clear() - selectedExistingCategories.clear() - } + /** + * Cleanup the existing in memory cache's + */ + fun cleanUp() { + selectedCategories.clear() + selectedExistingCategories.clear() + } - companion object { - const val SEARCH_CATS_LIMIT = 25 - } + companion object { + const val SEARCH_CATS_LIMIT = 25 + } - /** - * Provides selected existing categories - * - * @return selected existing categories - */ - fun getSelectedExistingCategories(): List { - return selectedExistingCategories - } + /** + * Provides selected existing categories + * + * @return selected existing categories + */ + fun getSelectedExistingCategories(): List = selectedExistingCategories - /** - * Initialize existing categories - * - * @param selectedExistingCategories existing categories - */ - fun setSelectedExistingCategories(selectedExistingCategories: MutableList) { - this.selectedExistingCategories = selectedExistingCategories + /** + * Initialize existing categories + * + * @param selectedExistingCategories existing categories + */ + fun setSelectedExistingCategories(selectedExistingCategories: MutableList) { + this.selectedExistingCategories = selectedExistingCategories + } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryClient.kt b/app/src/main/java/fr/free/nrw/commons/category/CategoryClient.kt index 49796b03a7..64463d8263 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryClient.kt @@ -1,7 +1,7 @@ package fr.free.nrw.commons.category -import io.reactivex.Single import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse +import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -15,109 +15,123 @@ const val CATEGORY_NEEDING_CATEGORIES = "needing categories" * Category Client to handle custom calls to Commons MediaWiki APIs */ @Singleton -class CategoryClient @Inject constructor(private val categoryInterface: CategoryInterface) : - ContinuationClient() { +class CategoryClient + @Inject + constructor( + private val categoryInterface: CategoryInterface, + ) : ContinuationClient() { + /** + * Searches for categories containing the specified string. + * + * @param filter The string to be searched + * @param itemLimit How many results are returned + * @param offset Starts returning items from the nth result. If offset is 9, the response starts with the 9th item of the search result + * @return + */ + @JvmOverloads + fun searchCategories( + filter: String?, + itemLimit: Int, + offset: Int = 0, + ): Single> = responseMapper(categoryInterface.searchCategories(filter, itemLimit, offset)) - /** - * Searches for categories containing the specified string. - * - * @param filter The string to be searched - * @param itemLimit How many results are returned - * @param offset Starts returning items from the nth result. If offset is 9, the response starts with the 9th item of the search result - * @return - */ - @JvmOverloads - fun searchCategories(filter: String?, itemLimit: Int, offset: Int = 0): - Single> { - return responseMapper(categoryInterface.searchCategories(filter, itemLimit, offset)) - } + /** + * Searches for categories starting with the specified string. + * + * @param prefix The prefix to be searched + * @param itemLimit How many results are returned + * @param offset Starts returning items from the nth result. If offset is 9, the response starts with the 9th item of the search result + * @return + */ + @JvmOverloads + fun searchCategoriesForPrefix( + prefix: String?, + itemLimit: Int, + offset: Int = 0, + ): Single> = + responseMapper( + categoryInterface.searchCategoriesForPrefix(prefix, itemLimit, offset), + ) - /** - * Searches for categories starting with the specified string. - * - * @param prefix The prefix to be searched - * @param itemLimit How many results are returned - * @param offset Starts returning items from the nth result. If offset is 9, the response starts with the 9th item of the search result - * @return - */ - @JvmOverloads - fun searchCategoriesForPrefix(prefix: String?, itemLimit: Int, offset: Int = 0): - Single> { - return responseMapper( - categoryInterface.searchCategoriesForPrefix(prefix, itemLimit, offset) - ) - } + /** + * Fetches categories starting and ending with a specified name. + * + * @param startingCategoryName Name of the category to start + * @param endingCategoryName Name of the category to end + * @param itemLimit How many categories to return + * @param offset offset + * @return MwQueryResponse + */ + @JvmOverloads + fun getCategoriesByName( + startingCategoryName: String?, + endingCategoryName: String?, + itemLimit: Int, + offset: Int = 0, + ): Single> = + responseMapper( + categoryInterface.getCategoriesByName( + startingCategoryName, + endingCategoryName, + itemLimit, + offset, + ), + ) - /** - * Fetches categories starting and ending with a specified name. - * - * @param startingCategoryName Name of the category to start - * @param endingCategoryName Name of the category to end - * @param itemLimit How many categories to return - * @param offset offset - * @return MwQueryResponse - */ - @JvmOverloads - fun getCategoriesByName(startingCategoryName: String?, endingCategoryName: String?, - itemLimit: Int, offset: Int = 0): Single> { - return responseMapper( - categoryInterface.getCategoriesByName(startingCategoryName, endingCategoryName, - itemLimit, offset) - ) - } + /** + * The method takes categoryName as input and returns a List of Subcategories + * It uses the generator query API to get the subcategories in a category, 500 at a time. + * + * @param categoryName Category name as defined on commons + * @return Observable emitting the categories returned. If our search yielded "Category:Test", "Test" is emitted. + */ + fun getSubCategoryList(categoryName: String): Single> = + continuationRequest(SUB_CATEGORY_CONTINUATION_PREFIX, categoryName) { + categoryInterface.getSubCategoryList( + categoryName, + it, + ) + } - /** - * The method takes categoryName as input and returns a List of Subcategories - * It uses the generator query API to get the subcategories in a category, 500 at a time. - * - * @param categoryName Category name as defined on commons - * @return Observable emitting the categories returned. If our search yielded "Category:Test", "Test" is emitted. - */ - fun getSubCategoryList(categoryName: String): Single> { - return continuationRequest(SUB_CATEGORY_CONTINUATION_PREFIX, categoryName) { - categoryInterface.getSubCategoryList( - categoryName, it - ) - } - } + /** + * The method takes categoryName as input and returns a List of parent categories + * It uses the generator query API to get the parent categories of a category, 500 at a time. + * + * @param categoryName Category name as defined on commons + * @return + */ + fun getParentCategoryList(categoryName: String): Single> = + continuationRequest(PARENT_CATEGORY_CONTINUATION_PREFIX, categoryName) { + categoryInterface.getParentCategoryList(categoryName, it) + } - /** - * The method takes categoryName as input and returns a List of parent categories - * It uses the generator query API to get the parent categories of a category, 500 at a time. - * - * @param categoryName Category name as defined on commons - * @return - */ - fun getParentCategoryList(categoryName: String): Single> { - return continuationRequest(PARENT_CATEGORY_CONTINUATION_PREFIX, categoryName) { - categoryInterface.getParentCategoryList(categoryName, it) + fun resetSubCategoryContinuation(category: String) { + resetContinuation(SUB_CATEGORY_CONTINUATION_PREFIX, category) } - } - - fun resetSubCategoryContinuation(category: String) { - resetContinuation(SUB_CATEGORY_CONTINUATION_PREFIX, category) - } - fun resetParentCategoryContinuation(category: String) { - resetContinuation(PARENT_CATEGORY_CONTINUATION_PREFIX, category) - } + fun resetParentCategoryContinuation(category: String) { + resetContinuation(PARENT_CATEGORY_CONTINUATION_PREFIX, category) + } - override fun responseMapper( - networkResult: Single, - key: String? - ): Single> { - return networkResult - .map { - handleContinuationResponse(it.continuation(), key) - it.query()?.pages() ?: emptyList() - } - .map { - it.filter { - page -> page.categoryInfo() == null || !page.categoryInfo().isHidden + override fun responseMapper( + networkResult: Single, + key: String?, + ): Single> = + networkResult + .map { + handleContinuationResponse(it.continuation(), key) + it.query()?.pages() ?: emptyList() }.map { - CategoryItem(it.title().replace(CATEGORY_PREFIX, ""), - it.description().toString(), it.thumbUrl().toString(), false) + it + .filter { page -> + page.categoryInfo() == null || !page.categoryInfo().isHidden + }.map { + CategoryItem( + it.title().replace(CATEGORY_PREFIX, ""), + it.description().toString(), + it.thumbUrl().toString(), + false, + ) + } } - } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryInterface.kt b/app/src/main/java/fr/free/nrw/commons/category/CategoryInterface.kt index c3c8ae4c7d..aa5ac8c3ad 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryInterface.kt @@ -17,11 +17,13 @@ interface CategoryInterface { * @param itemLimit How many results are returned * @return */ - @GET("w/api.php?action=query&format=json&formatversion=2&generator=search&prop=description|pageimages&piprop=thumbnail&pithumbsize=70&gsrnamespace=14") + @GET( + "w/api.php?action=query&format=json&formatversion=2&generator=search&prop=description|pageimages&piprop=thumbnail&pithumbsize=70&gsrnamespace=14", + ) fun searchCategories( @Query("gsrsearch") filter: String?, @Query("gsrlimit") itemLimit: Int, - @Query("gsroffset") offset: Int + @Query("gsroffset") offset: Int, ): Single /** @@ -31,11 +33,13 @@ interface CategoryInterface { * @param itemLimit How many results are returned * @return */ - @GET("w/api.php?action=query&format=json&formatversion=2&generator=allcategories&prop=categoryinfo|description|pageimages&piprop=thumbnail&pithumbsize=70") + @GET( + "w/api.php?action=query&format=json&formatversion=2&generator=allcategories&prop=categoryinfo|description|pageimages&piprop=thumbnail&pithumbsize=70", + ) fun searchCategoriesForPrefix( @Query("gacprefix") prefix: String?, @Query("gaclimit") itemLimit: Int, - @Query("gacoffset") offset: Int + @Query("gacoffset") offset: Int, ): Single /** @@ -47,23 +51,25 @@ interface CategoryInterface { * @param offset offset * @return MwQueryResponse */ - @GET("w/api.php?action=query&format=json&formatversion=2&generator=allcategories&prop=categoryinfo|description|pageimages&piprop=thumbnail&pithumbsize=70") + @GET( + "w/api.php?action=query&format=json&formatversion=2&generator=allcategories&prop=categoryinfo|description|pageimages&piprop=thumbnail&pithumbsize=70", + ) fun getCategoriesByName( @Query("gacfrom") startingCategory: String?, @Query("gacto") endingCategory: String?, @Query("gaclimit") itemLimit: Int, - @Query("gacoffset") offset: Int + @Query("gacoffset") offset: Int, ): Single @GET("w/api.php?action=query&format=json&formatversion=2&generator=categorymembers&gcmtype=subcat&prop=info&gcmlimit=50") fun getSubCategoryList( @Query("gcmtitle") categoryName: String, - @QueryMap(encoded = true) continuation: Map + @QueryMap(encoded = true) continuation: Map, ): Single @GET("w/api.php?action=query&format=json&formatversion=2&generator=categories&prop=info&gcllimit=50") fun getParentCategoryList( @Query("titles") categoryName: String?, - @QueryMap(encoded = true) continuation: Map + @QueryMap(encoded = true) continuation: Map, ): Single } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryItem.kt b/app/src/main/java/fr/free/nrw/commons/category/CategoryItem.kt index 027c9b8044..d0ee8d53cd 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryItem.kt +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryItem.kt @@ -4,12 +4,13 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class CategoryItem(val name: String, val description: String?, - val thumbnail: String?, var isSelected: Boolean) : Parcelable { - - override fun toString(): String { - return "CategoryItem: '$name'" - } +data class CategoryItem( + val name: String, + val description: String?, + val thumbnail: String?, + var isSelected: Boolean, +) : Parcelable { + override fun toString(): String = "CategoryItem: '$name'" override fun equals(other: Any?): Boolean { if (this === other) return true @@ -22,7 +23,5 @@ data class CategoryItem(val name: String, val description: String?, return true } - override fun hashCode(): Int { - return name.hashCode() - } + override fun hashCode(): Int = name.hashCode() } diff --git a/app/src/main/java/fr/free/nrw/commons/category/ContinuationClient.kt b/app/src/main/java/fr/free/nrw/commons/category/ContinuationClient.kt index a85eee79d0..0322cd7b6f 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/ContinuationClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/category/ContinuationClient.kt @@ -2,16 +2,16 @@ package fr.free.nrw.commons.category import io.reactivex.Single - abstract class ContinuationClient { private val continuationStore: MutableMap?> = mutableMapOf() private val continuationExists: MutableMap = mutableMapOf() private fun hasMorePagesFor(key: String) = continuationExists[key] ?: true + fun continuationRequest( prefix: String, name: String, - requestFunction: (Map) -> Single + requestFunction: (Map) -> Single, ): Single> { val key = "$prefix$name" return if (hasMorePagesFor(key)) { @@ -21,9 +21,15 @@ abstract class ContinuationClient { } } - abstract fun responseMapper(networkResult: Single, key: String?=null): Single> + abstract fun responseMapper( + networkResult: Single, + key: String? = null, + ): Single> - fun handleContinuationResponse(continuation:Map?, key:String?){ + fun handleContinuationResponse( + continuation: Map?, + key: String?, + ) { if (key != null) { continuationExists[key] = continuation?.let { continuation -> @@ -33,7 +39,10 @@ abstract class ContinuationClient { } } - protected fun resetContinuation(prefix: String, category: String) { + protected fun resetContinuation( + prefix: String, + category: String, + ) { continuationExists.remove("$prefix$category") continuationStore.remove("$prefix$category") } @@ -44,9 +53,11 @@ abstract class ContinuationClient { * @param prefix * @param userName the username */ - protected fun resetUserContinuation(prefix: String, userName: String) { + protected fun resetUserContinuation( + prefix: String, + userName: String, + ) { continuationExists.remove("$prefix$userName") continuationStore.remove("$prefix$userName") } - } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ChunkInfo.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ChunkInfo.kt index 0ef4066a2d..b611574b0e 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ChunkInfo.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ChunkInfo.kt @@ -7,32 +7,29 @@ import fr.free.nrw.commons.upload.UploadResult data class ChunkInfo( val uploadResult: UploadResult?, val indexOfNextChunkToUpload: Int, - val totalChunks: Int + val totalChunks: Int, ) : Parcelable { constructor(parcel: Parcel) : this( parcel.readParcelable(UploadResult::class.java.classLoader), parcel.readInt(), - parcel.readInt() + parcel.readInt(), ) { } - override fun writeToParcel(parcel: Parcel, flags: Int) { + override fun writeToParcel( + parcel: Parcel, + flags: Int, + ) { parcel.writeParcelable(uploadResult, flags) parcel.writeInt(indexOfNextChunkToUpload) parcel.writeInt(totalChunks) } - override fun describeContents(): Int { - return 0 - } + override fun describeContents(): Int = 0 companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): ChunkInfo { - return ChunkInfo(parcel) - } + override fun createFromParcel(parcel: Parcel): ChunkInfo = ChunkInfo(parcel) - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } + override fun newArray(size: Int): Array = arrayOfNulls(size) } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt index 81721e9b27..0a757619f0 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt @@ -5,7 +5,6 @@ import android.os.Parcelable import androidx.room.Embedded import androidx.room.Entity import androidx.room.PrimaryKey -import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.Media import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.upload.UploadItem @@ -31,8 +30,7 @@ data class Contribution constructor( var errorInfo: String? = null, /** * @return array list of entityids for the depictions - */ - /** + * * Each depiction loaded in depictions activity is associated with a wikidata entity id, this Id * is in turn used to upload depictions to wikibase */ @@ -44,26 +42,23 @@ data class Contribution constructor( var dateCreatedString: String? = null, var dateModified: Date? = null, var dateUploadStarted: Date? = null, - var hasInvalidLocation : Int = 0, + var hasInvalidLocation: Int = 0, var contentUri: Uri? = null, - var countryCode : String? = null, - var imageSHA1 : String? = null, + var countryCode: String? = null, + var imageSHA1: String? = null, /** * Number of times a contribution has been retried after a failure */ - var retries: Int = 0 + var retries: Int = 0, ) : Parcelable { - - fun completeWith(media: Media): Contribution { - return copy(pageId = media.pageId, media = media, state = STATE_COMPLETED) - } + fun completeWith(media: Media): Contribution = copy(pageId = media.pageId, media = media, state = STATE_COMPLETED) constructor( item: UploadItem, sessionManager: SessionManager, depictedItems: List, categories: List, - imageSHA1: String + imageSHA1: String, ) : this( Media( formatCaptions(item.uploadMediaDetails), @@ -71,7 +66,7 @@ data class Contribution constructor( item.fileName, formatDescriptions(item.uploadMediaDetails), sessionManager.userName, - sessionManager.userName + sessionManager.userName, ), localUri = item.mediaUri, decimalCoords = item.gpsCoords.decimalCoords, @@ -80,7 +75,7 @@ data class Contribution constructor( wikidataPlace = from(item.place), contentUri = item.contentUri, dateCreatedString = item.fileCreatedDateString, - imageSHA1 = imageSHA1 + imageSHA1 = imageSHA1, ) /** @@ -91,9 +86,7 @@ data class Contribution constructor( this.hasInvalidLocation = if (hasInvalidLocation) 1 else 0 } - fun hasInvalidLocation(): Boolean { - return hasInvalidLocation == 1 - } + fun hasInvalidLocation(): Boolean = hasInvalidLocation == 1 companion object { const val STATE_COMPLETED = -1 @@ -107,7 +100,8 @@ data class Contribution constructor( * @param uploadMediaDetails list of media Details */ fun formatCaptions(uploadMediaDetails: List) = - uploadMediaDetails.associate { it.languageCode!! to it.captionText } + uploadMediaDetails + .associate { it.languageCode!! to it.captionText } .filter { it.value.isNotBlank() } /** @@ -117,19 +111,15 @@ data class Contribution constructor( * @return a string with the pattern of {{en|1=descriptionText}} */ fun formatDescriptions(descriptions: List) = - descriptions.filter { it.descriptionText.isNotEmpty() } + descriptions + .filter { it.descriptionText.isNotEmpty() } .joinToString(separator = "") { "{{${it.languageCode}|1=${it.descriptionText}}}" } } - val fileKey : String? get() = chunkInfo?.uploadResult?.filekey + val fileKey: String? get() = chunkInfo?.uploadResult?.filekey val localUriPath: File? get() = localUri?.path?.let { File(it) } - fun isCompleted(): Boolean { - return chunkInfo != null && chunkInfo!!.totalChunks == chunkInfo!!.indexOfNextChunkToUpload - } - - fun dateUploadStartedInMillis(): Long { - return dateUploadStarted!!.time - } + fun isCompleted(): Boolean = chunkInfo != null && chunkInfo!!.totalChunks == chunkInfo!!.indexOfNextChunkToUpload + fun dateUploadStartedInMillis(): Long = dateUploadStarted!!.time } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt index b75332b73a..286abb97aa 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt @@ -14,88 +14,90 @@ import javax.inject.Named * Class that extends PagedList.BoundaryCallback for contributions list It defines the action that * is triggered for various boundary conditions in the list */ -class ContributionBoundaryCallback @Inject constructor( - private val repository: ContributionsRepository, - private val sessionManager: SessionManager, - private val mediaClient: MediaClient, - @param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler -) : BoundaryCallback() { - private val compositeDisposable: CompositeDisposable = CompositeDisposable() - var userName: String? = null +class ContributionBoundaryCallback + @Inject + constructor( + private val repository: ContributionsRepository, + private val sessionManager: SessionManager, + private val mediaClient: MediaClient, + @param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler, + ) : BoundaryCallback() { + private val compositeDisposable: CompositeDisposable = CompositeDisposable() + var userName: String? = null - - /** - * It is triggered when the list has no items User's Contributions are then fetched from the - * network - */ - override fun onZeroItemsLoaded() { - if (sessionManager.userName != null) { - mediaClient.resetUserNameContinuation(sessionManager.userName!!) + /** + * It is triggered when the list has no items User's Contributions are then fetched from the + * network + */ + override fun onZeroItemsLoaded() { + if (sessionManager.userName != null) { + mediaClient.resetUserNameContinuation(sessionManager.userName!!) + } + fetchContributions() } - fetchContributions() - } - - /** - * It is triggered when the user scrolls to the top of the list - * */ - override fun onItemAtFrontLoaded(itemAtFront: Contribution) { - } + /** + * It is triggered when the user scrolls to the top of the list + * */ + override fun onItemAtFrontLoaded(itemAtFront: Contribution) { + } - /** - * It is triggered when the user scrolls to the end of the list. User's Contributions are then - * fetched from the network - */ - override fun onItemAtEndLoaded(itemAtEnd: Contribution) { - fetchContributions() - } + /** + * It is triggered when the user scrolls to the end of the list. User's Contributions are then + * fetched from the network + */ + override fun onItemAtEndLoaded(itemAtEnd: Contribution) { + fetchContributions() + } - /** - * Fetches contributions using the MediaWiki API - */ - private fun fetchContributions() { - if (sessionManager.userName != null) { - userName?.let { userName -> - mediaClient.getMediaListForUser(userName) - .map { mediaList -> - mediaList.map { media -> - Contribution(media = media, state = Contribution.STATE_COMPLETED) - } - } - .subscribeOn(ioThreadScheduler) - .subscribe(::saveContributionsToDB) { error: Throwable -> - Timber.e( - "Failed to fetch contributions: %s", - error.message + /** + * Fetches contributions using the MediaWiki API + */ + private fun fetchContributions() { + if (sessionManager.userName != null) { + userName + ?.let { userName -> + mediaClient + .getMediaListForUser(userName) + .map { mediaList -> + mediaList.map { media -> + Contribution(media = media, state = Contribution.STATE_COMPLETED) + } + }.subscribeOn(ioThreadScheduler) + .subscribe(::saveContributionsToDB) { error: Throwable -> + Timber.e( + "Failed to fetch contributions: %s", + error.message, + ) + } + }?.let { + compositeDisposable.add( + it, ) } - }?.let { - compositeDisposable.add( - it - ) + } else { + compositeDisposable.clear() } - }else { - compositeDisposable.clear() } - } - /** - * Saves the contributions the the local DB - */ - private fun saveContributionsToDB(contributions: List) { - compositeDisposable.add( - repository.save(contributions) - .subscribeOn(ioThreadScheduler) - .subscribe { longs: List? -> - repository["last_fetch_timestamp"] = System.currentTimeMillis() - } - ) - } + /** + * Saves the contributions the the local DB + */ + private fun saveContributionsToDB(contributions: List) { + compositeDisposable.add( + repository + .save(contributions) + .subscribeOn(ioThreadScheduler) + .subscribe { longs: List? -> + repository["last_fetch_timestamp"] = System.currentTimeMillis() + }, + ) + } - /** - * Clean up - */ - fun dispose() { - compositeDisposable.dispose() + /** + * Clean up + */ + fun dispose() { + compositeDisposable.dispose() + } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRemoteDataSource.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRemoteDataSource.kt index 777bb8a21e..346c83b344 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRemoteDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRemoteDataSource.kt @@ -12,62 +12,61 @@ import javax.inject.Named /** * Data-Source which acts as mediator for contributions-data from the API */ -class ContributionsRemoteDataSource @Inject constructor( - private val mediaClient: MediaClient, - @param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler -) : ItemKeyedDataSource() { - private val compositeDisposable: CompositeDisposable = CompositeDisposable() - var userName: String? = null +class ContributionsRemoteDataSource + @Inject + constructor( + private val mediaClient: MediaClient, + @param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler, + ) : ItemKeyedDataSource() { + private val compositeDisposable: CompositeDisposable = CompositeDisposable() + var userName: String? = null - override fun loadInitial( - params: LoadInitialParams, - callback: LoadInitialCallback - ) { - fetchContributions(callback) - } - - override fun loadAfter( - params: LoadParams, - callback: LoadCallback - ) { - fetchContributions(callback) - } + override fun loadInitial( + params: LoadInitialParams, + callback: LoadInitialCallback, + ) { + fetchContributions(callback) + } - override fun loadBefore( - params: LoadParams, - callback: LoadCallback - ) { - } + override fun loadAfter( + params: LoadParams, + callback: LoadCallback, + ) { + fetchContributions(callback) + } - override fun getKey(item: Contribution): Int { - return item.pageId.hashCode() - } + override fun loadBefore( + params: LoadParams, + callback: LoadCallback, + ) { + } + override fun getKey(item: Contribution): Int = item.pageId.hashCode() - /** - * Fetches contributions using the MediaWiki API - */ - private fun fetchContributions(callback: LoadCallback) { - compositeDisposable.add( - mediaClient.getMediaListForUser(userName!!) - .map { mediaList -> - mediaList.map { - Contribution(media = it, state = Contribution.STATE_COMPLETED) - } - } - .subscribeOn(ioThreadScheduler) - .subscribe({ - callback.onResult(it) - }) { error: Throwable -> - Timber.e( - "Failed to fetch contributions: %s", - error.message - ) - } - ) - } + /** + * Fetches contributions using the MediaWiki API + */ + private fun fetchContributions(callback: LoadCallback) { + compositeDisposable.add( + mediaClient + .getMediaListForUser(userName!!) + .map { mediaList -> + mediaList.map { + Contribution(media = it, state = Contribution.STATE_COMPLETED) + } + }.subscribeOn(ioThreadScheduler) + .subscribe({ + callback.onResult(it) + }) { error: Throwable -> + Timber.e( + "Failed to fetch contributions: %s", + error.message, + ) + }, + ) + } - fun dispose() { - compositeDisposable.dispose() + fun dispose() { + compositeDisposable.dispose() + } } -} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt b/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt index e0f7e7c42e..77e52e1dbe 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt @@ -13,26 +13,30 @@ import fr.free.nrw.commons.databinding.DialogAddToWikipediaInstructionsBinding * Dialog fragment for displaying instructions for editing wikipedia */ class WikipediaInstructionsDialogFragment : DialogFragment() { - var callback: Callback? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ) = DialogAddToWikipediaInstructionsBinding.inflate(inflater, container, false).apply { - val contribution: Contribution? = arguments!!.getParcelable(ARG_CONTRIBUTION) - tvWikicode.setText(contribution?.media?.wikiCode) - instructionsCancel.setOnClickListener { dismiss() } - instructionsConfirm.setOnClickListener { - callback?.onConfirmClicked(contribution, checkboxCopyWikicode.isChecked) - } - }.root - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + savedInstanceState: Bundle?, + ) = DialogAddToWikipediaInstructionsBinding + .inflate(inflater, container, false) + .apply { + val contribution: Contribution? = arguments!!.getParcelable(ARG_CONTRIBUTION) + tvWikicode.setText(contribution?.media?.wikiCode) + instructionsCancel.setOnClickListener { dismiss() } + instructionsConfirm.setOnClickListener { + callback?.onConfirmClicked(contribution, checkboxCopyWikicode.isChecked) + } + }.root + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) dialog!!.window?.setSoftInputMode( - WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN + WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN, ) } @@ -40,15 +44,19 @@ class WikipediaInstructionsDialogFragment : DialogFragment() { * Callback for handling confirm button clicked */ interface Callback { - fun onConfirmClicked(contribution: Contribution?, copyWikicode: Boolean) + fun onConfirmClicked( + contribution: Contribution?, + copyWikicode: Boolean, + ) } companion object { const val ARG_CONTRIBUTION = "contribution" @JvmStatic - fun newInstance(contribution: Contribution) = WikipediaInstructionsDialogFragment().apply { - arguments = bundleOf(ARG_CONTRIBUTION to contribution) - } + fun newInstance(contribution: Contribution) = + WikipediaInstructionsDialogFragment().apply { + arguments = bundleOf(ARG_CONTRIBUTION to contribution) + } } } diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatus.kt b/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatus.kt index 31d48af97b..b3ef36318d 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatus.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatus.kt @@ -1,16 +1,16 @@ package fr.free.nrw.commons.customselector.database -import androidx.room.* +import androidx.room.Entity +import androidx.room.PrimaryKey /** * Entity class for Not For Upload status. */ @Entity(tableName = "images_not_for_upload_table") data class NotForUploadStatus( - /** * Original image sha1. */ @PrimaryKey - val imageSHA1 : String + val imageSHA1: String, ) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadDao.kt b/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatusDao.kt similarity index 70% rename from app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadDao.kt rename to app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatusDao.kt index d6c9235cef..b75a6e1d4f 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatusDao.kt @@ -1,18 +1,20 @@ package fr.free.nrw.commons.customselector.database -import androidx.room.* - +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query /** * Dao class for Not For Upload */ @Dao abstract class NotForUploadStatusDao { - /** * Insert into Not For Upload status. */ - @Insert( onConflict = OnConflictStrategy.REPLACE ) + @Insert(onConflict = OnConflictStrategy.REPLACE) abstract suspend fun insert(notForUploadStatus: NotForUploadStatus) /** @@ -25,33 +27,27 @@ abstract class NotForUploadStatusDao { * Query Not For Upload status with image sha1. */ @Query("SELECT * FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ") - abstract suspend fun getFromImageSHA1(imageSHA1 : String) : NotForUploadStatus? + abstract suspend fun getFromImageSHA1(imageSHA1: String): NotForUploadStatus? /** * Asynchronous image sha1 query. */ - suspend fun getNotForUploadFromImageSHA1(imageSHA1: String):NotForUploadStatus? { - return getFromImageSHA1(imageSHA1) - } + suspend fun getNotForUploadFromImageSHA1(imageSHA1: String): NotForUploadStatus? = getFromImageSHA1(imageSHA1) /** * Deletion Not For Upload status with image sha1. */ @Query("DELETE FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ") - abstract suspend fun deleteWithImageSHA1(imageSHA1 : String) + abstract suspend fun deleteWithImageSHA1(imageSHA1: String) /** * Asynchronous image sha1 deletion. */ - suspend fun deleteNotForUploadWithImageSHA1(imageSHA1: String) { - return deleteWithImageSHA1(imageSHA1) - } + suspend fun deleteNotForUploadWithImageSHA1(imageSHA1: String) = deleteWithImageSHA1(imageSHA1) /** * Check whether the imageSHA1 is present in database */ @Query("SELECT COUNT() FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ") - abstract suspend fun find(imageSHA1 : String): Int + abstract suspend fun find(imageSHA1: String): Int } - - diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatus.kt b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatus.kt index 93e4a82437..7f635ed954 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatus.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatus.kt @@ -3,37 +3,32 @@ package fr.free.nrw.commons.customselector.database import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey -import java.util.* +import java.util.Date /** * Entity class for Uploaded Status. */ @Entity(tableName = "uploaded_table", indices = [Index(value = ["modifiedImageSHA1"], unique = true)]) data class UploadedStatus( - /** * Original image sha1. */ @PrimaryKey - val imageSHA1 : String, - + val imageSHA1: String, /** * Modified image sha1 (after exif changes). */ - val modifiedImageSHA1 : String, - + val modifiedImageSHA1: String, /** * imageSHA1 query result from API. */ - var imageResult : Boolean, - + var imageResult: Boolean, /** * modifiedImageSHA1 query result from API. */ - var modifiedImageResult : Boolean, - + var modifiedImageResult: Boolean, /** * lastUpdated for data validation. */ - var lastUpdated : Date? = null + var lastUpdated: Date? = null, ) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedDao.kt b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatusDao.kt similarity index 62% rename from app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedDao.kt rename to app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatusDao.kt index 70454cc9e8..378af5b8db 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatusDao.kt @@ -1,18 +1,22 @@ package fr.free.nrw.commons.customselector.database -import androidx.room.* -import java.util.* +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 java.util.Calendar /** * UploadedStatusDao for Custom Selector. */ @Dao abstract class UploadedStatusDao { - /** * Insert into uploaded status. */ - @Insert( onConflict = OnConflictStrategy.REPLACE ) + @Insert(onConflict = OnConflictStrategy.REPLACE) abstract suspend fun insert(uploadedStatus: UploadedStatus) /** @@ -31,13 +35,13 @@ abstract class UploadedStatusDao { * Query uploaded status with image sha1. */ @Query("SELECT * FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) ") - abstract suspend fun getFromImageSHA1(imageSHA1 : String) : UploadedStatus? + abstract suspend fun getFromImageSHA1(imageSHA1: String): UploadedStatus? /** * Query uploaded status with modified image sha1. */ @Query("SELECT * FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) ") - abstract suspend fun getFromModifiedImageSHA1(modifiedImageSHA1 : String) : UploadedStatus? + abstract suspend fun getFromModifiedImageSHA1(modifiedImageSHA1: String): UploadedStatus? /** * Asynchronous insert into uploaded status table. @@ -51,20 +55,24 @@ abstract class UploadedStatusDao { * Check whether the imageSHA1 is present in database */ @Query("SELECT COUNT() FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) AND imageResult = (:imageResult) ") - abstract suspend fun findByImageSHA1(imageSHA1 : String, imageResult: Boolean): Int + abstract suspend fun findByImageSHA1( + imageSHA1: String, + imageResult: Boolean, + ): Int /** * Check whether the modifiedImageSHA1 is present in database */ - @Query("SELECT COUNT() FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) AND modifiedImageResult = (:modifiedImageResult) ") - abstract suspend fun findByModifiedImageSHA1(modifiedImageSHA1 : String, - modifiedImageResult: Boolean): Int + @Query( + "SELECT COUNT() FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) AND modifiedImageResult = (:modifiedImageResult) ", + ) + abstract suspend fun findByModifiedImageSHA1( + modifiedImageSHA1: String, + modifiedImageResult: Boolean, + ): Int /** * Asynchronous image sha1 query. */ - suspend fun getUploadedFromImageSHA1(imageSHA1: String):UploadedStatus? { - return getFromImageSHA1(imageSHA1) - } - -} \ No newline at end of file + suspend fun getUploadedFromImageSHA1(imageSHA1: String): UploadedStatus? = getFromImageSHA1(imageSHA1) +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/helper/CustomSelectorConstants.kt b/app/src/main/java/fr/free/nrw/commons/customselector/helper/CustomSelectorConstants.kt index f28a1c6137..e03b4da3c9 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/helper/CustomSelectorConstants.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/helper/CustomSelectorConstants.kt @@ -4,12 +4,10 @@ package fr.free.nrw.commons.customselector.helper * Stores constants related to custom image selector */ object CustomSelectorConstants { - const val BUCKET_ID = "bucket_id" const val TOTAL_SELECTED_IMAGES = "total_selected_images" const val PRESENT_POSITION = "present_position" const val NEW_SELECTED_IMAGES = "new_selected_images" const val SHOULD_REFRESH = "should_refresh" const val FULL_SCREEN_MODE_FIRST_LUNCH = "full_screen_mode_first_launch" - -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/helper/ImageHelper.kt b/app/src/main/java/fr/free/nrw/commons/customselector/helper/ImageHelper.kt index f59d8a844e..5df123ad2c 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/helper/ImageHelper.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/helper/ImageHelper.kt @@ -7,7 +7,6 @@ import fr.free.nrw.commons.customselector.model.Image * Image Helper object, includes all the static functions and variables required by custom selector. */ object ImageHelper { - /** * Custom selector preference key */ @@ -39,7 +38,10 @@ object ImageHelper { /** * Filters the images based on the given bucketId (folder) */ - fun filterImages(images: ArrayList, bukketId: Long?): ArrayList { + fun filterImages( + images: ArrayList, + bukketId: Long?, + ): ArrayList { if (bukketId == null) return images val filteredImages = arrayListOf() @@ -54,30 +56,37 @@ object ImageHelper { /** * getIndex: Returns the index of image in given list. */ - fun getIndex(list: ArrayList, image: Image): Int { - return list.indexOf(image) - } + fun getIndex( + list: ArrayList, + image: Image, + ): Int = list.indexOf(image) /** * getIndex: Returns the index of image in given list. */ - fun getIndexFromId(list: ArrayList, imageId: Long): Int { - for(i in list){ - if(i.id == imageId) + fun getIndexFromId( + list: ArrayList, + imageId: Long, + ): Int { + for (i in list) { + if (i.id == imageId) { return list.indexOf(i) + } } - return 0; + return 0 } /** * Gets the list of indices from the master list. */ - fun getIndexList(list: ArrayList, masterList: ArrayList): ArrayList { - - // Can be optimised as masterList is sorted by time. + fun getIndexList( + list: ArrayList, + masterList: ArrayList, + ): ArrayList { + // Can be optimised as masterList is sorted by time. val indexes = arrayListOf() - for(image in list) { + for (image in list) { val index = getIndex(masterList, image) if (index == -1) { continue @@ -86,4 +95,4 @@ object ImageHelper { } return indexes } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListener.kt b/app/src/main/java/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListener.kt index 961d511583..05159c56b8 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListener.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListener.kt @@ -2,23 +2,29 @@ package fr.free.nrw.commons.customselector.helper import android.content.Context import android.util.DisplayMetrics -import android.view.* +import android.view.Display +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.View +import android.view.WindowManager import kotlin.math.abs /** * Class for detecting swipe gestures */ -open class OnSwipeTouchListener(context: Context?) : View.OnTouchListener { - +open class OnSwipeTouchListener( + context: Context?, +) : View.OnTouchListener { private val gestureDetector: GestureDetector - private val SWIPE_THRESHOLD_HEIGHT = (getScreenResolution(context!!)).second / 3 - private val SWIPE_THRESHOLD_WIDTH = (getScreenResolution(context!!)).first / 3 - private val SWIPE_VELOCITY_THRESHOLD = 1000 + private val swipeThresholdHeight = (getScreenResolution(context!!)).second / 3 + private val swipeThresholdWidth = (getScreenResolution(context!!)).first / 3 + private val swipeVelocityThreshold = 1000 - override fun onTouch(view: View?, motionEvent: MotionEvent): Boolean { - return gestureDetector.onTouchEvent(motionEvent) - } + override fun onTouch( + view: View?, + motionEvent: MotionEvent, + ): Boolean = gestureDetector.onTouchEvent(motionEvent) fun getScreenResolution(context: Context): Pair { val wm: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager @@ -31,10 +37,7 @@ open class OnSwipeTouchListener(context: Context?) : View.OnTouchListener { } inner class GestureListener : GestureDetector.SimpleOnGestureListener() { - - override fun onDown(e: MotionEvent): Boolean { - return true - } + override fun onDown(e: MotionEvent): Boolean = true /** * Detects the gestures @@ -43,14 +46,16 @@ open class OnSwipeTouchListener(context: Context?) : View.OnTouchListener { event1: MotionEvent?, event2: MotionEvent, velocityX: Float, - velocityY: Float + velocityY: Float, ): Boolean { try { val diffY: Float = event2.y - (event1?.y ?: event2.y) val diffX: Float = event2.x - (event1?.x ?: event2.x) if (abs(diffX) > abs(diffY)) { - if (abs(diffX) > SWIPE_THRESHOLD_WIDTH && abs(velocityX) > - SWIPE_VELOCITY_THRESHOLD) { + if (abs(diffX) > swipeThresholdWidth && + abs(velocityX) > + swipeVelocityThreshold + ) { if (diffX > 0) { onSwipeRight() } else { @@ -58,8 +63,10 @@ open class OnSwipeTouchListener(context: Context?) : View.OnTouchListener { } } } else { - if (abs(diffY) > SWIPE_THRESHOLD_HEIGHT && abs(velocityY) > - SWIPE_VELOCITY_THRESHOLD) { + if (abs(diffY) > swipeThresholdHeight && + abs(velocityY) > + swipeVelocityThreshold + ) { if (diffY > 0) { onSwipeDown() } else { @@ -100,4 +107,4 @@ open class OnSwipeTouchListener(context: Context?) : View.OnTouchListener { init { gestureDetector = GestureDetector(context, GestureListener()) } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/listeners/FolderClickListener.kt b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/FolderClickListener.kt index bc3bd518dc..be808c3f07 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/listeners/FolderClickListener.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/FolderClickListener.kt @@ -4,12 +4,15 @@ package fr.free.nrw.commons.customselector.listeners * Custom Selector Folder Click Listener */ interface FolderClickListener { - /** * onFolderClick * @param folderId : folder id of the folder. * @param folderName : folder name of the folder. * @param lastItemId : last scroll position in the folder. */ - fun onFolderClick(folderId: Long, folderName: String, lastItemId: Long) -} \ No newline at end of file + fun onFolderClick( + folderId: Long, + folderName: String, + lastItemId: Long, + ) +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageLoaderListener.kt b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageLoaderListener.kt index 5ba43082d4..78ce46c6e4 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageLoaderListener.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageLoaderListener.kt @@ -7,7 +7,6 @@ import fr.free.nrw.commons.customselector.model.Image * responds to the device image query. */ interface ImageLoaderListener { - /** * On image loaded * @param images : queried device images. @@ -19,4 +18,4 @@ interface ImageLoaderListener { * @param throwable : throwable exception on failure. */ fun onFailed(throwable: Throwable) -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageSelectListener.kt b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageSelectListener.kt index d6349eb492..24565963b6 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageSelectListener.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageSelectListener.kt @@ -1,19 +1,20 @@ package fr.free.nrw.commons.customselector.listeners -import android.net.Uri import fr.free.nrw.commons.customselector.model.Image /** * Custom selector Image select listener */ interface ImageSelectListener { - /** * onSelectedImagesChanged * @param selectedImages : new selected images. * @param selectedNotForUploadImages : number of selected not for upload images */ - fun onSelectedImagesChanged(selectedImages: ArrayList, selectedNotForUploadImages: Int) + fun onSelectedImagesChanged( + selectedImages: ArrayList, + selectedNotForUploadImages: Int, + ) /** * onLongPress @@ -22,6 +23,6 @@ interface ImageSelectListener { fun onLongPress( position: Int, images: ArrayList, - selectedImages: ArrayList + selectedImages: ArrayList, ) -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/listeners/PassDataListener.kt b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/PassDataListener.kt index d2110d2085..da526be35e 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/listeners/PassDataListener.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/PassDataListener.kt @@ -6,5 +6,8 @@ import fr.free.nrw.commons.customselector.model.Image * Interface to pass data between fragment and activity */ interface PassDataListener { - fun passSelectedImages(selectedImages: ArrayList, shouldRefresh: Boolean) -} \ No newline at end of file + fun passSelectedImages( + selectedImages: ArrayList, + shouldRefresh: Boolean, + ) +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/listeners/RefreshUIListener.kt b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/RefreshUIListener.kt index d271c1d0b3..a6b4f3dce0 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/listeners/RefreshUIListener.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/RefreshUIListener.kt @@ -8,4 +8,4 @@ interface RefreshUIListener { * Refreshes the data in adapter */ fun refresh() -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/model/CallbackStatus.kt b/app/src/main/java/fr/free/nrw/commons/customselector/model/CallbackStatus.kt index 5cdcfb9bf7..c47806f16e 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/model/CallbackStatus.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/model/CallbackStatus.kt @@ -6,17 +6,17 @@ package fr.free.nrw.commons.customselector.model */ sealed class CallbackStatus { /** - IDLE : The callback is idle , doing nothing. + IDLE : The callback is idle , doing nothing. */ object IDLE : CallbackStatus() /** - FETCHING : Fetching images. + FETCHING : Fetching images. */ object FETCHING : CallbackStatus() /** - SUCCESS : Success fetching images. + SUCCESS : Success fetching images. */ object SUCCESS : CallbackStatus() -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/model/Folder.kt b/app/src/main/java/fr/free/nrw/commons/customselector/model/Folder.kt index 6857589bdf..ec08f6f73a 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/model/Folder.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/model/Folder.kt @@ -5,27 +5,22 @@ package fr.free.nrw.commons.customselector.model */ data class Folder( /** - bucketId : Unique directory id, eg 540528482 + bucketId : Unique directory id, eg 540528482 */ var bucketId: Long, - /** - name : bucket/folder name, eg Camera + name : bucket/folder name, eg Camera */ var name: String, - /** - images : folder images, list of all images under this folder. + images : folder images, list of all images under this folder. */ - var images: ArrayList = arrayListOf() - - + var images: ArrayList = arrayListOf(), ) { /** * Indicates whether some other object is "equal to" this one. */ override fun equals(other: Any?): Boolean { - if (javaClass != other?.javaClass) { return false } @@ -44,4 +39,4 @@ data class Folder( return true } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/model/Image.kt b/app/src/main/java/fr/free/nrw/commons/customselector/model/Image.kt index 38e7b6b855..a2965fb5df 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/model/Image.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/model/Image.kt @@ -9,65 +9,60 @@ import android.os.Parcelable */ data class Image( /** - id : Unique image id, primary key of image in device, eg 104950 + id : Unique image id, primary key of image in device, eg 104950 */ var id: Long, - /** - name : Name of the image with extension, eg CommonsLogo.jpeg + name : Name of the image with extension, eg CommonsLogo.jpeg */ var name: String, - /** - uri : Uri of the image, points to image location or name, eg content://media/external/images/camera/10495 (Android 10) + uri : Uri of the image, points to image location or name, eg content://media/external/images/camera/10495 (Android 10) */ var uri: Uri, - /** - path : System path of the image, eg storage/emulated/0/camera/CommonsLogo.jpeg + path : System path of the image, eg storage/emulated/0/camera/CommonsLogo.jpeg */ var path: String, - /** - bucketId : bucketId of folder, eg 540528482 + bucketId : bucketId of folder, eg 540528482 */ var bucketId: Long = 0, - /** - bucketName : name of folder, eg Camera + bucketName : name of folder, eg Camera */ var bucketName: String = "", - /** - sha1 : sha1 of original image. + sha1 : sha1 of original image. */ var sha1: String = "", - /** * date: Creation date of the image to show it inside the bubble during bubble scroll. */ - var date: String = "" - + var date: String = "", ) : Parcelable { - /** - default parcelable constructor. + default parcelable constructor. */ - constructor(parcel: Parcel): - this(parcel.readLong(), - parcel.readString()!!, - parcel.readParcelable(Uri::class.java.classLoader)!!, - parcel.readString()!!, - parcel.readLong(), - parcel.readString()!!, - parcel.readString()!!, - parcel.readString()!! - ) + constructor(parcel: Parcel) : + this( + parcel.readLong(), + parcel.readString()!!, + parcel.readParcelable(Uri::class.java.classLoader)!!, + parcel.readString()!!, + parcel.readLong(), + parcel.readString()!!, + parcel.readString()!!, + parcel.readString()!!, + ) /** - Write to parcel method. + Write to parcel method. */ - override fun writeToParcel(parcel: Parcel, flags: Int) { + override fun writeToParcel( + parcel: Parcel, + flags: Int, + ) { parcel.writeLong(id) parcel.writeString(name) parcel.writeParcelable(uri, flags) @@ -81,41 +76,38 @@ data class Image( /** * Describe the kinds of special objects contained in this Parcelable */ - override fun describeContents(): Int { - return 0 - } + override fun describeContents(): Int = 0 /** * Indicates whether some other object is "equal to" this one. */ override fun equals(other: Any?): Boolean { - - if(javaClass != other?.javaClass) { + if (javaClass != other?.javaClass) { return false } other as Image - if(id != other.id) { - return false; + if (id != other.id) { + return false } - if(name != other.name) { - return false; + if (name != other.name) { + return false } - if(uri != other.uri) { - return false; + if (uri != other.uri) { + return false } - if(path != other.path) { - return false; + if (path != other.path) { + return false } - if(bucketId != other.bucketId) { - return false; + if (bucketId != other.bucketId) { + return false } - if(bucketName != other.bucketName) { - return false; + if (bucketName != other.bucketName) { + return false } - if(sha1 != other.sha1) { - return false; + if (sha1 != other.sha1) { + return false } return true @@ -125,12 +117,8 @@ data class Image( * Parcelable companion object */ companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): Image { - return Image(parcel) - } + override fun createFromParcel(parcel: Parcel): Image = Image(parcel) - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } + override fun newArray(size: Int): Array = arrayOfNulls(size) } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/model/Result.kt b/app/src/main/java/fr/free/nrw/commons/customselector/model/Result.kt index 11ed8ef006..5cccccae6a 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/model/Result.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/model/Result.kt @@ -7,10 +7,9 @@ data class Result( /** * CallbackStatus : stores the result status */ - val status:CallbackStatus, - + val status: CallbackStatus, /** * Images : images retrieved */ - val images: ArrayList) { -} \ No newline at end of file + val images: ArrayList, +) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapter.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapter.kt index abebc89444..87f68a3e13 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapter.kt @@ -21,14 +21,11 @@ class FolderAdapter( * Application context. */ context: Context, - /** * Folder Click listener for click events. */ - private val itemClickListener: FolderClickListener - + private val itemClickListener: FolderClickListener, ) : RecyclerViewAdapter(context) { - /** * List of folders. */ @@ -37,7 +34,10 @@ class FolderAdapter( /** * Create view holder, returns View holder item. */ - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): FolderViewHolder { val itemView = inflater.inflate(R.layout.item_custom_selector_folder, parent, false) return FolderViewHolder(itemView) } @@ -45,28 +45,31 @@ class FolderAdapter( /** * Bind view holder, setup the item view, title, count and click listener */ - override fun onBindViewHolder(holder: FolderViewHolder, position: Int) { + override fun onBindViewHolder( + holder: FolderViewHolder, + position: Int, + ) { val folder = folders[position] val toBeRemoved = ArrayList() - for(image in folder.images) { + for (image in folder.images) { // Remove all the top images that do not exist anymore - if(context.contentResolver.getType(image.uri) == null){ + if (context.contentResolver.getType(image.uri) == null) { // File not found toBeRemoved.add(image) } else { break } } - holder.image.setImageDrawable (null) + holder.image.setImageDrawable(null) folder.images.removeAll(toBeRemoved) val count = folder.images.size - if(count == 0 && folders.size > 0) { + if (count == 0 && folders.size > 0) { // Folder is empty, remove folder from the adapter. - holder.itemView.post{ + holder.itemView.post { val updatePosition = folders.indexOf(folder) - if(updatePosition != -1) { + if (updatePosition != -1) { folders.removeAt(updatePosition) notifyItemRemoved(updatePosition) notifyItemRangeChanged(updatePosition, folders.size) @@ -89,9 +92,10 @@ class FolderAdapter( fun init(newFolders: List) { val oldFolderList: MutableList = folders val newFolderList = newFolders.toMutableList() - val diffResult = DiffUtil.calculateDiff( - FoldersDiffCallback(oldFolderList, newFolderList) - ) + val diffResult = + DiffUtil.calculateDiff( + FoldersDiffCallback(oldFolderList, newFolderList), + ) folders = newFolderList diffResult.dispatchUpdatesTo(this) } @@ -99,15 +103,14 @@ class FolderAdapter( /** * returns item count. */ - override fun getItemCount(): Int { - return folders.size - } + override fun getItemCount(): Int = folders.size /** * Folder view holder. */ - class FolderViewHolder(itemView:View) : RecyclerView.ViewHolder(itemView) { - + class FolderViewHolder( + itemView: View, + ) : RecyclerView.ViewHolder(itemView) { /** * Folder thumbnail image view. */ @@ -129,37 +132,33 @@ class FolderAdapter( */ class FoldersDiffCallback( var oldFolders: MutableList, - var newFolders: MutableList + var newFolders: MutableList, ) : DiffUtil.Callback() { /** * Returns the size of the old list. */ - override fun getOldListSize(): Int { - return oldFolders.size - } + override fun getOldListSize(): Int = oldFolders.size /** * Returns the size of the new list. */ - override fun getNewListSize(): Int { - return newFolders.size - } + override fun getNewListSize(): Int = newFolders.size /** * Called by the DiffUtil to decide whether two object represent the same Item. */ - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - return oldFolders.get(oldItemPosition).bucketId == newFolders.get(newItemPosition).bucketId - } + override fun areItemsTheSame( + oldItemPosition: Int, + newItemPosition: Int, + ): Boolean = oldFolders.get(oldItemPosition).bucketId == newFolders.get(newItemPosition).bucketId /** * Called by the DiffUtil when it wants to check whether two items have the same data. * DiffUtil uses this information to detect if the contents of an item has changed. */ - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - return oldFolders.get(oldItemPosition).equals(newFolders.get(newItemPosition)) - } - + override fun areContentsTheSame( + oldItemPosition: Int, + newItemPosition: Int, + ): Boolean = oldFolders.get(oldItemPosition).equals(newFolders.get(newItemPosition)) } - -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt index 58f4c83850..74b937f970 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt @@ -5,7 +5,6 @@ import android.content.SharedPreferences import android.view.View import android.view.ViewGroup import android.widget.ImageView -import android.widget.TextView import android.widget.Toast import androidx.constraintlayout.widget.Group import androidx.recyclerview.widget.DiffUtil @@ -20,8 +19,13 @@ import fr.free.nrw.commons.customselector.helper.ImageHelper.SHOW_ALREADY_ACTION import fr.free.nrw.commons.customselector.listeners.ImageSelectListener import fr.free.nrw.commons.customselector.model.Image import fr.free.nrw.commons.customselector.ui.selector.ImageLoader -import kotlinx.coroutines.* -import java.util.* +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch +import java.util.TreeMap import kotlin.collections.ArrayList /** @@ -32,20 +36,16 @@ class ImageAdapter( * Application Context. */ context: Context, - /** * Image select listener for click events on image. */ private var imageSelectListener: ImageSelectListener, - /** * ImageLoader queries images. */ - private var imageLoader: ImageLoader -): - - RecyclerViewAdapter(context), FastScrollRecyclerView.SectionedAdapter { - + private var imageLoader: ImageLoader, +) : RecyclerViewAdapter(context), + FastScrollRecyclerView.SectionedAdapter { /** * ImageSelectedOrUpdated payload class. */ @@ -106,14 +106,17 @@ class ImageAdapter( /** * Coroutine Dispatchers and Scope. */ - private var defaultDispatcher : CoroutineDispatcher = Dispatchers.Default - private var ioDispatcher : CoroutineDispatcher = Dispatchers.IO - private val scope : CoroutineScope = MainScope() + private var defaultDispatcher: CoroutineDispatcher = Dispatchers.Default + private var ioDispatcher: CoroutineDispatcher = Dispatchers.IO + private val scope: CoroutineScope = MainScope() /** * Create View holder. */ - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): ImageViewHolder { val itemView = inflater.inflate(R.layout.item_custom_selector_image, parent, false) return ImageViewHolder(itemView) } @@ -121,10 +124,15 @@ class ImageAdapter( /** * Bind View holder, load image, selected view, click listeners. */ - override fun onBindViewHolder(holder: ImageViewHolder, position: Int) { - if(images.size == 0) { return } - var image=images[position] - holder.image.setImageDrawable (null) + override fun onBindViewHolder( + holder: ImageViewHolder, + position: Int, + ) { + if (images.size == 0) { + return + } + var image = images[position] + holder.image.setImageDrawable(null) if (context.contentResolver.getType(image.uri) == null) { // Image does not exist anymore, update adapter. holder.itemView.post { @@ -140,18 +148,19 @@ class ImageAdapter( sharedPreferences.getBoolean(SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY, true) // Getting selected index when switch is on - val selectedIndex: Int = if (showAlreadyActionedImages) { - ImageHelper.getIndex(selectedImages, image) + val selectedIndex: Int = + if (showAlreadyActionedImages) { + ImageHelper.getIndex(selectedImages, image) - // Getting selected index when switch is off - } else if (actionableImagesMap.size > position) { - ImageHelper - .getIndex(selectedImages, ArrayList(actionableImagesMap.values)[position]) + // Getting selected index when switch is off + } else if (actionableImagesMap.size > position) { + ImageHelper + .getIndex(selectedImages, ArrayList(actionableImagesMap.values)[position]) - // For any other case return -1 - } else { - -1 - } + // For any other case return -1 + } else { + -1 + } val isSelected = selectedIndex != -1 if (isSelected) { @@ -160,7 +169,11 @@ class ImageAdapter( holder.itemUnselected() } imageLoader.queryAndSetView( - holder, image, ioDispatcher, defaultDispatcher ,uploadingContributionList + holder, + image, + ioDispatcher, + defaultDispatcher, + uploadingContributionList, ) scope.launch { val sharedPreferences: SharedPreferences = @@ -173,22 +186,28 @@ class ImageAdapter( if (!alreadyAddedPositions.contains(position)) { processThumbnailForActionedImage(holder, position, uploadingContributionList) - // If the position is already visited, that means the image is already present - // inside map, so it will fetch the image from the map and load in the holder + // If the position is already visited, that means the image is already present + // inside map, so it will fetch the image from the map and load in the holder } else { val actionableImages: List = ArrayList(actionableImagesMap.values) - if(actionableImages.size > position) { + if (actionableImages.size > position) { image = actionableImages[position] - Glide.with(holder.image).load(image.uri) - .thumbnail(0.3f).into(holder.image) + Glide + .with(holder.image) + .load(image.uri) + .thumbnail(0.3f) + .into(holder.image) } } - // If switch is turned off, it just fetches the image from all images without any - // further operations + // If switch is turned off, it just fetches the image from all images without any + // further operations } else { - Glide.with(holder.image).load(image.uri) - .thumbnail(0.3f).into(holder.image) + Glide + .with(holder.image) + .load(image.uri) + .thumbnail(0.3f) + .into(holder.image) } } @@ -210,12 +229,16 @@ class ImageAdapter( suspend fun processThumbnailForActionedImage( holder: ImageViewHolder, position: Int, - uploadingContributionList: List + uploadingContributionList: List, ) { - val next = imageLoader.nextActionableImage( - allImages, ioDispatcher, defaultDispatcher, - nextImagePosition, uploadingContributionList - ) + val next = + imageLoader.nextActionableImage( + allImages, + ioDispatcher, + defaultDispatcher, + nextImagePosition, + uploadingContributionList, + ) // If next actionable image is found, saves it, as the the search for // finding next actionable image will start from this position @@ -229,8 +252,11 @@ class ImageAdapter( actionableImagesMap[next] = allImages[next] alreadyAddedPositions.add(imagePositionAsPerIncreasingOrder) imagePositionAsPerIncreasingOrder++ - Glide.with(holder.image).load(allImages[next].uri) - .thumbnail(0.3f).into(holder.image) + Glide + .with(holder.image) + .load(allImages[next].uri) + .thumbnail(0.3f) + .into(holder.image) notifyItemInserted(position) notifyItemRangeChanged(position, itemCount + 1) } @@ -248,7 +274,7 @@ class ImageAdapter( */ private fun onThumbnailClicked( position: Int, - holder: ImageViewHolder + holder: ImageViewHolder, ) { val sharedPreferences: SharedPreferences = context.getSharedPreferences(CUSTOM_SELECTOR_PREFERENCE_KEY, 0) @@ -269,7 +295,10 @@ class ImageAdapter( /** * Handle click event on an image, update counter on images. */ - private fun selectOrRemoveImage(holder: ImageViewHolder, position: Int){ + private fun selectOrRemoveImage( + holder: ImageViewHolder, + position: Int, + ) { val sharedPreferences: SharedPreferences = context.getSharedPreferences(CUSTOM_SELECTOR_PREFERENCE_KEY, 0) val showAlreadyActionedImages = @@ -277,14 +306,15 @@ class ImageAdapter( // Getting clicked index from all images index when show_already_actioned_images // switch is on - val clickedIndex: Int = if(showAlreadyActionedImages) { - ImageHelper.getIndex(selectedImages, images[position]) + val clickedIndex: Int = + if (showAlreadyActionedImages) { + ImageHelper.getIndex(selectedImages, images[position]) - // Getting clicked index from actionable images when show_already_actioned_images - // switch is off - } else { - ImageHelper.getIndex(selectedImages, ArrayList(actionableImagesMap.values)[position]) - } + // Getting clicked index from actionable images when show_already_actioned_images + // switch is off + } else { + ImageHelper.getIndex(selectedImages, ArrayList(actionableImagesMap.values)[position]) + } if (clickedIndex != -1) { selectedImages.removeAt(clickedIndex) @@ -294,13 +324,14 @@ class ImageAdapter( notifyItemChanged(position, ImageUnselected()) // Getting index from all images index when switch is on - val indexes = if (showAlreadyActionedImages) { - ImageHelper.getIndexList(selectedImages, images) + val indexes = + if (showAlreadyActionedImages) { + ImageHelper.getIndexList(selectedImages, images) - // Getting index from actionable images when switch is off - } else { - ImageHelper.getIndexList(selectedImages, ArrayList(actionableImagesMap.values)) - } + // Getting index from actionable images when switch is off + } else { + ImageHelper.getIndexList(selectedImages, ArrayList(actionableImagesMap.values)) + } for (index in indexes) { notifyItemChanged(index, ImageSelectedOrUpdated()) } @@ -313,15 +344,16 @@ class ImageAdapter( } // Getting index from all images index when switch is on - val indexes: ArrayList = if (showAlreadyActionedImages) { - selectedImages.add(images[position]) - ImageHelper.getIndexList(selectedImages, images) + val indexes: ArrayList = + if (showAlreadyActionedImages) { + selectedImages.add(images[position]) + ImageHelper.getIndexList(selectedImages, images) - // Getting index from actionable images when switch is off - } else { - selectedImages.add(ArrayList(actionableImagesMap.values)[position]) - ImageHelper.getIndexList(selectedImages, ArrayList(actionableImagesMap.values)) - } + // Getting index from actionable images when switch is off + } else { + selectedImages.add(ArrayList(actionableImagesMap.values)[position]) + ImageHelper.getIndexList(selectedImages, ArrayList(actionableImagesMap.values)) + } for (index in indexes) { notifyItemChanged(index, ImageSelectedOrUpdated()) @@ -334,10 +366,15 @@ class ImageAdapter( /** * Initialize the data set. */ - fun init(newImages: List, fixedImages: List, emptyMap: TreeMap, uploadedImages: List = ArrayList()) { + fun init( + newImages: List, + fixedImages: List, + emptyMap: TreeMap, + uploadedImages: List = ArrayList(), + ) { allImages = fixedImages - val oldImageList:ArrayList = images - val newImageList:ArrayList = ArrayList(newImages) + val oldImageList: ArrayList = images + val newImageList: ArrayList = ArrayList(newImages) actionableImagesMap = emptyMap alreadyAddedPositions = ArrayList() uploadingContributionList = uploadedImages @@ -345,9 +382,10 @@ class ImageAdapter( reachedEndOfFolder = false selectedImages = ArrayList() imagePositionAsPerIncreasingOrder = 0 - val diffResult = DiffUtil.calculateDiff( - ImagesDiffCallback(oldImageList, newImageList) - ) + val diffResult = + DiffUtil.calculateDiff( + ImagesDiffCallback(oldImageList, newImageList), + ) images = newImageList diffResult.dispatchUpdatesTo(this) } @@ -355,31 +393,35 @@ class ImageAdapter( /** * Set new selected images */ - fun setSelectedImages(newSelectedImages: ArrayList){ + fun setSelectedImages(newSelectedImages: ArrayList) { selectedImages = ArrayList(newSelectedImages) imageSelectListener.onSelectedImagesChanged(selectedImages, 0) } + /** * Refresh the data in the adapter */ - fun refresh(newImages: List, fixedImages: List, uploadingImages: List = ArrayList()) { + fun refresh( + newImages: List, + fixedImages: List, + uploadingImages: List = ArrayList(), + ) { numberOfSelectedImagesMarkedAsNotForUpload = 0 images.clear() selectedImages = arrayListOf() - init(newImages, fixedImages, TreeMap(),uploadingImages) + init(newImages, fixedImages, TreeMap(), uploadingImages) notifyDataSetChanged() } /** * Clear selected images and empty the list. */ - fun clearSelectedImages(){ + fun clearSelectedImages() { numberOfSelectedImagesMarkedAsNotForUpload = 0 selectedImages.clear() selectedImages = arrayListOf() } - /** * Remove image from actionable images map. */ @@ -389,7 +431,7 @@ class ImageAdapter( val showAlreadyActionedImages = sharedPreferences.getBoolean(SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY, true) - if(showAlreadyActionedImages) { + if (showAlreadyActionedImages) { refresh(allImages, allImages, uploadingContributionList) } else { val iterator = actionableImagesMap.entries.iterator() @@ -402,16 +444,14 @@ class ImageAdapter( iterator.remove() alreadyAddedPositions.removeAt(alreadyAddedPositions.size - 1) notifyItemRemoved(index) - notifyItemRangeChanged(index, itemCount ) + notifyItemRangeChanged(index, itemCount) break } index++ } } - } - /** * Returns the total number of items in the data set held by the adapter. * @@ -424,24 +464,22 @@ class ImageAdapter( sharedPreferences.getBoolean(SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY, true) // While switch is on initializes the holder with all images size - return if(showAlreadyActionedImages) { + return if (showAlreadyActionedImages) { allImages.size - // While switch is off and searching for next actionable has ended, initializes the holder - // with size of all actionable images + // While switch is off and searching for next actionable has ended, initializes the holder + // with size of all actionable images } else if (actionableImagesMap.size == allImages.size || reachedEndOfFolder) { actionableImagesMap.size - // While switch is off, initializes the holder with and extra view holder so that finding - // and addition of the next actionable image in the adapter can be continued + // While switch is off, initializes the holder with and extra view holder so that finding + // and addition of the next actionable image in the adapter can be continued } else { actionableImagesMap.size + 1 } } - fun getImageIdAt(position: Int): Long { - return images.get(position).id - } + fun getImageIdAt(position: Int): Long = images.get(position).id /** * CleanUp function. @@ -453,7 +491,9 @@ class ImageAdapter( /** * Image view holder. */ - class ImageViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { + class ImageViewHolder( + itemView: View, + ) : RecyclerView.ViewHolder(itemView) { val image: ImageView = itemView.findViewById(R.id.image_thumbnail) private val uploadedGroup: Group = itemView.findViewById(R.id.uploaded_group) private val uploadingGroup: Group = itemView.findViewById(R.id.uploading_group) @@ -495,16 +535,12 @@ class ImageAdapter( notForUploadGroup.visibility = View.VISIBLE } - fun isItemUploaded():Boolean { - return uploadedGroup.visibility == View.VISIBLE - } + fun isItemUploaded(): Boolean = uploadedGroup.visibility == View.VISIBLE /** * Item is not for upload */ - fun isItemNotForUpload():Boolean { - return notForUploadGroup.visibility == View.VISIBLE - } + fun isItemNotForUpload(): Boolean = notForUploadGroup.visibility == View.VISIBLE /** * Item is not uploading @@ -533,45 +569,38 @@ class ImageAdapter( */ class ImagesDiffCallback( var oldImageList: ArrayList, - var newImageList: ArrayList - ) : DiffUtil.Callback(){ - + var newImageList: ArrayList, + ) : DiffUtil.Callback() { /** * Returns the size of the old list. */ - override fun getOldListSize(): Int { - return oldImageList.size - } + override fun getOldListSize(): Int = oldImageList.size /** * Returns the size of the new list. */ - override fun getNewListSize(): Int { - return newImageList.size - } + override fun getNewListSize(): Int = newImageList.size /** * Called by the DiffUtil to decide whether two object represent the same Item. */ - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - return newImageList[newItemPosition].id == oldImageList[oldItemPosition].id - } + override fun areItemsTheSame( + oldItemPosition: Int, + newItemPosition: Int, + ): Boolean = newImageList[newItemPosition].id == oldImageList[oldItemPosition].id /** * Called by the DiffUtil when it wants to check whether two items have the same data. * DiffUtil uses this information to detect if the contents of an item has changed. */ - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - return oldImageList[oldItemPosition].equals(newImageList[newItemPosition]) - } - + override fun areContentsTheSame( + oldItemPosition: Int, + newItemPosition: Int, + ): Boolean = oldImageList[oldItemPosition].equals(newImageList[newItemPosition]) } /** * Returns the text for showing inside the bubble during bubble scroll. */ - override fun getSectionName(position: Int): String { - return images[position].date - } - + override fun getSectionName(position: Int): String = images[position].date } diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/RecyclerViewAdapter.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/RecyclerViewAdapter.kt index 75f9353028..3318d58906 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/RecyclerViewAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/RecyclerViewAdapter.kt @@ -7,6 +7,8 @@ import androidx.recyclerview.widget.RecyclerView /** * Generic Recycler view adapter. */ -abstract class RecyclerViewAdapter(val context: Context): RecyclerView.Adapter() { +abstract class RecyclerViewAdapter( + val context: Context, +) : RecyclerView.Adapter() { val inflater: LayoutInflater = LayoutInflater.from(context) -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt index bcb7446d8e..d39b69f29c 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt @@ -8,23 +8,16 @@ import android.content.SharedPreferences import android.content.pm.PackageManager import android.os.Build import android.os.Bundle -import android.util.Log import android.view.View import android.view.Window import android.widget.Button import android.widget.ImageButton import android.widget.TextView import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme @@ -38,7 +31,6 @@ import androidx.compose.runtime.mutableStateOf 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.res.colorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -57,23 +49,27 @@ import fr.free.nrw.commons.databinding.ActivityCustomSelectorBinding import fr.free.nrw.commons.databinding.CustomSelectorBottomLayoutBinding import fr.free.nrw.commons.databinding.CustomSelectorToolbarBinding import fr.free.nrw.commons.filepicker.Constants -import fr.free.nrw.commons.filepicker.FilePicker import fr.free.nrw.commons.media.ZoomableActivity import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.upload.FileUtilsWrapper import fr.free.nrw.commons.utils.CustomSelectorUtils -import fr.free.nrw.commons.utils.PermissionUtils -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.io.File import java.lang.Integer.max import javax.inject.Inject - /** * Custom Selector Activity. */ -class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectListener { - +class CustomSelectorActivity : + BaseActivity(), + FolderClickListener, + ImageSelectListener { /** * ViewBindings */ @@ -147,7 +143,7 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL */ var imageFragment: ImageFragment? = null - private var progressDialogText:String="" + private var progressDialogText: String = "" private var showPartialAccessIndicator by mutableStateOf(false) @@ -158,7 +154,8 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL super.onCreate(savedInstanceState) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && ContextCompat.checkSelfPermission( - this, Manifest.permission.READ_MEDIA_IMAGES + this, + Manifest.permission.READ_MEDIA_IMAGES, ) == PackageManager.PERMISSION_DENIED ) { showPartialAccessIndicator = true @@ -168,25 +165,27 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL toolbarBinding = CustomSelectorToolbarBinding.bind(binding.root) bottomSheetBinding = CustomSelectorBottomLayoutBinding.bind(binding.root) binding.partialAccessIndicator.setContent { - PartialStorageAccessIndicator( + partialStorageAccessIndicator( isVisible = showPartialAccessIndicator, onManage = { - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { requestPermissions(arrayOf(Manifest.permission.READ_MEDIA_IMAGES), 1) } }, - modifier = Modifier - .padding(vertical = 8.dp, horizontal = 4.dp) - .fillMaxWidth() + modifier = + Modifier + .padding(vertical = 8.dp, horizontal = 4.dp) + .fillMaxWidth(), ) } val view = binding.root setContentView(view) prefs = applicationContext.getSharedPreferences("CustomSelector", MODE_PRIVATE) - viewModel = ViewModelProvider(this, customSelectorViewModelFactory).get( - CustomSelectorViewModel::class.java - ) + viewModel = + ViewModelProvider(this, customSelectorViewModelFactory).get( + CustomSelectorViewModel::class.java, + ) setupViews() @@ -208,11 +207,11 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, - grantResults: IntArray + grantResults: IntArray, ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) - if(requestCode == 1 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (requestCode == 1 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { showPartialAccessIndicator = false } } @@ -226,7 +225,11 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL /** * When data will be send from full screen mode, it will be passed to fragment */ - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + override fun onActivityResult( + requestCode: Int, + resultCode: Int, + data: Intent?, + ) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == Constants.RequestCodes.RECEIVE_DATA_FROM_FULL_SCREEN_MODE && resultCode == Activity.RESULT_OK @@ -254,7 +257,8 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL * Set up view, default folder view. */ private fun setupViews() { - supportFragmentManager.beginTransaction() + supportFragmentManager + .beginTransaction() .replace(R.id.fragment_container, FolderFragment.newInstance()) .commit() setUpToolbar() @@ -322,12 +326,13 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL var allImagesAlreadyNotForUpload = true images.forEach { image -> - val imageSHA1 = CustomSelectorUtils.getImageSHA1( - image.uri, - ioDispatcher, - fileUtilsWrapper, - contentResolver - ) + val imageSHA1 = + CustomSelectorUtils.getImageSHA1( + image.uri, + ioDispatcher, + fileUtilsWrapper, + contentResolver, + ) val exists = notForUploadStatusDao.find(imageSHA1) if (exists < 1) { allImagesAlreadyNotForUpload = false @@ -337,12 +342,13 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL if (!allImagesAlreadyNotForUpload) { // Insert or delete images as necessary, but the UI updates should be posted back to the main thread images.forEach { image -> - val imageSHA1 = CustomSelectorUtils.getImageSHA1( - image.uri, - ioDispatcher, - fileUtilsWrapper, - contentResolver - ) + val imageSHA1 = + CustomSelectorUtils.getImageSHA1( + image.uri, + ioDispatcher, + fileUtilsWrapper, + contentResolver, + ) notForUploadStatusDao.insert(NotForUploadStatus(imageSHA1)) } withContext(Dispatchers.Main) { @@ -353,12 +359,13 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL } } else { images.forEach { image -> - val imageSHA1 = CustomSelectorUtils.getImageSHA1( - image.uri, - ioDispatcher, - fileUtilsWrapper, - contentResolver - ) + val imageSHA1 = + CustomSelectorUtils.getImageSHA1( + image.uri, + ioDispatcher, + fileUtilsWrapper, + contentResolver, + ) notForUploadStatusDao.deleteNotForUploadWithImageSHA1(imageSHA1) } @@ -386,13 +393,19 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL /** * Change the title of the toolbar. */ - private fun changeTitle(title: String, selectedImageCount:Int) { - if (title.isNotEmpty()){ + private fun changeTitle( + title: String, + selectedImageCount: Int, + ) { + if (title.isNotEmpty()) { val titleText = findViewById(R.id.title) var titleWithAppendedImageCount = title if (selectedImageCount > 0) { - titleWithAppendedImageCount += " (${resources.getQuantityString(R.plurals.custom_picker_images_selected_title_appendix, - selectedImageCount, selectedImageCount)})" + titleWithAppendedImageCount += " (${resources.getQuantityString( + R.plurals.custom_picker_images_selected_title_appendix, + selectedImageCount, + selectedImageCount, + )})" } if (titleText != null) { titleText.text = titleWithAppendedImageCount @@ -415,8 +428,13 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL /** * override on folder click, change the toolbar title on folder click. */ - override fun onFolderClick(folderId: Long, folderName: String, lastItemId: Long) { - supportFragmentManager.beginTransaction() + override fun onFolderClick( + folderId: Long, + folderName: String, + lastItemId: Long, + ) { + supportFragmentManager + .beginTransaction() .add(R.id.fragment_container, ImageFragment.newInstance(folderId, lastItemId)) .addToBackStack(null) .commit() @@ -433,18 +451,21 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL */ override fun onSelectedImagesChanged( selectedImages: ArrayList, - selectedNotForUploadImages: Int + selectedNotForUploadImages: Int, ) { viewModel.selectedImages.value = selectedImages changeTitle(bucketName, selectedImages.size) uploadLimitExceeded = selectedImages.size > uploadLimit - uploadLimitExceededBy = max(selectedImages.size - uploadLimit,0) + uploadLimitExceededBy = max(selectedImages.size - uploadLimit, 0) if (uploadLimitExceeded && selectedNotForUploadImages == 0) { toolbarBinding.imageLimitError.visibility = View.VISIBLE - bottomSheetBinding.upload.text = resources.getString( - R.string.custom_selector_button_limit_text, uploadLimit) + bottomSheetBinding.upload.text = + resources.getString( + R.string.custom_selector_button_limit_text, + uploadLimit, + ) } else { toolbarBinding.imageLimitError.visibility = View.INVISIBLE bottomSheetBinding.upload.text = resources.getString(R.string.upload) @@ -461,11 +482,11 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL bottomSheetBinding.notForUpload.text = when (selectedImages.size == selectedNotForUploadImages) { true -> { - progressDialogText=getString(R.string.unmarking_as_not_for_upload) + progressDialogText = getString(R.string.unmarking_as_not_for_upload) getString(R.string.unmark_as_not_for_upload) } else -> { - progressDialogText=getString(R.string.marking_as_not_for_upload) + progressDialogText = getString(R.string.marking_as_not_for_upload) getString(R.string.mark_as_not_for_upload) } } @@ -481,13 +502,13 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL override fun onLongPress( position: Int, images: ArrayList, - selectedImages: ArrayList + selectedImages: ArrayList, ) { val intent = Intent(this, ZoomableActivity::class.java) intent.putExtra(CustomSelectorConstants.PRESENT_POSITION, position) intent.putParcelableArrayListExtra( CustomSelectorConstants.TOTAL_SELECTED_IMAGES, - selectedImages + selectedImages, ) intent.putExtra(CustomSelectorConstants.BUCKET_ID, bucketId) startActivityForResult(intent, Constants.RequestCodes.RECEIVE_DATA_FROM_FULL_SCREEN_MODE) @@ -498,22 +519,22 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL * Get the selected images. Remove any non existent file, forward the data to finish selector. */ fun onDone() { - val selectedImages = viewModel.selectedImages.value - if (selectedImages.isNullOrEmpty()) { - finishPickImages(arrayListOf()) - return - } - var i = 0 - while (i < selectedImages.size) { - val path = selectedImages[i].path - val file = File(path) - if (!file.exists()) { - selectedImages.removeAt(i) - i-- - } - i++ + val selectedImages = viewModel.selectedImages.value + if (selectedImages.isNullOrEmpty()) { + finishPickImages(arrayListOf()) + return + } + var i = 0 + while (i < selectedImages.size) { + val path = selectedImages[i].path + val file = File(path) + if (!file.exists()) { + selectedImages.removeAt(i) + i-- } - finishPickImages(selectedImages) + i++ + } + finishPickImages(selectedImages) } /** @@ -547,10 +568,13 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL val dialog = Dialog(this) dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) dialog.setContentView(R.layout.custom_selector_limit_dialog) - (dialog.findViewById(R.id.btn_dismiss_limit_warning) as Button).setOnClickListener() - { dialog.dismiss() } - (dialog.findViewById(R.id.upload_limit_warning) as TextView).text = resources.getString( - R.string.custom_selector_over_limit_warning, uploadLimit, uploadLimitExceededBy) + (dialog.findViewById(R.id.btn_dismiss_limit_warning) as Button).setOnClickListener { dialog.dismiss() } + (dialog.findViewById(R.id.upload_limit_warning) as TextView).text = + resources.getString( + R.string.custom_selector_over_limit_warning, + uploadLimit, + uploadLimitExceededBy, + ) dialog.show() } @@ -560,9 +584,17 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL */ override fun onDestroy() { if (isImageFragmentOpen) { - prefs.edit().putLong(FOLDER_ID, bucketId).putString(FOLDER_NAME, bucketName).apply() + prefs + .edit() + .putLong(FOLDER_ID, bucketId) + .putString(FOLDER_NAME, bucketName) + .apply() } else { - prefs.edit().remove(FOLDER_ID).remove(FOLDER_NAME).apply() + prefs + .edit() + .remove(FOLDER_ID) + .remove(FOLDER_NAME) + .apply() } super.onDestroy() } @@ -573,38 +605,41 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL const val ITEM_ID: String = "ItemId" } } + @Composable -fun PartialStorageAccessIndicator( +fun partialStorageAccessIndicator( isVisible: Boolean, - onManage: ()-> Unit, - modifier: Modifier = Modifier + onManage: () -> Unit, + modifier: Modifier = Modifier, ) { - if(isVisible) { + if (isVisible) { OutlinedCard( modifier = modifier, - colors = CardDefaults.cardColors( - containerColor = colorResource(R.color.primarySuperLightColor) - ), + colors = + CardDefaults.cardColors( + containerColor = colorResource(R.color.primarySuperLightColor), + ), border = BorderStroke(0.5.dp, color = colorResource(R.color.primaryColor)), - shape = RoundedCornerShape(8.dp) + shape = RoundedCornerShape(8.dp), ) { Row(modifier = Modifier.padding(16.dp).fillMaxWidth()) { Text( text = "You've given access to a select number of photos", - modifier = Modifier.weight(1f) + modifier = Modifier.weight(1f), ) TextButton( onClick = onManage, modifier = Modifier.align(Alignment.Bottom), - colors = ButtonDefaults.buttonColors( - containerColor = colorResource(R.color.primaryColor) - ), - shape = RoundedCornerShape(8.dp) + colors = + ButtonDefaults.buttonColors( + containerColor = colorResource(R.color.primaryColor), + ), + shape = RoundedCornerShape(8.dp), ) { Text( text = "Manage", style = MaterialTheme.typography.labelMedium, - color = colorResource(R.color.primaryTextColor) + color = colorResource(R.color.primaryTextColor), ) } } @@ -614,11 +649,15 @@ fun PartialStorageAccessIndicator( @Preview @Composable -fun PartialStorageAccessIndicatorPreview() { +fun partialStorageAccessIndicatorPreview() { Surface { - PartialStorageAccessIndicator(isVisible = true, onManage = {}, modifier = Modifier - .padding(vertical = 8.dp, horizontal = 4.dp) - .fillMaxWidth() + partialStorageAccessIndicator( + isVisible = true, + onManage = {}, + modifier = + Modifier + .padding(vertical = 8.dp, horizontal = 4.dp) + .fillMaxWidth(), ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModel.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModel.kt index 90f9963fbb..f3465063a7 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModel.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModel.kt @@ -14,8 +14,10 @@ import kotlinx.coroutines.cancel /** * Custom Selector view model. */ -class CustomSelectorViewModel(var context: Context,var imageFileLoader: ImageFileLoader) : ViewModel() { - +class CustomSelectorViewModel( + var context: Context, + var imageFileLoader: ImageFileLoader, +) : ViewModel() { /** * Scope for coroutine task (image fetch). */ @@ -37,15 +39,17 @@ class CustomSelectorViewModel(var context: Context,var imageFileLoader: ImageFil fun fetchImages() { result.postValue(Result(CallbackStatus.FETCHING, arrayListOf())) scope.cancel() - imageFileLoader.loadDeviceImages(object: ImageLoaderListener { - override fun onImageLoaded(images: ArrayList) { - result.postValue(Result(CallbackStatus.SUCCESS, images)) - } - - override fun onFailed(throwable: Throwable) { - result.postValue(Result(CallbackStatus.SUCCESS, arrayListOf())) - } - }) + imageFileLoader.loadDeviceImages( + object : ImageLoaderListener { + override fun onImageLoaded(images: ArrayList) { + result.postValue(Result(CallbackStatus.SUCCESS, images)) + } + + override fun onFailed(throwable: Throwable) { + result.postValue(Result(CallbackStatus.SUCCESS, arrayListOf())) + } + }, + ) } /** @@ -55,4 +59,4 @@ class CustomSelectorViewModel(var context: Context,var imageFileLoader: ImageFil scope.cancel() super.onCleared() } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelFactory.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelFactory.kt index 616c5e83fc..27679a5df9 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelFactory.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelFactory.kt @@ -8,10 +8,12 @@ import javax.inject.Inject /** * View Model Factory. */ -class CustomSelectorViewModelFactory @Inject constructor(val context: Context,val imageFileLoader: ImageFileLoader) : ViewModelProvider.Factory { - - override fun create(modelClass: Class) : CustomSelectorViewModel { - return CustomSelectorViewModel(context,imageFileLoader) as CustomSelectorViewModel +class CustomSelectorViewModelFactory + @Inject + constructor( + val context: Context, + val imageFileLoader: ImageFileLoader, + ) : ViewModelProvider.Factory { + override fun create(modelClass: Class): CustomSelectorViewModel = + CustomSelectorViewModel(context, imageFileLoader) as CustomSelectorViewModel } - -} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/FolderFragment.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/FolderFragment.kt index 95f427f49e..6ca2b06e40 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/FolderFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/FolderFragment.kt @@ -9,10 +9,10 @@ import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import fr.free.nrw.commons.customselector.helper.ImageHelper -import fr.free.nrw.commons.customselector.model.Result import fr.free.nrw.commons.customselector.listeners.FolderClickListener import fr.free.nrw.commons.customselector.model.CallbackStatus import fr.free.nrw.commons.customselector.model.Folder +import fr.free.nrw.commons.customselector.model.Result import fr.free.nrw.commons.customselector.ui.adapter.FolderAdapter import fr.free.nrw.commons.databinding.FragmentCustomSelectorBinding import fr.free.nrw.commons.di.CommonsDaggerSupportFragment @@ -24,12 +24,11 @@ import javax.inject.Inject * Custom selector folder fragment. */ class FolderFragment : CommonsDaggerSupportFragment() { - /** * ViewBinding */ private var _binding: FragmentCustomSelectorBinding? = null - private val binding get() = _binding + val binding get() = _binding /** * View Model for images. @@ -53,6 +52,7 @@ class FolderFragment : CommonsDaggerSupportFragment() { var mediaClient: MediaClient? = null @Inject set + /** * Folder Adapter. */ @@ -66,15 +66,13 @@ class FolderFragment : CommonsDaggerSupportFragment() { /** * Folder List. */ - private lateinit var folders : ArrayList + private lateinit var folders: ArrayList /** * Companion newInstance. */ - companion object{ - fun newInstance(): FolderFragment { - return FolderFragment() - } + companion object { + fun newInstance(): FolderFragment = FolderFragment() } /** @@ -83,21 +81,24 @@ class FolderFragment : CommonsDaggerSupportFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - viewModel = ViewModelProvider(requireActivity(),customSelectorViewModelFactory!!).get(CustomSelectorViewModel::class.java) - + viewModel = ViewModelProvider(requireActivity(), customSelectorViewModelFactory!!).get(CustomSelectorViewModel::class.java) } /** * OnCreateView. * Inflate Layout, init adapter, init gridLayoutManager, setUp recycler view, observe the view model for result. */ - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { _binding = FragmentCustomSelectorBinding.inflate(inflater, container, false) folderAdapter = FolderAdapter(requireActivity(), activity as FolderClickListener) gridLayoutManager = GridLayoutManager(context, columnCount()) selectorRV = binding?.selectorRv loader = binding?.loader - with(binding?.selectorRv){ + with(binding?.selectorRv) { this?.layoutManager = gridLayoutManager this?.setHasFixedSize(true) this?.adapter = folderAdapter @@ -114,9 +115,9 @@ class FolderFragment : CommonsDaggerSupportFragment() { * Load adapter. */ private fun handleResult(result: Result) { - if(result.status is CallbackStatus.SUCCESS){ + if (result.status is CallbackStatus.SUCCESS) { val images = result.images - if(images.isEmpty()){ + if (images.isEmpty()) { binding?.emptyText?.let { it.visibility = View.VISIBLE } diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoader.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoader.kt index bc4d179a2e..f079dee507 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoader.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoader.kt @@ -20,8 +20,9 @@ import kotlin.coroutines.CoroutineContext * Custom Selector Image File Loader. * Loads device images. */ -class ImageFileLoader(val context: Context) : CoroutineScope{ - +class ImageFileLoader( + val context: Context, +) : CoroutineScope { /** * Coroutine context for fetching images. */ @@ -30,14 +31,15 @@ class ImageFileLoader(val context: Context) : CoroutineScope{ /** * Media paramerters required. */ - private val projection = arrayOf( - MediaStore.Images.Media._ID, - MediaStore.Images.Media.DISPLAY_NAME, - MediaStore.Images.Media.DATA, - MediaStore.Images.Media.BUCKET_ID, - MediaStore.Images.Media.BUCKET_DISPLAY_NAME, - MediaStore.Images.Media.DATE_ADDED - ) + private val projection = + arrayOf( + MediaStore.Images.Media._ID, + MediaStore.Images.Media.DISPLAY_NAME, + MediaStore.Images.Media.DATA, + MediaStore.Images.Media.BUCKET_ID, + MediaStore.Images.Media.BUCKET_DISPLAY_NAME, + MediaStore.Images.Media.DATE_ADDED, + ) /** * Load Device Images under coroutine. @@ -50,12 +52,18 @@ class ImageFileLoader(val context: Context) : CoroutineScope{ } } - /** * Load Device images using cursor */ - private fun getImages(listener:ImageLoaderListener) { - val cursor = context.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, MediaStore.Images.Media.DATE_ADDED + " DESC") + private fun getImages(listener: ImageLoaderListener) { + val cursor = + context.contentResolver.query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + projection, + null, + null, + MediaStore.Images.Media.DATE_ADDED + " DESC", + ) if (cursor == null) { listener.onFailed(NullPointerException()) return @@ -85,10 +93,12 @@ class ImageFileLoader(val context: Context) : CoroutineScope{ val file = if (path == null || path.isEmpty()) { null - } else try { - File(path) - } catch (ignored: Exception) { - null + } else { + try { + File(path) + } catch (ignored: Exception) { + null + } } if (file != null && file.exists() && name != null && path != null && bucketName != null) { @@ -106,30 +116,29 @@ class ImageFileLoader(val context: Context) : CoroutineScope{ val dateFormat = DateFormat.getMediumDateFormat(context) val formattedDate = dateFormat.format(date) - val image = Image( - id, - name, - uri, - path, - bucketId, - bucketName, - date = (formattedDate) - ) + val image = + Image( + id, + name, + uri, + path, + bucketId, + bucketName, + date = (formattedDate), + ) images.add(image) } - } while (cursor.moveToNext()) } cursor.close() listener.onImageLoaded(images) } - /** * Abort loading images. */ - fun abortLoadImage(){ - //todo Abort loading images. + fun abortLoadImage() { + // todo Abort loading images. } /* diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt index c5e5de4f64..7e522f6815 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt @@ -37,17 +37,19 @@ import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.upload.FileProcessor import fr.free.nrw.commons.upload.FileUtilsWrapper import io.reactivex.schedulers.Schedulers -import java.util.* +import java.util.TreeMap import javax.inject.Inject import kotlin.collections.ArrayList /** * Custom Selector Image Fragment. */ -class ImageFragment : CommonsDaggerSupportFragment(), RefreshUIListener, PassDataListener { - +class ImageFragment : + CommonsDaggerSupportFragment(), + RefreshUIListener, + PassDataListener { private var _binding: FragmentCustomSelectorBinding? = null - private val binding get() = _binding + val binding get() = _binding /** * Current bucketId. @@ -107,7 +109,6 @@ class ImageFragment : CommonsDaggerSupportFragment(), RefreshUIListener, PassDat private lateinit var progressDialog: AlertDialog private lateinit var progressDialogLayout: ProgressDialogBinding - /** * NotForUploadStatus Dao class for database operations */ @@ -142,7 +143,6 @@ class ImageFragment : CommonsDaggerSupportFragment(), RefreshUIListener, PassDat lateinit var contributionDao: ContributionDao companion object { - /** * Switch state */ @@ -157,7 +157,10 @@ class ImageFragment : CommonsDaggerSupportFragment(), RefreshUIListener, PassDat /** * newInstance from bucketId. */ - fun newInstance(bucketId: Long, lastItemId: Long): ImageFragment { + fun newInstance( + bucketId: Long, + lastItemId: Long, + ): ImageFragment { val fragment = ImageFragment() val args = Bundle() args.putLong(BUCKET_ID, bucketId) @@ -175,9 +178,10 @@ class ImageFragment : CommonsDaggerSupportFragment(), RefreshUIListener, PassDat super.onCreate(savedInstanceState) bucketId = arguments?.getLong(BUCKET_ID) lastItemId = arguments?.getLong(LAST_ITEM_ID, 0) - viewModel = ViewModelProvider(requireActivity(), customSelectorViewModelFactory).get( - CustomSelectorViewModel::class.java - ) + viewModel = + ViewModelProvider(requireActivity(), customSelectorViewModelFactory).get( + CustomSelectorViewModel::class.java, + ) } /** @@ -188,7 +192,7 @@ class ImageFragment : CommonsDaggerSupportFragment(), RefreshUIListener, PassDat override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View? { _binding = FragmentCustomSelectorBinding.inflate(inflater, container, false) imageAdapter = @@ -200,9 +204,12 @@ class ImageFragment : CommonsDaggerSupportFragment(), RefreshUIListener, PassDat this?.adapter = imageAdapter } - viewModel?.result?.observe(viewLifecycleOwner, Observer { - handleResult(it) - }) + viewModel?.result?.observe( + viewLifecycleOwner, + Observer { + handleResult(it) + }, + ) switch = binding?.switchWidget switch?.visibility = View.VISIBLE @@ -323,20 +330,22 @@ class ImageFragment : CommonsDaggerSupportFragment(), RefreshUIListener, PassDat override fun onDestroy() { imageAdapter.cleanUp() - val position = (selectorRV?.layoutManager as GridLayoutManager) - .findFirstVisibleItemPosition() + val position = + (selectorRV?.layoutManager as GridLayoutManager) + .findFirstVisibleItemPosition() // Check for empty RecyclerView. if (position != -1 && filteredImages.size > 0) { context?.let { context -> - context.getSharedPreferences( - "CustomSelector", - BaseActivity.MODE_PRIVATE - )?.let { prefs -> - prefs.edit()?.let { editor -> - editor.putLong("ItemId", imageAdapter.getImageIdAt(position))?.apply() + context + .getSharedPreferences( + "CustomSelector", + BaseActivity.MODE_PRIVATE, + )?.let { prefs -> + prefs.edit()?.let { editor -> + editor.putLong("ItemId", imageAdapter.getImageIdAt(position))?.apply() + } } - } } } super.onDestroy() @@ -354,7 +363,7 @@ class ImageFragment : CommonsDaggerSupportFragment(), RefreshUIListener, PassDat /** * Removes the image from the actionable image map */ - fun removeImage(image : Image){ + fun removeImage(image: Image) { imageAdapter.removeImageFromActionableImageMap(image) } @@ -364,11 +373,15 @@ class ImageFragment : CommonsDaggerSupportFragment(), RefreshUIListener, PassDat fun clearSelectedImages() { imageAdapter.clearSelectedImages() } + /** * Passes selected images and other information from Activity to Fragment and connects it with * the adapter */ - override fun passSelectedImages(selectedImages: ArrayList, shouldRefresh: Boolean) { + override fun passSelectedImages( + selectedImages: ArrayList, + shouldRefresh: Boolean, + ) { imageAdapter.setSelectedImages(selectedImages) val uploadingContributions = getUploadingContributions() @@ -398,11 +411,10 @@ class ImageFragment : CommonsDaggerSupportFragment(), RefreshUIListener, PassDat } } - private fun getUploadingContributions(): List { - - return contributionDao.getContribution( - listOf(Contribution.STATE_IN_PROGRESS, Contribution.STATE_FAILED, Contribution.STATE_QUEUED, Contribution.STATE_PAUSED) - )?.subscribeOn(Schedulers.io())?.blockingGet() ?: emptyList() - } - + private fun getUploadingContributions(): List = + contributionDao + .getContribution( + listOf(Contribution.STATE_IN_PROGRESS, Contribution.STATE_FAILED, Contribution.STATE_QUEUED, Contribution.STATE_PAUSED), + )?.subscribeOn(Schedulers.io()) + ?.blockingGet() ?: emptyList() } diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageLoader.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageLoader.kt index 4e3ad09021..1fb5c59537 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageLoader.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageLoader.kt @@ -15,368 +15,389 @@ import fr.free.nrw.commons.upload.FileProcessor import fr.free.nrw.commons.upload.FileUtilsWrapper import fr.free.nrw.commons.utils.CustomSelectorUtils import fr.free.nrw.commons.utils.CustomSelectorUtils.Companion.checkWhetherFileExistsOnCommonsUsingSHA1 -import kotlinx.coroutines.* -import java.util.* +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch +import java.util.Calendar import java.util.concurrent.TimeUnit import javax.inject.Inject /** * Image Loader class, loads images, depending on API results. */ -class ImageLoader @Inject constructor( - - /** - * MediaClient for SHA1 query. - */ - var mediaClient: MediaClient, - - /** - * FileProcessor to pre-process the file. - */ - var fileProcessor: FileProcessor, - - /** - * File Utils Wrapper for SHA1 - */ - var fileUtilsWrapper: FileUtilsWrapper, - - /** - * UploadedStatusDao for cache query. - */ - var uploadedStatusDao: UploadedStatusDao, - - /** - * NotForUploadDao for database operations - */ - var notForUploadStatusDao: NotForUploadStatusDao, - - /** - * Context for coroutine. - */ - val context: Context -) { - - /** - * Maps to facilitate image query. - */ - private var mapModifiedImageSHA1: HashMap = HashMap() - private var mapHolderImage : HashMap = HashMap() - private var mapResult: HashMap = HashMap() - private var mapImageSHA1: HashMap = HashMap() - - /** - * Coroutine Scope. - */ - private val scope : CoroutineScope = MainScope() - - /** - * Query image and setUp the view. - */ - fun queryAndSetView( - holder: ImageViewHolder, - image: Image, - ioDispatcher: CoroutineDispatcher, - defaultDispatcher: CoroutineDispatcher, - uploadedContributionsList : List +class ImageLoader + @Inject + constructor( + /** + * MediaClient for SHA1 query. + */ + var mediaClient: MediaClient, + /** + * FileProcessor to pre-process the file. + */ + var fileProcessor: FileProcessor, + /** + * File Utils Wrapper for SHA1 + */ + var fileUtilsWrapper: FileUtilsWrapper, + /** + * UploadedStatusDao for cache query. + */ + var uploadedStatusDao: UploadedStatusDao, + /** + * NotForUploadDao for database operations + */ + var notForUploadStatusDao: NotForUploadStatusDao, + /** + * Context for coroutine. + */ + val context: Context, ) { - /** - * Recycler view uses same view holder, so we can identify the latest query image from holder. + * Maps to facilitate image query. */ - mapHolderImage[holder] = image - holder.itemNotUploaded() - holder.itemForUpload() - holder.itemNotUploading() - - scope.launch { - var result: Result = Result.NOTFOUND + private var mapModifiedImageSHA1: HashMap = HashMap() + private var mapHolderImage: HashMap = HashMap() + private var mapResult: HashMap = HashMap() + private var mapImageSHA1: HashMap = HashMap() - if (mapHolderImage[holder] != image) { - return@launch - } - - val imageSHA1: String = when (mapImageSHA1[image.uri] != null) { - true -> mapImageSHA1[image.uri]!! - else -> CustomSelectorUtils.getImageSHA1( - image.uri, - ioDispatcher, - fileUtilsWrapper, - context.contentResolver - ) - } - mapImageSHA1[image.uri] = imageSHA1 - - if (imageSHA1.isEmpty()) { - return@launch - } - val uploadedStatus = getFromUploaded(imageSHA1) + /** + * Coroutine Scope. + */ + private val scope: CoroutineScope = MainScope() - val sha1 = uploadedStatus?.let { - result = getResultFromUploadedStatus(uploadedStatus) - uploadedStatus.modifiedImageSHA1 - } ?: run { - if (mapHolderImage[holder] == image) { - getSHA1(image, defaultDispatcher) - } else { - "" + /** + * Query image and setUp the view. + */ + fun queryAndSetView( + holder: ImageViewHolder, + image: Image, + ioDispatcher: CoroutineDispatcher, + defaultDispatcher: CoroutineDispatcher, + uploadedContributionsList: List, + ) { + /** + * Recycler view uses same view holder, so we can identify the latest query image from holder. + */ + mapHolderImage[holder] = image + holder.itemNotUploaded() + holder.itemForUpload() + holder.itemNotUploading() + + scope.launch { + var result: Result = Result.NOTFOUND + + if (mapHolderImage[holder] != image) { + return@launch } - } - if (mapHolderImage[holder] != image) { - return@launch - } + val imageSHA1: String = + when (mapImageSHA1[image.uri] != null) { + true -> mapImageSHA1[image.uri]!! + else -> + CustomSelectorUtils.getImageSHA1( + image.uri, + ioDispatcher, + fileUtilsWrapper, + context.contentResolver, + ) + } + mapImageSHA1[image.uri] = imageSHA1 - val existsInNotForUploadTable = notForUploadStatusDao.find(imageSHA1) - - if (result in arrayOf(Result.NOTFOUND, Result.INVALID) && sha1.isNotEmpty()) { - when { - mapResult[imageSHA1] == null -> { - // Query original image. - result = checkWhetherFileExistsOnCommonsUsingSHA1( - imageSHA1, - ioDispatcher, - mediaClient - ) - when (result) { - is Result.TRUE -> { - mapResult[imageSHA1] = Result.TRUE - } - is Result.ERROR -> { - mapResult[imageSHA1] = Result.ERROR - } - is Result.FALSE -> { - mapResult[imageSHA1] = Result.FALSE - } - is Result.INVALID -> { - mapResult[imageSHA1] = Result.INVALID - } - is Result.NOTFOUND -> { - mapResult[imageSHA1] = Result.NOTFOUND - } + if (imageSHA1.isEmpty()) { + return@launch + } + val uploadedStatus = getFromUploaded(imageSHA1) + + val sha1 = + uploadedStatus?.let { + result = getResultFromUploadedStatus(uploadedStatus) + uploadedStatus.modifiedImageSHA1 + } ?: run { + if (mapHolderImage[holder] == image) { + getSHA1(image, defaultDispatcher) + } else { + "" } } - else -> { - result = mapResult[imageSHA1]!! - } + + if (mapHolderImage[holder] != image) { + return@launch } - if (result is Result.TRUE) { - // Original image found. - insertIntoUploaded(imageSHA1, sha1, result is Result.TRUE, false) - } else { + + val existsInNotForUploadTable = notForUploadStatusDao.find(imageSHA1) + + if (result in arrayOf(Result.NOTFOUND, Result.INVALID) && sha1.isNotEmpty()) { when { - mapResult[sha1] == null -> { - // Original image not found, query modified image. - result = checkWhetherFileExistsOnCommonsUsingSHA1( - sha1, - ioDispatcher, - mediaClient - ) + mapResult[imageSHA1] == null -> { + // Query original image. + result = + checkWhetherFileExistsOnCommonsUsingSHA1( + imageSHA1, + ioDispatcher, + mediaClient, + ) when (result) { is Result.TRUE -> { - mapResult[sha1] = Result.TRUE + mapResult[imageSHA1] = Result.TRUE } is Result.ERROR -> { - mapResult[sha1] = Result.ERROR + mapResult[imageSHA1] = Result.ERROR } is Result.FALSE -> { - mapResult[sha1] = Result.FALSE + mapResult[imageSHA1] = Result.FALSE } is Result.INVALID -> { - mapResult[sha1] = Result.INVALID + mapResult[imageSHA1] = Result.INVALID } is Result.NOTFOUND -> { - mapResult[sha1] = Result.NOTFOUND + mapResult[imageSHA1] = Result.NOTFOUND } } } else -> { - result = mapResult[sha1]!! + result = mapResult[imageSHA1]!! } } - if (result != Result.ERROR) { - insertIntoUploaded(imageSHA1, sha1, false, result is Result.TRUE) + if (result is Result.TRUE) { + // Original image found. + insertIntoUploaded(imageSHA1, sha1, result is Result.TRUE, false) + } else { + when { + mapResult[sha1] == null -> { + // Original image not found, query modified image. + result = + checkWhetherFileExistsOnCommonsUsingSHA1( + sha1, + ioDispatcher, + mediaClient, + ) + when (result) { + is Result.TRUE -> { + mapResult[sha1] = Result.TRUE + } + is Result.ERROR -> { + mapResult[sha1] = Result.ERROR + } + is Result.FALSE -> { + mapResult[sha1] = Result.FALSE + } + is Result.INVALID -> { + mapResult[sha1] = Result.INVALID + } + is Result.NOTFOUND -> { + mapResult[sha1] = Result.NOTFOUND + } + } + } + else -> { + result = mapResult[sha1]!! + } + } + if (result != Result.ERROR) { + insertIntoUploaded(imageSHA1, sha1, false, result is Result.TRUE) + } } } - } - val sharedPreferences: SharedPreferences = - context - .getSharedPreferences(ImageHelper.CUSTOM_SELECTOR_PREFERENCE_KEY, 0) - val showAlreadyActionedImages = - sharedPreferences.getBoolean( - ImageHelper.SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY, - true - ) - - if (mapHolderImage[holder] == image) { - if ((result is Result.TRUE) && showAlreadyActionedImages) { - holder.itemUploaded() - } else holder.itemNotUploaded() - - if ((existsInNotForUploadTable > 0) && showAlreadyActionedImages) { - holder.itemNotForUpload() - } else holder.itemForUpload() - } + val sharedPreferences: SharedPreferences = + context + .getSharedPreferences(ImageHelper.CUSTOM_SELECTOR_PREFERENCE_KEY, 0) + val showAlreadyActionedImages = + sharedPreferences.getBoolean( + ImageHelper.SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY, + true, + ) - if (uploadedContributionsList.isNotEmpty()) { - for (contribution in uploadedContributionsList ) { - if (contribution.contentUri == image.uri && showAlreadyActionedImages) { - holder.itemUploading() - break + if (mapHolderImage[holder] == image) { + if ((result is Result.TRUE) && showAlreadyActionedImages) { + holder.itemUploaded() } else { - holder.itemNotUploading() + holder.itemNotUploaded() + } + + if ((existsInNotForUploadTable > 0) && showAlreadyActionedImages) { + holder.itemNotForUpload() + } else { + holder.itemForUpload() + } + } + + if (uploadedContributionsList.isNotEmpty()) { + for (contribution in uploadedContributionsList) { + if (contribution.contentUri == image.uri && showAlreadyActionedImages) { + holder.itemUploading() + break + } else { + holder.itemNotUploading() + } } } } } - } - /** - * Finds out the next actionable image position - */ - suspend fun nextActionableImage( - allImages: List, ioDispatcher: CoroutineDispatcher, - defaultDispatcher: CoroutineDispatcher, - nextImagePosition: Int, - currentlyUploadingImages: List - ): Int { - var next: Int - // Traversing from given position to the end - for (i in nextImagePosition until allImages.size){ - val currentImage = allImages[i] - - if (currentlyUploadingImages.any { it.contentUri == currentImage.uri }) { - continue // Skip this image as it's currently being uploaded - } + /** + * Finds out the next actionable image position + */ + suspend fun nextActionableImage( + allImages: List, + ioDispatcher: CoroutineDispatcher, + defaultDispatcher: CoroutineDispatcher, + nextImagePosition: Int, + currentlyUploadingImages: List, + ): Int { + var next: Int + // Traversing from given position to the end + for (i in nextImagePosition until allImages.size) { + val currentImage = allImages[i] + + if (currentlyUploadingImages.any { it.contentUri == currentImage.uri }) { + continue // Skip this image as it's currently being uploaded + } - val imageSHA1: String = when (mapImageSHA1[currentImage.uri] != null) { - true -> mapImageSHA1[currentImage.uri]!! - else -> CustomSelectorUtils.getImageSHA1( - currentImage.uri, - ioDispatcher, - fileUtilsWrapper, - context.contentResolver - ) - } - next = notForUploadStatusDao.find(imageSHA1) + val imageSHA1: String = + when (mapImageSHA1[currentImage.uri] != null) { + true -> mapImageSHA1[currentImage.uri]!! + else -> + CustomSelectorUtils.getImageSHA1( + currentImage.uri, + ioDispatcher, + fileUtilsWrapper, + context.contentResolver, + ) + } + next = notForUploadStatusDao.find(imageSHA1) - // After checking the image in the not for upload table, if the image is present then - // skips the image and moves to next image for checking - if(next > 0){ - continue + // After checking the image in the not for upload table, if the image is present then + // skips the image and moves to next image for checking + if (next > 0) { + continue - // Otherwise checks in already uploaded table - } else { - next = uploadedStatusDao.findByImageSHA1(imageSHA1, true) - - // If the image is not present in the already uploaded table, checks for its - // modified SHA1 in already uploaded table - if (next <= 0) { - val modifiedImageSha1 = getSHA1(currentImage, defaultDispatcher) - next = uploadedStatusDao.findByModifiedImageSHA1( - modifiedImageSha1, - true - ) + // Otherwise checks in already uploaded table + } else { + next = uploadedStatusDao.findByImageSHA1(imageSHA1, true) - // If the modified image SHA1 is not present in the already uploaded table, - // returns the position as next actionable image position + // If the image is not present in the already uploaded table, checks for its + // modified SHA1 in already uploaded table if (next <= 0) { - return i + val modifiedImageSha1 = getSHA1(currentImage, defaultDispatcher) + next = + uploadedStatusDao.findByModifiedImageSHA1( + modifiedImageSha1, + true, + ) + + // If the modified image SHA1 is not present in the already uploaded table, + // returns the position as next actionable image position + if (next <= 0) { + return i - // If present in the db then skips iteration for the image and moves to the next - // for checking + // If present in the db then skips iteration for the image and moves to the next + // for checking + } else { + continue + } + + // If present in the db then skips iteration for the image and moves to the next + // for checking } else { continue } - - // If present in the db then skips iteration for the image and moves to the next - // for checking - } else { - continue } } + return -1 } - return -1 - } - /** - * Get SHA1, return SHA1 if available, otherwise generate and store the SHA1. - * - * @return sha1 of the image - */ - suspend fun getSHA1(image: Image, defaultDispatcher: CoroutineDispatcher): String { - mapModifiedImageSHA1[image]?.let{ - return it + /** + * Get SHA1, return SHA1 if available, otherwise generate and store the SHA1. + * + * @return sha1 of the image + */ + suspend fun getSHA1( + image: Image, + defaultDispatcher: CoroutineDispatcher, + ): String { + mapModifiedImageSHA1[image]?.let { + return it + } + val sha1 = + CustomSelectorUtils + .generateModifiedSHA1( + image, + defaultDispatcher, + context, + fileProcessor, + fileUtilsWrapper, + ) + mapModifiedImageSHA1[image] = sha1 + return sha1 } - val sha1 = CustomSelectorUtils - .generateModifiedSHA1(image, - defaultDispatcher, - context, - fileProcessor, - fileUtilsWrapper - ) - mapModifiedImageSHA1[image] = sha1; - return sha1; - } - /** - * Get the uploaded status entry from the database. - */ - suspend fun getFromUploaded(imageSha1:String): UploadedStatus? { - return uploadedStatusDao.getUploadedFromImageSHA1(imageSha1) - } + /** + * Get the uploaded status entry from the database. + */ + suspend fun getFromUploaded(imageSha1: String): UploadedStatus? = uploadedStatusDao.getUploadedFromImageSHA1(imageSha1) - /** - * Insert into uploaded status table. - */ - suspend fun insertIntoUploaded(imageSha1:String, modifiedImageSha1:String, imageResult:Boolean, modifiedImageResult: Boolean){ - uploadedStatusDao.insertUploaded( - UploadedStatus( - imageSha1, - modifiedImageSha1, - imageResult, - modifiedImageResult + /** + * Insert into uploaded status table. + */ + suspend fun insertIntoUploaded( + imageSha1: String, + modifiedImageSha1: String, + imageResult: Boolean, + modifiedImageResult: Boolean, + ) { + uploadedStatusDao.insertUploaded( + UploadedStatus( + imageSha1, + modifiedImageSha1, + imageResult, + modifiedImageResult, + ), ) - ) - } + } - /** - * Get result data from database. - */ - fun getResultFromUploadedStatus(uploadedStatus: UploadedStatus): Result { - if (uploadedStatus.imageResult || uploadedStatus.modifiedImageResult) { - return Result.TRUE - } else { - uploadedStatus.lastUpdated?.let { - val duration = Calendar.getInstance().time.time - it.time - if (TimeUnit.MILLISECONDS.toDays(duration) < INVALIDATE_DAY_COUNT) { - return Result.FALSE + /** + * Get result data from database. + */ + fun getResultFromUploadedStatus(uploadedStatus: UploadedStatus): Result { + if (uploadedStatus.imageResult || uploadedStatus.modifiedImageResult) { + return Result.TRUE + } else { + uploadedStatus.lastUpdated?.let { + val duration = Calendar.getInstance().time.time - it.time + if (TimeUnit.MILLISECONDS.toDays(duration) < INVALIDATE_DAY_COUNT) { + return Result.FALSE + } } } + return Result.INVALID } - return Result.INVALID - } - /** - * Sealed Result class. - */ - sealed class Result { - object TRUE : Result() - object FALSE : Result() - object INVALID : Result() - object NOTFOUND : Result() - object ERROR : Result() - } + /** + * Sealed Result class. + */ + sealed class Result { + object TRUE : Result() + + object FALSE : Result() + + object INVALID : Result() + + object NOTFOUND : Result() + + object ERROR : Result() + } - /** - * Companion Object - */ - companion object { /** - * Invalidate Day count. - * False Database Entries are invalid after INVALIDATE_DAY_COUNT and need to be re-queried. + * Companion Object */ - const val INVALIDATE_DAY_COUNT: Long = 7 + companion object { + /** + * Invalidate Day count. + * False Database Entries are invalid after INVALIDATE_DAY_COUNT and need to be re-queried. + */ + const val INVALIDATE_DAY_COUNT: Long = 7 + } } - -} diff --git a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt index 594f087c83..71947fa1a6 100644 --- a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt +++ b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt @@ -5,7 +5,10 @@ import androidx.room.RoomDatabase import androidx.room.TypeConverters import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.ContributionDao -import fr.free.nrw.commons.customselector.database.* +import fr.free.nrw.commons.customselector.database.NotForUploadStatus +import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao +import fr.free.nrw.commons.customselector.database.UploadedStatus +import fr.free.nrw.commons.customselector.database.UploadedStatusDao import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.PlaceDao import fr.free.nrw.commons.review.ReviewDao @@ -17,13 +20,22 @@ import fr.free.nrw.commons.upload.depicts.DepictsDao * The database for accessing the respective DAOs * */ -@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class, NotForUploadStatus::class, ReviewEntity::class, Place::class], version = 18, exportSchema = false) +@Database( + entities = [Contribution::class, Depicts::class, UploadedStatus::class, NotForUploadStatus::class, ReviewEntity::class, Place::class], + version = 18, + exportSchema = false, +) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun contributionDao(): ContributionDao + abstract fun PlaceDao(): PlaceDao - abstract fun DepictsDao(): DepictsDao; - abstract fun UploadedStatusDao(): UploadedStatusDao; + + abstract fun DepictsDao(): DepictsDao + + abstract fun UploadedStatusDao(): UploadedStatusDao + abstract fun NotForUploadStatusDao(): NotForUploadStatusDao + abstract fun ReviewDao(): ReviewDao } diff --git a/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt b/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt index 3ec9a9723a..0dbdf71ae0 100644 --- a/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt @@ -1,6 +1,5 @@ package fr.free.nrw.commons.description - import android.app.ProgressDialog import android.content.Intent import android.os.Bundle @@ -29,11 +28,12 @@ import io.reactivex.schedulers.Schedulers import timber.log.Timber import javax.inject.Inject - /** * Activity for populating and editing existing description and caption */ -class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventListener { +class DescriptionEditActivity : + BaseActivity(), + UploadMediaDetailAdapter.EventListener { /** * Adapter for showing UploadMediaDetail in the activity */ @@ -70,7 +70,7 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi private lateinit var binding: ActivityDescriptionEditBinding - private val REQUEST_CODE_FOR_VOICE_INPUT = 1213 + private val requestCodeForVoiceInput = 1213 private var descriptionAndCaptions: ArrayList? = null @@ -78,7 +78,6 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi @Inject lateinit var sessionManager: SessionManager - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -110,12 +109,17 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi * @param descriptionAndCaptions list of description and caption */ private fun initRecyclerView(descriptionAndCaptions: ArrayList?) { - uploadMediaDetailAdapter = UploadMediaDetailAdapter(this, - savedLanguageValue, descriptionAndCaptions, recentLanguagesDao) + uploadMediaDetailAdapter = + UploadMediaDetailAdapter( + this, + savedLanguageValue, + descriptionAndCaptions, + recentLanguagesDao, + ) uploadMediaDetailAdapter.setCallback { titleStringID: Int, messageStringId: Int -> showInfoAlert( titleStringID, - messageStringId + messageStringId, ) } uploadMediaDetailAdapter.setEventListener(this) @@ -129,11 +133,17 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi * @param titleStringID Title ID * @param messageStringId Message ID */ - private fun showInfoAlert(titleStringID: Int, messageStringId: Int) { + private fun showInfoAlert( + titleStringID: Int, + messageStringId: Int, + ) { showAlertDialog( - this, getString(titleStringID), - getString(messageStringId), getString(android.R.string.ok), - null, true + this, + getString(titleStringID), + getString(messageStringId), + getString(android.R.string.ok), + null, + true, ) } @@ -144,13 +154,13 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi */ override fun addLanguage() { val uploadMediaDetail = UploadMediaDetail() - uploadMediaDetail.isManuallyAdded = true //This was manually added by the user + uploadMediaDetail.isManuallyAdded = true // This was manually added by the user uploadMediaDetailAdapter.addDescription(uploadMediaDetail) rvDescriptions!!.smoothScrollToPosition(uploadMediaDetailAdapter.itemCount - 1) } private fun onBackButtonClicked(view: View) { - onBackPressedDispatcher.onBackPressed() + onBackPressedDispatcher.onBackPressed() } private fun onSubmitButtonClicked(view: View) { @@ -174,10 +184,11 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi val descriptionStart = wikiText!!.substring(0, descriptionIndex + 12) val descriptionToEnd = wikiText!!.substring(descriptionIndex + 12) val descriptionEndIndex = descriptionToEnd.indexOf("\n") - val descriptionEnd = wikiText!!.substring( - descriptionStart.length - + descriptionEndIndex - ) + val descriptionEnd = + wikiText!!.substring( + descriptionStart.length + + descriptionEndIndex, + ) buffer.append(descriptionStart) for (i in uploadMediaDetails.indices) { val uploadDetails = uploadMediaDetails[i] @@ -203,65 +214,72 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi * @param updatedWikiText updated wiki text * @param uploadMediaDetails descriptions and captions */ - private fun editDescription(media : Media, updatedWikiText : String, uploadMediaDetails : ArrayList){ - + private fun editDescription( + media: Media, + updatedWikiText: String, + uploadMediaDetails: ArrayList, + ) { try { - descriptionEditHelper?.addDescription( - applicationContext, media, - updatedWikiText - ) - ?.subscribeOn(Schedulers.io()) + descriptionEditHelper + ?.addDescription( + applicationContext, + media, + updatedWikiText, + )?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(Consumer { s: Boolean? -> Timber.d("Descriptions are added.") })?.let { + ?.subscribe(Consumer { s: Boolean? -> Timber.d("Descriptions are added.") }) + ?.let { compositeDisposable.add( - it + it, ) } - } catch (e : InvalidLoginTokenException) { + } catch (e: InvalidLoginTokenException) { val username: String? = sessionManager?.userName - val logoutListener = CommonsApplication.BaseLogoutListener( - this, - getString(R.string.invalid_login_message), - username - ) + val logoutListener = + CommonsApplication.BaseLogoutListener( + this, + getString(R.string.invalid_login_message), + username, + ) val commonsApplication = CommonsApplication.getInstance() - if (commonsApplication != null ){ - commonsApplication.clearApplicationData(this,logoutListener) + if (commonsApplication != null) { + commonsApplication.clearApplicationData(this, logoutListener) } } - val updatedCaptions = LinkedHashMap() for (mediaDetail in uploadMediaDetails) { try { compositeDisposable.add( - descriptionEditHelper!!.addCaption( - applicationContext, media, - mediaDetail.languageCode, mediaDetail.captionText - ) - .subscribeOn(Schedulers.io()) + descriptionEditHelper!! + .addCaption( + applicationContext, + media, + mediaDetail.languageCode, + mediaDetail.captionText, + ).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { s: Boolean? -> updatedCaptions[mediaDetail.languageCode!!] = mediaDetail.captionText media.captions = updatedCaptions Timber.d("Caption is added.") - }) - } - catch (e : InvalidLoginTokenException) { - val username = sessionManager.userName - val logoutListener = CommonsApplication.BaseLogoutListener( - this, - getString(R.string.invalid_login_message), - username + }, ) + } catch (e: InvalidLoginTokenException) { + val username = sessionManager.userName + val logoutListener = + CommonsApplication.BaseLogoutListener( + this, + getString(R.string.invalid_login_message), + username, + ) val commonsApplication = CommonsApplication.getInstance() - if (commonsApplication != null ){ - commonsApplication.clearApplicationData(this,logoutListener) + if (commonsApplication != null) { + commonsApplication.clearApplicationData(this, logoutListener) } } - } } @@ -274,23 +292,29 @@ class DescriptionEditActivity : BaseActivity(), UploadMediaDetailAdapter.EventLi progressDialog!!.show() } - override - fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_CODE_FOR_VOICE_INPUT) { + override fun onActivityResult( + requestCode: Int, + resultCode: Int, + data: Intent?, + ) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == requestCodeForVoiceInput) { if (resultCode == RESULT_OK && data != null) { - val result = data.getStringArrayListExtra( RecognizerIntent.EXTRA_RESULTS ) - uploadMediaDetailAdapter.handleSpeechResult(result!![0]) } - else { Timber.e("Error %s", resultCode) } + val result = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS) + uploadMediaDetailAdapter.handleSpeechResult(result!![0]) + } else { + Timber.e("Error %s", resultCode) + } } } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putParcelableArrayList(LIST_OF_DESCRIPTION_AND_CAPTION, uploadMediaDetailAdapter.items as ArrayList) outState.putString(WIKITEXT, wikiText) outState.putString(Prefs.DESCRIPTION_LANGUAGE, savedLanguageValue) - //save Media + // save Media outState.putParcelable("media", media) } } diff --git a/app/src/main/java/fr/free/nrw/commons/description/EditDescriptionConstants.kt b/app/src/main/java/fr/free/nrw/commons/description/EditDescriptionConstants.kt index 7502a10edc..0c0b546714 100644 --- a/app/src/main/java/fr/free/nrw/commons/description/EditDescriptionConstants.kt +++ b/app/src/main/java/fr/free/nrw/commons/description/EditDescriptionConstants.kt @@ -6,5 +6,5 @@ package fr.free.nrw.commons.description object EditDescriptionConstants { const val LIST_OF_DESCRIPTION_AND_CAPTION = "description.descriptionAndCaption" const val WIKITEXT = "description.wikiText" - const val UPDATED_WIKITEXT = "description.updatedWikiText"; -} \ No newline at end of file + const val UPDATED_WIKITEXT = "description.updatedWikiText" +} diff --git a/app/src/main/java/fr/free/nrw/commons/di/ExploreMapFragmentModule.kt b/app/src/main/java/fr/free/nrw/commons/di/ExploreMapFragmentModule.kt index fe037d3e2a..39e01371a5 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/ExploreMapFragmentModule.kt +++ b/app/src/main/java/fr/free/nrw/commons/di/ExploreMapFragmentModule.kt @@ -6,8 +6,7 @@ import dagger.Provides import fr.free.nrw.commons.explore.map.ExploreMapFragment @Module -class ExploreMapFragmentModule{ - +class ExploreMapFragmentModule { @Provides fun ExploreMapFragment.providesActivity(): Activity = activity!! } diff --git a/app/src/main/java/fr/free/nrw/commons/di/NearbyParentFragmentModule.kt b/app/src/main/java/fr/free/nrw/commons/di/NearbyParentFragmentModule.kt index 51b29621db..c7c9840ef2 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/NearbyParentFragmentModule.kt +++ b/app/src/main/java/fr/free/nrw/commons/di/NearbyParentFragmentModule.kt @@ -6,8 +6,7 @@ import dagger.Provides import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment @Module -class NearbyParentFragmentModule{ - +class NearbyParentFragmentModule { @Provides fun NearbyParentFragment.providesActivity(): Activity = activity!! } diff --git a/app/src/main/java/fr/free/nrw/commons/edit/EditActivity.kt b/app/src/main/java/fr/free/nrw/commons/edit/EditActivity.kt index a04e0c3232..d585a99e91 100644 --- a/app/src/main/java/fr/free/nrw/commons/edit/EditActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/edit/EditActivity.kt @@ -44,31 +44,32 @@ class EditActivity : AppCompatActivity() { imageUri = intent.getStringExtra("image") ?: "" vm = ViewModelProvider(this).get(EditViewModel::class.java) val sourceExif = imageUri.toUri().path?.let { ExifInterface(it) } - val exifTags = arrayOf( - ExifInterface.TAG_APERTURE, - ExifInterface.TAG_DATETIME, - ExifInterface.TAG_EXPOSURE_TIME, - ExifInterface.TAG_FLASH, - ExifInterface.TAG_FOCAL_LENGTH, - ExifInterface.TAG_GPS_ALTITUDE, - ExifInterface.TAG_GPS_ALTITUDE_REF, - ExifInterface.TAG_GPS_DATESTAMP, - ExifInterface.TAG_GPS_LATITUDE, - ExifInterface.TAG_GPS_LATITUDE_REF, - ExifInterface.TAG_GPS_LONGITUDE, - ExifInterface.TAG_GPS_LONGITUDE_REF, - ExifInterface.TAG_GPS_PROCESSING_METHOD, - ExifInterface.TAG_GPS_TIMESTAMP, - ExifInterface.TAG_IMAGE_LENGTH, - ExifInterface.TAG_IMAGE_WIDTH, - ExifInterface.TAG_ISO, - ExifInterface.TAG_MAKE, - ExifInterface.TAG_MODEL, - ExifInterface.TAG_ORIENTATION, - ExifInterface.TAG_WHITE_BALANCE, - ExifInterface.WHITEBALANCE_AUTO, - ExifInterface.WHITEBALANCE_MANUAL - ) + val exifTags = + arrayOf( + ExifInterface.TAG_APERTURE, + ExifInterface.TAG_DATETIME, + ExifInterface.TAG_EXPOSURE_TIME, + ExifInterface.TAG_FLASH, + ExifInterface.TAG_FOCAL_LENGTH, + ExifInterface.TAG_GPS_ALTITUDE, + ExifInterface.TAG_GPS_ALTITUDE_REF, + ExifInterface.TAG_GPS_DATESTAMP, + ExifInterface.TAG_GPS_LATITUDE, + ExifInterface.TAG_GPS_LATITUDE_REF, + ExifInterface.TAG_GPS_LONGITUDE, + ExifInterface.TAG_GPS_LONGITUDE_REF, + ExifInterface.TAG_GPS_PROCESSING_METHOD, + ExifInterface.TAG_GPS_TIMESTAMP, + ExifInterface.TAG_IMAGE_LENGTH, + ExifInterface.TAG_IMAGE_WIDTH, + ExifInterface.TAG_ISO, + ExifInterface.TAG_MAKE, + ExifInterface.TAG_MODEL, + ExifInterface.TAG_ORIENTATION, + ExifInterface.TAG_WHITE_BALANCE, + ExifInterface.WHITEBALANCE_AUTO, + ExifInterface.WHITEBALANCE_MANUAL, + ) for (tag in exifTags) { val attribute = sourceExif?.getAttribute(tag.toString()) sourceExifAttributeList.add(Pair(tag.toString(), attribute)) @@ -87,37 +88,38 @@ class EditActivity : AppCompatActivity() { private fun init() { binding.iv.adjustViewBounds = true binding.iv.scaleType = ImageView.ScaleType.MATRIX - binding.iv.post(Runnable { - val options = BitmapFactory.Options() - options.inJustDecodeBounds = true - BitmapFactory.decodeFile(imageUri, options) - - val bitmapWidth = options.outWidth - val bitmapHeight = options.outHeight - - // Check if the bitmap dimensions exceed a certain threshold - val maxBitmapSize = 2000 // Set your maximum size here - if (bitmapWidth > maxBitmapSize || bitmapHeight > maxBitmapSize) { - val scaleFactor = calculateScaleFactor(bitmapWidth, bitmapHeight, maxBitmapSize) - options.inSampleSize = scaleFactor - options.inJustDecodeBounds = false - val scaledBitmap = BitmapFactory.decodeFile(imageUri, options) - binding.iv.setImageBitmap(scaledBitmap) - // Update the ImageView with the scaled bitmap - val scale = binding.iv.measuredWidth.toFloat() / scaledBitmap.width.toFloat() - binding.iv.layoutParams.height = (scale * scaledBitmap.height).toInt() - binding.iv.imageMatrix = scaleMatrix(scale, scale) - } else { - - options.inJustDecodeBounds = false - val bitmap = BitmapFactory.decodeFile(imageUri, options) - binding.iv.setImageBitmap(bitmap) - - val scale = binding.iv.measuredWidth.toFloat() / bitmapWidth.toFloat() - binding.iv.layoutParams.height = (scale * bitmapHeight).toInt() - binding.iv.imageMatrix = scaleMatrix(scale, scale) - } - }) + binding.iv.post( + Runnable { + val options = BitmapFactory.Options() + options.inJustDecodeBounds = true + BitmapFactory.decodeFile(imageUri, options) + + val bitmapWidth = options.outWidth + val bitmapHeight = options.outHeight + + // Check if the bitmap dimensions exceed a certain threshold + val maxBitmapSize = 2000 // Set your maximum size here + if (bitmapWidth > maxBitmapSize || bitmapHeight > maxBitmapSize) { + val scaleFactor = calculateScaleFactor(bitmapWidth, bitmapHeight, maxBitmapSize) + options.inSampleSize = scaleFactor + options.inJustDecodeBounds = false + val scaledBitmap = BitmapFactory.decodeFile(imageUri, options) + binding.iv.setImageBitmap(scaledBitmap) + // Update the ImageView with the scaled bitmap + val scale = binding.iv.measuredWidth.toFloat() / scaledBitmap.width.toFloat() + binding.iv.layoutParams.height = (scale * scaledBitmap.height).toInt() + binding.iv.imageMatrix = scaleMatrix(scale, scale) + } else { + options.inJustDecodeBounds = false + val bitmap = BitmapFactory.decodeFile(imageUri, options) + binding.iv.setImageBitmap(bitmap) + + val scale = binding.iv.measuredWidth.toFloat() / bitmapWidth.toFloat() + binding.iv.layoutParams.height = (scale * bitmapHeight).toInt() + binding.iv.imageMatrix = scaleMatrix(scale, scale) + } + }, + ) binding.rotateBtn.setOnClickListener { animateImageHeight() } @@ -138,8 +140,16 @@ class EditActivity : AppCompatActivity() { * further rotation actions. */ private fun animateImageHeight() { - val drawableWidth: Float = binding.iv.getDrawable().getIntrinsicWidth().toFloat() - val drawableHeight: Float = binding.iv.getDrawable().getIntrinsicHeight().toFloat() + val drawableWidth: Float = + binding.iv + .getDrawable() + .getIntrinsicWidth() + .toFloat() + val drawableHeight: Float = + binding.iv + .getDrawable() + .getIntrinsicHeight() + .toFloat() val viewWidth: Float = binding.iv.getMeasuredWidth().toFloat() val viewHeight: Float = binding.iv.getMeasuredHeight().toFloat() val rotation = imageRotation % 360 @@ -152,7 +162,6 @@ class EditActivity : AppCompatActivity() { Timber.d("Rotation $rotation") Timber.d("new Rotation $newRotation") - if (rotation == 0 || rotation == 180) { imageScale = viewWidth / drawableWidth newImageScale = viewWidth / drawableHeight @@ -169,23 +178,24 @@ class EditActivity : AppCompatActivity() { animator.interpolator = AccelerateDecelerateInterpolator() - animator.addListener(object : AnimatorListener { - override fun onAnimationStart(animation: Animator) { - binding.rotateBtn.setEnabled(false) - } - - override fun onAnimationEnd(animation: Animator) { - imageRotation = newRotation % 360 - binding.rotateBtn.setEnabled(true) - } + animator.addListener( + object : AnimatorListener { + override fun onAnimationStart(animation: Animator) { + binding.rotateBtn.setEnabled(false) + } - override fun onAnimationCancel(animation: Animator) { - } + override fun onAnimationEnd(animation: Animator) { + imageRotation = newRotation % 360 + binding.rotateBtn.setEnabled(true) + } - override fun onAnimationRepeat(animation: Animator) { - } + override fun onAnimationCancel(animation: Animator) { + } - }) + override fun onAnimationRepeat(animation: Animator) { + } + }, + ) animator.addUpdateListener { animation -> val animVal = animation.animatedValue as Float @@ -195,20 +205,21 @@ class EditActivity : AppCompatActivity() { val animatedScale = complementaryAnimVal * imageScale + animVal * newImageScale val animatedRotation = complementaryAnimVal * rotation + animVal * newRotation binding.iv.getLayoutParams().height = animatedHeight - val matrix: Matrix = rotationMatrix( - animatedRotation, - drawableWidth / 2, - drawableHeight / 2 - ) + val matrix: Matrix = + rotationMatrix( + animatedRotation, + drawableWidth / 2, + drawableHeight / 2, + ) matrix.postScale( animatedScale, animatedScale, drawableWidth / 2, - drawableHeight / 2 + drawableHeight / 2, ) matrix.postTranslate( -(drawableWidth - binding.iv.getMeasuredWidth()) / 2, - -(drawableHeight - binding.iv.getMeasuredHeight()) / 2 + -(drawableHeight - binding.iv.getMeasuredHeight()) / 2, ) binding.iv.setImageMatrix(matrix) binding.iv.requestLayout() @@ -228,11 +239,9 @@ class EditActivity : AppCompatActivity() { * as a result, and finishes the current activity. */ fun getRotatedImage() { - val filePath = imageUri.toUri().path val file = filePath?.let { File(it) } - val rotatedImage = file?.let { vm.rotateImage(imageRotation, it) } if (rotatedImage == null) { Toast.makeText(this, "Failed to rotate to image", Toast.LENGTH_LONG).show() @@ -243,9 +252,9 @@ class EditActivity : AppCompatActivity() { copyExifData(editedImageExif) } val resultIntent = Intent() - resultIntent.putExtra("editedImageFilePath", rotatedImage?.toUri()?.path ?: "Error"); - setResult(RESULT_OK, resultIntent); - finish(); + resultIntent.putExtra("editedImageFilePath", rotatedImage?.toUri()?.path ?: "Error") + setResult(RESULT_OK, resultIntent) + finish() } /** @@ -257,7 +266,6 @@ class EditActivity : AppCompatActivity() { * @param editedImageExif The ExifInterface object for the edited image. */ private fun copyExifData(editedImageExif: ExifInterface?) { - for (attr in sourceExifAttributeList) { Log.d("Tag is ${attr.first}", "Value is ${attr.second}") editedImageExif!!.setAttribute(attr.first, attr.second) @@ -282,7 +290,11 @@ class EditActivity : AppCompatActivity() { * The scale factor ensures that the scaled bitmap will fit within the maximum size * while maintaining aspect ratio. */ - private fun calculateScaleFactor(originalWidth: Int, originalHeight: Int, maxSize: Int): Int { + private fun calculateScaleFactor( + originalWidth: Int, + originalHeight: Int, + maxSize: Int, + ): Int { var scaleFactor = 1 if (originalWidth > maxSize || originalHeight > maxSize) { @@ -295,7 +307,4 @@ class EditActivity : AppCompatActivity() { return scaleFactor } - - - -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/edit/EditViewModel.kt b/app/src/main/java/fr/free/nrw/commons/edit/EditViewModel.kt index 80db0f1ab1..9bf6f93145 100644 --- a/app/src/main/java/fr/free/nrw/commons/edit/EditViewModel.kt +++ b/app/src/main/java/fr/free/nrw/commons/edit/EditViewModel.kt @@ -9,8 +9,7 @@ import java.io.File * This ViewModel class is responsible for managing image editing operations, such as * rotating images. It utilizes a TransformImage implementation to perform image transformations. */ -class EditViewModel() : ViewModel() { - +class EditViewModel : ViewModel() { // Ideally should be injected using DI private val transformImage: TransformImage = TransformImageImpl() @@ -21,7 +20,8 @@ class EditViewModel() : ViewModel() { * @param imageFile The File representing the image to be rotated. * @return The rotated image File, or null if the rotation operation fails. */ - fun rotateImage(degree: Int, imageFile: File): File? { - return transformImage.rotateImage(imageFile, degree) - } -} \ No newline at end of file + fun rotateImage( + degree: Int, + imageFile: File, + ): File? = transformImage.rotateImage(imageFile, degree) +} diff --git a/app/src/main/java/fr/free/nrw/commons/edit/TransformImage.kt b/app/src/main/java/fr/free/nrw/commons/edit/TransformImage.kt index 4c3607a8ed..586df0daf0 100644 --- a/app/src/main/java/fr/free/nrw/commons/edit/TransformImage.kt +++ b/app/src/main/java/fr/free/nrw/commons/edit/TransformImage.kt @@ -9,7 +9,6 @@ import java.io.File * implementations to provide specific functionality for tasks like rotating images. */ interface TransformImage { - /** * Rotates the specified image file by the given degree. * @@ -17,5 +16,8 @@ interface TransformImage { * @param degree The degree by which to rotate the image. * @return The rotated image File, or null if the rotation operation fails. */ - fun rotateImage(imageFile: File, degree : Int ):File? -} \ No newline at end of file + fun rotateImage( + imageFile: File, + degree: Int, + ): File? +} diff --git a/app/src/main/java/fr/free/nrw/commons/edit/TransformImageImpl.kt b/app/src/main/java/fr/free/nrw/commons/edit/TransformImageImpl.kt index fb96ca0443..b596196915 100644 --- a/app/src/main/java/fr/free/nrw/commons/edit/TransformImageImpl.kt +++ b/app/src/main/java/fr/free/nrw/commons/edit/TransformImageImpl.kt @@ -15,8 +15,7 @@ import java.io.FileOutputStream * function for rotating images by a specified degree using the LLJTran library. Right now it reads * the input image file, performs the rotation, and saves the rotated image to a new file. */ -class TransformImageImpl() : TransformImage { - +class TransformImageImpl : TransformImage { /** * Rotates the specified image file by the given degree. * @@ -24,46 +23,50 @@ class TransformImageImpl() : TransformImage { * @param degree The degree by which to rotate the image. * @return The rotated image File, or null if the rotation operation fails. */ - override fun rotateImage(imageFile: File, degree : Int): File? { - + override fun rotateImage( + imageFile: File, + degree: Int, + ): File? { Timber.tag("Trying to rotate image").d("Starting") - val path = Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_DOWNLOADS - ) + val path = + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DOWNLOADS, + ) val imagePath = System.currentTimeMillis() val file: File = File(path, "$imagePath.jpg") val output = file - val rotated = try { - val lljTran = LLJTran(imageFile) - lljTran.read( - LLJTran.READ_ALL, - false, - ) // This could throw an LLJTranException. I am not catching it for now... Let's see. - lljTran.transform( - when(degree){ - 90 -> LLJTran.ROT_90 - 180 -> LLJTran.ROT_180 - 270 -> LLJTran.ROT_270 - else -> { - LLJTran.ROT_90 - } - }, - LLJTran.OPT_DEFAULTS or LLJTran.OPT_XFORM_ORIENTATION - ) - BufferedOutputStream(FileOutputStream(output)).use { writer -> - lljTran.save(writer, LLJTran.OPT_WRITE_ALL ) + val rotated = + try { + val lljTran = LLJTran(imageFile) + lljTran.read( + LLJTran.READ_ALL, + false, + ) // This could throw an LLJTranException. I am not catching it for now... Let's see. + lljTran.transform( + when (degree) { + 90 -> LLJTran.ROT_90 + 180 -> LLJTran.ROT_180 + 270 -> LLJTran.ROT_270 + else -> { + LLJTran.ROT_90 + } + }, + LLJTran.OPT_DEFAULTS or LLJTran.OPT_XFORM_ORIENTATION, + ) + BufferedOutputStream(FileOutputStream(output)).use { writer -> + lljTran.save(writer, LLJTran.OPT_WRITE_ALL) + } + lljTran.freeMemory() + true + } catch (e: LLJTranException) { + Timber.tag("Error").d(e) + return null + false } - lljTran.freeMemory() - true - } catch (e: LLJTranException) { - Timber.tag("Error").d(e) - return null - false - } if (rotated) { Timber.tag("Done rotating image").d("Done") diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchModule.kt b/app/src/main/java/fr/free/nrw/commons/explore/SearchModule.kt index e0a9d41a01..4323613229 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchModule.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchModule.kt @@ -15,14 +15,11 @@ import fr.free.nrw.commons.explore.media.SearchMediaFragmentPresenterImpl @Module abstract class SearchModule { @Binds - abstract fun SearchDepictionsFragmentPresenterImpl.bindsSearchDepictionsFragmentPresenter() - : SearchDepictionsFragmentPresenter + abstract fun SearchDepictionsFragmentPresenterImpl.bindsSearchDepictionsFragmentPresenter(): SearchDepictionsFragmentPresenter @Binds - abstract fun SearchCategoriesFragmentPresenterImpl.bindsSearchCategoriesFragmentPresenter() - : SearchCategoriesFragmentPresenter + abstract fun SearchCategoriesFragmentPresenterImpl.bindsSearchCategoriesFragmentPresenter(): SearchCategoriesFragmentPresenter @Binds - abstract fun SearchMediaFragmentPresenterImpl.bindsSearchMediaFragmentPresenter() - : SearchMediaFragmentPresenter + abstract fun SearchMediaFragmentPresenterImpl.bindsSearchMediaFragmentPresenter(): SearchMediaFragmentPresenter } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/CategoriesModule.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/CategoriesModule.kt index 394b88ca6c..7d66f59de0 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/CategoriesModule.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/CategoriesModule.kt @@ -9,19 +9,14 @@ import fr.free.nrw.commons.explore.categories.parent.ParentCategoriesPresenterIm import fr.free.nrw.commons.explore.categories.sub.SubCategoriesPresenter import fr.free.nrw.commons.explore.categories.sub.SubCategoriesPresenterImpl - @Module abstract class CategoriesModule { - @Binds - abstract fun CategoryMediaPresenterImpl.bindsCategoryMediaPresenter() - : CategoryMediaPresenter + abstract fun CategoryMediaPresenterImpl.bindsCategoryMediaPresenter(): CategoryMediaPresenter @Binds - abstract fun SubCategoriesPresenterImpl.bindsSubCategoriesPresenter() - : SubCategoriesPresenter + abstract fun SubCategoriesPresenterImpl.bindsSubCategoriesPresenter(): SubCategoriesPresenter @Binds - abstract fun ParentCategoriesPresenterImpl.bindsParentCategoriesPresenter() - : ParentCategoriesPresenter + abstract fun ParentCategoriesPresenterImpl.bindsParentCategoriesPresenter(): ParentCategoriesPresenter } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/PageableCategoryFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/PageableCategoryFragment.kt index 54a97cb093..89d2bd817f 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/PageableCategoryFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/PageableCategoryFragment.kt @@ -4,7 +4,6 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.category.CategoryDetailsActivity import fr.free.nrw.commons.explore.paging.BasePagingFragment - abstract class PageableCategoryFragment : BasePagingFragment() { override val errorTextId: Int = R.string.error_loading_categories override val pagedListAdapter by lazy { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/PagedCategoriesAdapter.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/PagedCategoriesAdapter.kt index 6710f8dc81..04edcc1e22 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/PagedCategoriesAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/PagedCategoriesAdapter.kt @@ -8,31 +8,44 @@ import androidx.recyclerview.widget.RecyclerView import fr.free.nrw.commons.category.CATEGORY_PREFIX import fr.free.nrw.commons.databinding.ItemRecentSearchesBinding -class PagedSearchCategoriesAdapter(private val onCategoryClicked: (String) -> Unit) : - PagedListAdapter(PagedSearchCategoriesDiffUtilCallback) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = CategoryItemViewHolder( - ItemRecentSearchesBinding.inflate(LayoutInflater.from(parent.context), parent, false) +class PagedSearchCategoriesAdapter( + private val onCategoryClicked: (String) -> Unit, +) : PagedListAdapter(PagedSearchCategoriesDiffUtilCallback) { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ) = CategoryItemViewHolder( + ItemRecentSearchesBinding.inflate(LayoutInflater.from(parent.context), parent, false), ) - override fun onBindViewHolder(holder: CategoryItemViewHolder, position: Int) { + override fun onBindViewHolder( + holder: CategoryItemViewHolder, + position: Int, + ) { holder.bind(getItem(position)!!, onCategoryClicked) } } class CategoryItemViewHolder( - private val binding: ItemRecentSearchesBinding + private val binding: ItemRecentSearchesBinding, ) : RecyclerView.ViewHolder(binding.root) { - fun bind(item: String, onCategoryClicked: (String) -> Unit) = with(binding) { + fun bind( + item: String, + onCategoryClicked: (String) -> Unit, + ) = with(binding) { root.setOnClickListener { onCategoryClicked(item) } textView1.text = item.substringAfter(CATEGORY_PREFIX) } } private object PagedSearchCategoriesDiffUtilCallback : 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/fr/free/nrw/commons/explore/categories/media/CategoriesMediaFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/media/CategoriesMediaFragment.kt index c13445f2b7..6de1248b4a 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/media/CategoriesMediaFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/media/CategoriesMediaFragment.kt @@ -6,16 +6,17 @@ import fr.free.nrw.commons.category.CATEGORY_PREFIX import fr.free.nrw.commons.explore.media.PageableMediaFragment import javax.inject.Inject - class CategoriesMediaFragment : PageableMediaFragment() { - @Inject lateinit var presenter: CategoryMediaPresenter override val injectedPresenter get() = presenter - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) onQueryUpdated("$CATEGORY_PREFIX${arguments!!.getString("categoryName")!!}") } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/media/CategoryMediaPresenterImpl.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/media/CategoryMediaPresenterImpl.kt index 0ca581416a..07e5b0f568 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/media/CategoryMediaPresenterImpl.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/media/CategoryMediaPresenterImpl.kt @@ -13,8 +13,10 @@ interface CategoryMediaPresenter : PagingContract.Presenter /** * Presenter for DepictedImagesFragment */ -class CategoryMediaPresenterImpl @Inject constructor( - @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, - dataSourceFactory: PageableCategoriesMediaDataSource -) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), - CategoryMediaPresenter +class CategoryMediaPresenterImpl + @Inject + constructor( + @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, + dataSourceFactory: PageableCategoriesMediaDataSource, + ) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), + CategoryMediaPresenter diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/media/PageableCategoriesMediaDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/media/PageableCategoriesMediaDataSource.kt index 7ba5209f86..49ed304853 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/media/PageableCategoriesMediaDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/media/PageableCategoriesMediaDataSource.kt @@ -7,14 +7,16 @@ import fr.free.nrw.commons.explore.paging.PageableBaseDataSource import fr.free.nrw.commons.media.MediaClient import javax.inject.Inject -class PageableCategoriesMediaDataSource @Inject constructor( - liveDataConverter: LiveDataConverter, - private val mediaClient: MediaClient -) : PageableBaseDataSource(liveDataConverter) { - override val loadFunction: LoadFunction = { loadSize: Int, startPosition: Int -> - if(startPosition == 0){ - mediaClient.resetCategoryContinuation(query) +class PageableCategoriesMediaDataSource + @Inject + constructor( + liveDataConverter: LiveDataConverter, + private val mediaClient: MediaClient, + ) : PageableBaseDataSource(liveDataConverter) { + override val loadFunction: LoadFunction = { loadSize: Int, startPosition: Int -> + if (startPosition == 0) { + mediaClient.resetCategoryContinuation(query) + } + mediaClient.getMediaListFromCategory(query).blockingGet() } - mediaClient.getMediaListFromCategory(query).blockingGet() } -} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/PageableParentCategoriesDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/PageableParentCategoriesDataSource.kt index 16a7cdb088..b045be7d23 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/PageableParentCategoriesDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/PageableParentCategoriesDataSource.kt @@ -5,15 +5,16 @@ import fr.free.nrw.commons.explore.paging.LiveDataConverter import fr.free.nrw.commons.explore.paging.PageableBaseDataSource import javax.inject.Inject -class PageableParentCategoriesDataSource @Inject constructor( - liveDataConverter: LiveDataConverter, - val categoryClient: CategoryClient -) : PageableBaseDataSource(liveDataConverter) { - - override val loadFunction = { loadSize: Int, startPosition: Int -> - if (startPosition == 0) { - categoryClient.resetParentCategoryContinuation(query) +class PageableParentCategoriesDataSource + @Inject + constructor( + liveDataConverter: LiveDataConverter, + val categoryClient: CategoryClient, + ) : PageableBaseDataSource(liveDataConverter) { + override val loadFunction = { loadSize: Int, startPosition: Int -> + if (startPosition == 0) { + categoryClient.resetParentCategoryContinuation(query) + } + categoryClient.getParentCategoryList(query).blockingGet().map { it.name } } - categoryClient.getParentCategoryList(query).blockingGet().map { it.name } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesFragment.kt index a43fdad1dc..6ceccf6075 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesFragment.kt @@ -7,9 +7,7 @@ import fr.free.nrw.commons.category.CATEGORY_PREFIX import fr.free.nrw.commons.explore.categories.PageableCategoryFragment import javax.inject.Inject - class ParentCategoriesFragment : PageableCategoryFragment() { - @Inject lateinit var presenter: ParentCategoriesPresenter @@ -18,9 +16,11 @@ class ParentCategoriesFragment : PageableCategoryFragment() { override fun getEmptyText(query: String) = getString(R.string.no_parentcategory_found) - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) onQueryUpdated("$CATEGORY_PREFIX${arguments!!.getString("categoryName")!!}") } } - diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesPresenterImpl.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesPresenterImpl.kt index 90c11d10d4..06ba4e5eb1 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesPresenterImpl.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/parent/ParentCategoriesPresenterImpl.kt @@ -7,11 +7,12 @@ import io.reactivex.Scheduler import javax.inject.Inject import javax.inject.Named - interface ParentCategoriesPresenter : PagingContract.Presenter -class ParentCategoriesPresenterImpl @Inject constructor( - @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, - dataSourceFactory: PageableParentCategoriesDataSource -) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), - ParentCategoriesPresenter +class ParentCategoriesPresenterImpl + @Inject + constructor( + @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, + dataSourceFactory: PageableParentCategoriesDataSource, + ) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), + ParentCategoriesPresenter diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableSearchCategoriesDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableSearchCategoriesDataSource.kt index 775462861b..11591bea72 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableSearchCategoriesDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/PageableSearchCategoriesDataSource.kt @@ -5,13 +5,16 @@ import fr.free.nrw.commons.explore.paging.LiveDataConverter import fr.free.nrw.commons.explore.paging.PageableBaseDataSource import javax.inject.Inject -class PageableSearchCategoriesDataSource @Inject constructor( - liveDataConverter: LiveDataConverter, - val categoryClient: CategoryClient -) : PageableBaseDataSource(liveDataConverter) { - - override val loadFunction = { loadSize: Int, startPosition: Int -> - categoryClient.searchCategories(query, loadSize, startPosition).blockingGet() - .map { it.name } +class PageableSearchCategoriesDataSource + @Inject + constructor( + liveDataConverter: LiveDataConverter, + val categoryClient: CategoryClient, + ) : PageableBaseDataSource(liveDataConverter) { + override val loadFunction = { loadSize: Int, startPosition: Int -> + categoryClient + .searchCategories(query, loadSize, startPosition) + .blockingGet() + .map { it.name } + } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesFragmentPresenterImpl.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesFragmentPresenterImpl.kt index 998dee1e80..0624cea5be 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesFragmentPresenterImpl.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/search/SearchCategoriesFragmentPresenterImpl.kt @@ -9,8 +9,10 @@ import javax.inject.Named interface SearchCategoriesFragmentPresenter : PagingContract.Presenter -class SearchCategoriesFragmentPresenterImpl @Inject constructor( - @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, - dataSourceFactory: PageableSearchCategoriesDataSource -) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), - SearchCategoriesFragmentPresenter +class SearchCategoriesFragmentPresenterImpl + @Inject + constructor( + @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, + dataSourceFactory: PageableSearchCategoriesDataSource, + ) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), + SearchCategoriesFragmentPresenter diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/PageableSubCategoriesDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/PageableSubCategoriesDataSource.kt index 4c1df96693..6179b92551 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/PageableSubCategoriesDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/PageableSubCategoriesDataSource.kt @@ -5,15 +5,16 @@ import fr.free.nrw.commons.explore.paging.LiveDataConverter import fr.free.nrw.commons.explore.paging.PageableBaseDataSource import javax.inject.Inject -class PageableSubCategoriesDataSource @Inject constructor( - liveDataConverter: LiveDataConverter, - val categoryClient: CategoryClient -) : PageableBaseDataSource(liveDataConverter) { - - override val loadFunction = { loadSize: Int, startPosition: Int -> - if (startPosition == 0) { - categoryClient.resetSubCategoryContinuation(query) +class PageableSubCategoriesDataSource + @Inject + constructor( + liveDataConverter: LiveDataConverter, + val categoryClient: CategoryClient, + ) : PageableBaseDataSource(liveDataConverter) { + override val loadFunction = { loadSize: Int, startPosition: Int -> + if (startPosition == 0) { + categoryClient.resetSubCategoryContinuation(query) + } + categoryClient.getSubCategoryList(query).blockingGet().map { it.name } } - categoryClient.getSubCategoryList(query).blockingGet().map { it.name } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesFragment.kt index 36ffca70e7..19fe52beb0 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesFragment.kt @@ -7,9 +7,7 @@ import fr.free.nrw.commons.category.CATEGORY_PREFIX import fr.free.nrw.commons.explore.categories.PageableCategoryFragment import javax.inject.Inject - class SubCategoriesFragment : PageableCategoryFragment() { - @Inject lateinit var presenter: SubCategoriesPresenter override val injectedPresenter @@ -17,7 +15,10 @@ class SubCategoriesFragment : PageableCategoryFragment() { override fun getEmptyText(query: String) = getString(R.string.no_subcategory_found) - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) onQueryUpdated("$CATEGORY_PREFIX${arguments!!.getString("categoryName")!!}") } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesPresenterImpl.kt b/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesPresenterImpl.kt index 5f41a36a44..df4aebcf26 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesPresenterImpl.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/sub/SubCategoriesPresenterImpl.kt @@ -9,8 +9,10 @@ import javax.inject.Named interface SubCategoriesPresenter : PagingContract.Presenter -class SubCategoriesPresenterImpl @Inject constructor( - @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, - dataSourceFactory: PageableSubCategoriesDataSource -) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), - SubCategoriesPresenter +class SubCategoriesPresenterImpl + @Inject + constructor( + @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, + dataSourceFactory: PageableSubCategoriesDataSource, + ) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), + SubCategoriesPresenter diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictionAdapter.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictionAdapter.kt index 632881150b..56baacc659 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictionAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictionAdapter.kt @@ -9,22 +9,31 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.databinding.ItemDepictionsBinding import fr.free.nrw.commons.upload.structure.depictions.DepictedItem -class DepictionAdapter(private val onDepictionClicked: (DepictedItem) -> Unit) : - PagedListAdapter(DepictionDiffUtilCallback) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = DepictedItemViewHolder( - ItemDepictionsBinding.inflate(LayoutInflater.from(parent.context), parent, false) +class DepictionAdapter( + private val onDepictionClicked: (DepictedItem) -> Unit, +) : PagedListAdapter(DepictionDiffUtilCallback) { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ) = DepictedItemViewHolder( + ItemDepictionsBinding.inflate(LayoutInflater.from(parent.context), parent, false), ) - override fun onBindViewHolder(holder: DepictedItemViewHolder, position: Int) { + override fun onBindViewHolder( + holder: DepictedItemViewHolder, + position: Int, + ) { holder.bind(getItem(position)!!, onDepictionClicked) } } class DepictedItemViewHolder( - private val binding: ItemDepictionsBinding + private val binding: ItemDepictionsBinding, ) : RecyclerView.ViewHolder(binding.root) { - fun bind(item: DepictedItem, onDepictionClicked: (DepictedItem) -> Unit) = with(binding) { + fun bind( + item: DepictedItem, + onDepictionClicked: (DepictedItem) -> Unit, + ) = with(binding) { root.setOnClickListener { onDepictionClicked(item) } depictsLabel.text = item.name description.text = item.description @@ -37,9 +46,13 @@ class DepictedItemViewHolder( } private object DepictionDiffUtilCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: DepictedItem, newItem: DepictedItem) = - oldItem.id == newItem.id + override fun areItemsTheSame( + oldItem: DepictedItem, + newItem: DepictedItem, + ) = oldItem.id == newItem.id - override fun areContentsTheSame(oldItem: DepictedItem, newItem: DepictedItem) = - oldItem == newItem + override fun areContentsTheSame( + oldItem: DepictedItem, + newItem: DepictedItem, + ) = oldItem == newItem } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictionModule.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictionModule.kt index cb8ee6999c..d6d5954e1b 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictionModule.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictionModule.kt @@ -14,16 +14,12 @@ import fr.free.nrw.commons.explore.depictions.parent.ParentDepictionsPresenterIm */ @Module abstract class DepictionModule { - @Binds - abstract fun ParentDepictionsPresenterImpl.bindsParentDepictionPresenter() - : ParentDepictionsPresenter + abstract fun ParentDepictionsPresenterImpl.bindsParentDepictionPresenter(): ParentDepictionsPresenter @Binds - abstract fun ChildDepictionsPresenterImpl.bindsChildDepictionPresenter() - : ChildDepictionsPresenter + abstract fun ChildDepictionsPresenterImpl.bindsChildDepictionPresenter(): ChildDepictionsPresenter @Binds - abstract fun DepictedImagesPresenterImpl.bindsDepictedImagesContractPresenter() - : DepictedImagesPresenter + abstract fun DepictedImagesPresenterImpl.bindsDepictedImagesContractPresenter(): DepictedImagesPresenter } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictsClient.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictsClient.kt index e01e12f55f..acf0f9351d 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictsClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/DepictsClient.kt @@ -10,9 +10,9 @@ import fr.free.nrw.commons.wikidata.WikidataProperties import fr.free.nrw.commons.wikidata.model.DataValue import fr.free.nrw.commons.wikidata.model.DepictSearchItem import fr.free.nrw.commons.wikidata.model.Entities -import fr.free.nrw.commons.wikidata.model.Statement_partial +import fr.free.nrw.commons.wikidata.model.StatementPartial import io.reactivex.Single -import java.util.* +import java.util.Locale import javax.inject.Inject import javax.inject.Singleton @@ -20,89 +20,101 @@ import javax.inject.Singleton * Depicts Client to handle custom calls to Commons Wikibase APIs */ @Singleton -class DepictsClient @Inject constructor(private val depictsInterface: DepictsInterface) { +class DepictsClient + @Inject + constructor( + private val depictsInterface: DepictsInterface, + ) { + /** + * Search for depictions using the search item + * @return list of depicted items + */ + fun searchForDepictions( + query: String?, + limit: Int, + offset: Int, + ): Single> { + val language = Locale.getDefault().language + return depictsInterface + .searchForDepicts(query, "$limit", language, language, "$offset") + .map { it.search.joinToString("|", transform = DepictSearchItem::id) } + .mapToDepictions() + } - /** - * Search for depictions using the search item - * @return list of depicted items - */ - fun searchForDepictions(query: String?, limit: Int, offset: Int): Single> { - val language = Locale.getDefault().language - return depictsInterface.searchForDepicts(query, "$limit", language, language, "$offset") - .map { it.search.joinToString("|", transform = DepictSearchItem::id) } - .mapToDepictions() - } + fun getEntities(ids: String): Single = depictsInterface.getEntities(ids) - fun getEntities(ids: String): Single { - return depictsInterface.getEntities(ids) - } + fun toDepictions(sparqlResponse: Single): Single> = + sparqlResponse + .map { + it.results.bindings.joinToString("|", transform = Binding::id) + }.mapToDepictions() - fun toDepictions(sparqlResponse: Single): Single> { - return sparqlResponse.map { - it.results.bindings.joinToString("|", transform = Binding::id) - }.mapToDepictions() - } - - /** - * Fetches Entities from ids ex. "Q1233|Q546" and converts them into DepictedItem - */ - @SuppressLint("CheckResult") - private fun Single.mapToDepictions() = - flatMap(::getEntities) - .map { entities -> - entities.entities().values.map { entity -> - mapToDepictItem(entity) - } - } + /** + * Fetches Entities from ids ex. "Q1233|Q546" and converts them into DepictedItem + */ + @SuppressLint("CheckResult") + private fun Single.mapToDepictions() = + flatMap(::getEntities) + .map { entities -> + entities.entities().values.map { entity -> + mapToDepictItem(entity) + } + } - /** - * Convert different entities into DepictedItem - */ - private fun mapToDepictItem(entity: Entities.Entity): DepictedItem { - return if (entity.descriptions().byLanguageOrFirstOrEmpty() == "") { - val instanceOfIDs = entity[WikidataProperties.INSTANCE_OF] - .toIds() - if (instanceOfIDs.isNotEmpty()) { - val entities: Entities = getEntities(instanceOfIDs[0]).blockingGet() - val nameAsDescription = entities.entities().values.first().labels() - .byLanguageOrFirstOrEmpty() - DepictedItem( - entity, - entity.labels().byLanguageOrFirstOrEmpty(), - nameAsDescription - ) + /** + * Convert different entities into DepictedItem + */ + private fun mapToDepictItem(entity: Entities.Entity): DepictedItem = + if (entity.descriptions().byLanguageOrFirstOrEmpty() == "") { + val instanceOfIDs = + entity[WikidataProperties.INSTANCE_OF] + .toIds() + if (instanceOfIDs.isNotEmpty()) { + val entities: Entities = getEntities(instanceOfIDs[0]).blockingGet() + val nameAsDescription = + entities + .entities() + .values + .first() + .labels() + .byLanguageOrFirstOrEmpty() + DepictedItem( + entity, + entity.labels().byLanguageOrFirstOrEmpty(), + nameAsDescription, + ) + } else { + DepictedItem( + entity, + entity.labels().byLanguageOrFirstOrEmpty(), + "", + ) + } } else { DepictedItem( entity, entity.labels().byLanguageOrFirstOrEmpty(), - "" + entity.descriptions().byLanguageOrFirstOrEmpty(), ) } - } else { - DepictedItem( - entity, - entity.labels().byLanguageOrFirstOrEmpty(), - entity.descriptions().byLanguageOrFirstOrEmpty() - ) - } - } - /** - * Tries to get Entities.Label by default language from the map. - * If that returns null, Tries to retrieve first element from the map. - * If that still returns null, function returns "". - */ - private fun Map.byLanguageOrFirstOrEmpty() = - let { - it[Locale.getDefault().language] ?: it.values.firstOrNull() }?.value() ?: "" + /** + * Tries to get Entities.Label by default language from the map. + * If that returns null, Tries to retrieve first element from the map. + * If that still returns null, function returns "". + */ + private fun Map.byLanguageOrFirstOrEmpty() = + let { + it[Locale.getDefault().language] ?: it.values.firstOrNull() + }?.value() ?: "" - /** - * returns list of id ex. "Q2323" from Statement_partial - */ - private fun List?.toIds(): List { - return this?.map { it.mainSnak.dataValue } - ?.filterIsInstance() - ?.map { it.value.id } - ?: emptyList() + /** + * returns list of id ex. "Q2323" from Statement_partial + */ + private fun List?.toIds(): List = + this + ?.map { it.mainSnak.dataValue } + ?.filterIsInstance() + ?.map { it.value.id } + ?: emptyList() } -} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/PageableDepictionsFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/PageableDepictionsFragment.kt index 4c288d08ab..9c41628a23 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/PageableDepictionsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/PageableDepictionsFragment.kt @@ -4,7 +4,6 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.explore.paging.BasePagingFragment import fr.free.nrw.commons.upload.structure.depictions.DepictedItem - abstract class PageableDepictionsFragment : BasePagingFragment() { override val errorTextId: Int = R.string.error_loading_depictions override val pagedListAdapter by lazy { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/ChildDepictionsFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/ChildDepictionsFragment.kt index ae5cc755e4..5275362997 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/ChildDepictionsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/ChildDepictionsFragment.kt @@ -6,18 +6,19 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.explore.depictions.PageableDepictionsFragment import javax.inject.Inject - -class ChildDepictionsFragment: PageableDepictionsFragment() { +class ChildDepictionsFragment : PageableDepictionsFragment() { @Inject lateinit var presenter: ChildDepictionsPresenter override val injectedPresenter - get() = presenter + get() = presenter - override fun getEmptyText(query: String) = - getString(R.string.no_child_classes, arguments!!.getString("wikidataItemName")!!) + override fun getEmptyText(query: String) = getString(R.string.no_child_classes, arguments!!.getString("wikidataItemName")!!) - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) onQueryUpdated(arguments!!.getString("entityId")!!) } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/ChildDepictionsPresenterImpl.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/ChildDepictionsPresenterImpl.kt index b8c1abe63e..2d88522be8 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/ChildDepictionsPresenterImpl.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/ChildDepictionsPresenterImpl.kt @@ -10,8 +10,10 @@ import javax.inject.Named interface ChildDepictionsPresenter : PagingContract.Presenter -class ChildDepictionsPresenterImpl @Inject constructor( - @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, - dataSourceFactory: PageableChildDepictionsDataSource -) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), - ChildDepictionsPresenter +class ChildDepictionsPresenterImpl + @Inject + constructor( + @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, + dataSourceFactory: PageableChildDepictionsDataSource, + ) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), + ChildDepictionsPresenter diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/PageableChildDepictionsDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/PageableChildDepictionsDataSource.kt index c189a2e0a3..c801f08e72 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/PageableChildDepictionsDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/child/PageableChildDepictionsDataSource.kt @@ -6,12 +6,13 @@ import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient import fr.free.nrw.commons.upload.structure.depictions.DepictedItem import javax.inject.Inject -class PageableChildDepictionsDataSource @Inject constructor( - liveDataConverter: LiveDataConverter, - private val okHttpJsonApiClient: OkHttpJsonApiClient -) : PageableBaseDataSource(liveDataConverter) { - override val loadFunction = { limit: Int, startPosition: Int -> - okHttpJsonApiClient.getChildDepictions(query, startPosition, limit).blockingGet() +class PageableChildDepictionsDataSource + @Inject + constructor( + liveDataConverter: LiveDataConverter, + private val okHttpJsonApiClient: OkHttpJsonApiClient, + ) : PageableBaseDataSource(liveDataConverter) { + override val loadFunction = { limit: Int, startPosition: Int -> + okHttpJsonApiClient.getChildDepictions(query, startPosition, limit).blockingGet() + } } -} - diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/DepictedImagesFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/DepictedImagesFragment.kt index f31bba68d1..cc1b664b2a 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/DepictedImagesFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/DepictedImagesFragment.kt @@ -2,7 +2,6 @@ package fr.free.nrw.commons.explore.depictions.media import android.os.Bundle import android.view.View -import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity import fr.free.nrw.commons.explore.media.PageableMediaFragment import javax.inject.Inject @@ -13,7 +12,10 @@ class DepictedImagesFragment : PageableMediaFragment() { override val injectedPresenter get() = presenter - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) onQueryUpdated(arguments!!.getString("entityId")!!) } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/DepictedImagesPresenterImpl.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/DepictedImagesPresenterImpl.kt index 743b78e437..e227a48754 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/DepictedImagesPresenterImpl.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/DepictedImagesPresenterImpl.kt @@ -13,8 +13,10 @@ interface DepictedImagesPresenter : PagingContract.Presenter /** * Presenter for DepictedImagesFragment */ -class DepictedImagesPresenterImpl @Inject constructor( - @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, - dataSourceFactory: PageableDepictedMediaDataSource -) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), - DepictedImagesPresenter +class DepictedImagesPresenterImpl + @Inject + constructor( + @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, + dataSourceFactory: PageableDepictedMediaDataSource, + ) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), + DepictedImagesPresenter diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSource.kt index 9592d76057..0f1c5109b1 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSource.kt @@ -7,11 +7,13 @@ import fr.free.nrw.commons.explore.paging.PageableBaseDataSource import fr.free.nrw.commons.media.WikidataMediaClient import javax.inject.Inject -class PageableDepictedMediaDataSource @Inject constructor( - liveDataConverter: LiveDataConverter, - private val wikiMediaClient: WikidataMediaClient -) : PageableBaseDataSource(liveDataConverter) { - override val loadFunction: LoadFunction = { loadSize: Int, startPosition: Int -> - wikiMediaClient.fetchImagesForDepictedItem(query, loadSize, startPosition).blockingGet() +class PageableDepictedMediaDataSource + @Inject + constructor( + liveDataConverter: LiveDataConverter, + private val wikiMediaClient: WikidataMediaClient, + ) : PageableBaseDataSource(liveDataConverter) { + override val loadFunction: LoadFunction = { loadSize: Int, startPosition: Int -> + wikiMediaClient.fetchImagesForDepictedItem(query, loadSize, startPosition).blockingGet() + } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/PageableParentDepictionsDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/PageableParentDepictionsDataSource.kt index 8771adbfab..d59a845abd 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/PageableParentDepictionsDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/PageableParentDepictionsDataSource.kt @@ -6,12 +6,13 @@ import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient import fr.free.nrw.commons.upload.structure.depictions.DepictedItem import javax.inject.Inject -class PageableParentDepictionsDataSource @Inject constructor( - liveDataConverter: LiveDataConverter, - private val okHttpJsonApiClient: OkHttpJsonApiClient -) : PageableBaseDataSource(liveDataConverter) { - override val loadFunction = { limit: Int, startPosition: Int -> - okHttpJsonApiClient.getParentDepictions(query, startPosition, limit).blockingGet() +class PageableParentDepictionsDataSource + @Inject + constructor( + liveDataConverter: LiveDataConverter, + private val okHttpJsonApiClient: OkHttpJsonApiClient, + ) : PageableBaseDataSource(liveDataConverter) { + override val loadFunction = { limit: Int, startPosition: Int -> + okHttpJsonApiClient.getParentDepictions(query, startPosition, limit).blockingGet() + } } -} - diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/ParentDepictionsFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/ParentDepictionsFragment.kt index 779764409f..52a5aff5d8 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/ParentDepictionsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/ParentDepictionsFragment.kt @@ -6,7 +6,6 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.explore.depictions.PageableDepictionsFragment import javax.inject.Inject - class ParentDepictionsFragment : PageableDepictionsFragment() { @Inject lateinit var presenter: ParentDepictionsPresenter @@ -14,10 +13,12 @@ class ParentDepictionsFragment : PageableDepictionsFragment() { override val injectedPresenter get() = presenter - override fun getEmptyText(query: String) = - getString(R.string.no_parent_classes, arguments!!.getString("wikidataItemName")!!) + override fun getEmptyText(query: String) = getString(R.string.no_parent_classes, arguments!!.getString("wikidataItemName")!!) - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) onQueryUpdated(arguments!!.getString("entityId")!!) } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/ParentDepictionsPresenterImpl.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/ParentDepictionsPresenterImpl.kt index db9ee9192d..5bb066aa13 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/ParentDepictionsPresenterImpl.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/parent/ParentDepictionsPresenterImpl.kt @@ -10,10 +10,10 @@ import javax.inject.Named interface ParentDepictionsPresenter : PagingContract.Presenter -class ParentDepictionsPresenterImpl @Inject constructor( - @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, - dataSourceFactory: PageableParentDepictionsDataSource -) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), - ParentDepictionsPresenter { - -} +class ParentDepictionsPresenterImpl + @Inject + constructor( + @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, + dataSourceFactory: PageableParentDepictionsDataSource, + ) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), + ParentDepictionsPresenter diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/search/PageableDepictionsDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/search/PageableDepictionsDataSource.kt index 961d1afb2e..9297fc7c87 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/search/PageableDepictionsDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/search/PageableDepictionsDataSource.kt @@ -1,9 +1,9 @@ package fr.free.nrw.commons.explore.depictions.search +import fr.free.nrw.commons.explore.depictions.DepictsClient import fr.free.nrw.commons.explore.paging.LiveDataConverter import fr.free.nrw.commons.explore.paging.LoadingState import fr.free.nrw.commons.explore.paging.PageableBaseDataSource -import fr.free.nrw.commons.explore.depictions.DepictsClient import fr.free.nrw.commons.upload.structure.depictions.DepictedItem import io.reactivex.processors.PublishProcessor import javax.inject.Inject @@ -11,13 +11,13 @@ import javax.inject.Inject typealias LoadFunction = (Int, Int) -> List typealias LoadingStates = PublishProcessor -class PageableDepictionsDataSource @Inject constructor( - liveDataConverter: LiveDataConverter, - val depictsClient: DepictsClient -) : PageableBaseDataSource(liveDataConverter) { - - override val loadFunction = { loadSize: Int, startPosition: Int -> - depictsClient.searchForDepictions(query, loadSize, startPosition).blockingGet() +class PageableDepictionsDataSource + @Inject + constructor( + liveDataConverter: LiveDataConverter, + val depictsClient: DepictsClient, + ) : PageableBaseDataSource(liveDataConverter) { + override val loadFunction = { loadSize: Int, startPosition: Int -> + depictsClient.searchForDepictions(query, loadSize, startPosition).blockingGet() + } } -} - diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/search/SearchDepictionsFragmentPresenterImpl.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/search/SearchDepictionsFragmentPresenterImpl.kt index e85b08e223..5433ed3a72 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/search/SearchDepictionsFragmentPresenterImpl.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/search/SearchDepictionsFragmentPresenterImpl.kt @@ -9,11 +9,14 @@ import javax.inject.Inject import javax.inject.Named interface SearchDepictionsFragmentPresenter : PagingContract.Presenter + /** * The presenter class for SearchDepictionsFragment */ -class SearchDepictionsFragmentPresenterImpl @Inject constructor( - @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, - dataSourceFactory: PageableDepictionsDataSource -) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), - SearchDepictionsFragmentPresenter +class SearchDepictionsFragmentPresenterImpl + @Inject + constructor( + @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, + dataSourceFactory: PageableDepictionsDataSource, + ) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), + SearchDepictionsFragmentPresenter diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt index 162cfa2f2b..70b46b1291 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/MediaConverter.kt @@ -8,95 +8,105 @@ import fr.free.nrw.commons.utils.MediaDataExtractorUtil import fr.free.nrw.commons.wikidata.WikidataProperties import fr.free.nrw.commons.wikidata.model.DataValue import fr.free.nrw.commons.wikidata.model.Entities -import org.apache.commons.lang3.StringUtils -import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage import fr.free.nrw.commons.wikidata.model.gallery.ExtMetadata import fr.free.nrw.commons.wikidata.model.gallery.ImageInfo +import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage +import org.apache.commons.lang3.StringUtils import java.text.ParseException -import java.util.* +import java.util.Date import javax.inject.Inject -class MediaConverter @Inject constructor() { - fun convert(page: MwQueryPage, entity: Entities.Entity, imageInfo: ImageInfo): Media { - val metadata = imageInfo.metadata - requireNotNull(metadata) { "No metadata" } - // Stores mapping of title attribute to hidden attribute of each category - val myMap = mutableMapOf() - page.categories()?.forEach { myMap[it.title()] = (it.hidden()) } +class MediaConverter + @Inject + constructor() { + fun convert( + page: MwQueryPage, + entity: Entities.Entity, + imageInfo: ImageInfo, + ): Media { + val metadata = imageInfo.metadata + requireNotNull(metadata) { "No metadata" } + // Stores mapping of title attribute to hidden attribute of each category + val myMap = mutableMapOf() + page.categories()?.forEach { myMap[it.title()] = (it.hidden()) } - return Media( - page.pageId().toString(), - imageInfo.thumbUrl.takeIf { it.isNotBlank() } ?: imageInfo.originalUrl, - imageInfo.originalUrl, - page.title(), - metadata.imageDescription(), - safeParseDate(metadata.dateTime()), - metadata.licenseShortName(), - metadata.prefixedLicenseUrl, - getAuthor(metadata), - imageInfo.user, - MediaDataExtractorUtil.extractCategoriesFromList(metadata.categories), - metadata.latLng, - entity.labels().mapValues { it.value.value() }, - entity.descriptions().mapValues { it.value.value() }, - entity.depictionIds(), - myMap - ) - } - - /** - * Creating Media object from MWQueryPage. - * Earlier only basic details were set for the media object but going forward, - * a full media object(with categories, descriptions, coordinates etc) can be constructed using this method - * - * @param page response from the API - * @return Media object - */ - - private fun safeParseDate(dateStr: String): Date? { - return try { - CommonsDateUtil.getMediaSimpleDateFormat().parse(dateStr) - } catch (e: ParseException) { - null + return Media( + page.pageId().toString(), + imageInfo.thumbUrl.takeIf { it.isNotBlank() } ?: imageInfo.originalUrl, + imageInfo.originalUrl, + page.title(), + metadata.imageDescription(), + safeParseDate(metadata.dateTime()), + metadata.licenseShortName(), + metadata.prefixedLicenseUrl, + getAuthor(metadata), + imageInfo.user, + MediaDataExtractorUtil.extractCategoriesFromList(metadata.categories), + metadata.latLng, + entity.labels().mapValues { it.value.value() }, + entity.descriptions().mapValues { it.value.value() }, + entity.depictionIds(), + myMap, + ) } - } + /** + * Creating Media object from MWQueryPage. + * Earlier only basic details were set for the media object but going forward, + * a full media object(with categories, descriptions, coordinates etc) can be constructed using this method + * + * @param page response from the API + * @return Media object + */ - /** - * This method extracts the Commons Username from the artist HTML information - * @param metadata - * @return - */ - private fun getAuthor(metadata: ExtMetadata): String? { - return try { - val authorHtml = metadata.artist() - val anchorStartTagTerminalChars = "\">" - val anchorCloseTag = "" + private fun safeParseDate(dateStr: String): Date? = + try { + CommonsDateUtil.getMediaSimpleDateFormat().parse(dateStr) + } catch (e: ParseException) { + null + } - return authorHtml.substring( - authorHtml.indexOf(anchorStartTagTerminalChars) + anchorStartTagTerminalChars - .length, authorHtml.indexOf(anchorCloseTag) - ) - } catch (ex: java.lang.Exception) { - "" + /** + * This method extracts the Commons Username from the artist HTML information + * @param metadata + * @return + */ + private fun getAuthor(metadata: ExtMetadata): String? { + return try { + val authorHtml = metadata.artist() + val anchorStartTagTerminalChars = "\">" + val anchorCloseTag = "" + + return authorHtml.substring( + authorHtml.indexOf(anchorStartTagTerminalChars) + + anchorStartTagTerminalChars + .length, + authorHtml.indexOf(anchorCloseTag), + ) + } catch (ex: java.lang.Exception) { + "" + } } } -} private fun Entities.Entity.depictionIds() = this[WikidataProperties.DEPICTS]?.mapNotNull { (it.mainSnak.dataValue as? DataValue.EntityId)?.value?.id } ?: emptyList() private val ExtMetadata.prefixedLicenseUrl: String - get() = licenseUrl().let { - if (!it.startsWith("http://") && !it.startsWith("https://")) - "https://$it" - else - it - } + get() = + licenseUrl().let { + if (!it.startsWith("http://") && !it.startsWith("https://")) { + "https://$it" + } else { + it + } + } private val ExtMetadata.latLng: LatLng? - get() = if (!StringUtils.isBlank(gpsLatitude) && !StringUtils.isBlank(gpsLongitude)) - LatLng(gpsLatitude.toDouble(), gpsLongitude.toDouble(), 0.0f) - else - null + get() = + if (!StringUtils.isBlank(gpsLatitude) && !StringUtils.isBlank(gpsLongitude)) { + LatLng(gpsLatitude.toDouble(), gpsLongitude.toDouble(), 0.0f) + } else { + null + } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaDataSource.kt index 89bdf38795..074a2125f5 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaDataSource.kt @@ -1,17 +1,19 @@ package fr.free.nrw.commons.explore.media import fr.free.nrw.commons.Media +import fr.free.nrw.commons.explore.depictions.search.LoadFunction import fr.free.nrw.commons.explore.paging.LiveDataConverter import fr.free.nrw.commons.explore.paging.PageableBaseDataSource -import fr.free.nrw.commons.explore.depictions.search.LoadFunction import fr.free.nrw.commons.media.MediaClient import javax.inject.Inject -class PageableMediaDataSource @Inject constructor( - liveDataConverter: LiveDataConverter, - private val mediaClient: MediaClient -) : PageableBaseDataSource(liveDataConverter) { - override val loadFunction: LoadFunction = { loadSize: Int, startPosition: Int -> - mediaClient.getMediaListFromSearch(query, loadSize, startPosition).blockingGet() +class PageableMediaDataSource + @Inject + constructor( + liveDataConverter: LiveDataConverter, + private val mediaClient: MediaClient, + ) : PageableBaseDataSource(liveDataConverter) { + override val loadFunction: LoadFunction = { loadSize: Int, startPosition: Int -> + mediaClient.getMediaListFromSearch(query, loadSize, startPosition).blockingGet() + } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaFragment.kt index 3ff43aeacb..987f4ca005 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/PageableMediaFragment.kt @@ -9,8 +9,9 @@ import fr.free.nrw.commons.category.CategoryImagesCallback import fr.free.nrw.commons.explore.paging.BasePagingFragment import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider -abstract class PageableMediaFragment : BasePagingFragment(), MediaDetailProvider { - +abstract class PageableMediaFragment : + BasePagingFragment(), + MediaDetailProvider { override val pagedListAdapter by lazy { PagedMediaAdapter(categoryImagesCallback::onMediaClicked) } @@ -33,7 +34,10 @@ abstract class PageableMediaFragment : BasePagingFragment(), MediaDetailP private val simpleDataObserver = SimpleDataObserver { categoryImagesCallback.viewPagerNotifyDataSetChanged() } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) pagedListAdapter.registerAdapterDataObserver(simpleDataObserver) } @@ -44,7 +48,9 @@ abstract class PageableMediaFragment : BasePagingFragment(), MediaDetailP } override fun getMediaAtPosition(position: Int): Media? = - pagedListAdapter.currentList?.get(position)?.takeIf { it.filename != null } + pagedListAdapter.currentList + ?.get(position) + ?.takeIf { it.filename != null } .also { pagedListAdapter.currentList?.loadAround(position) binding.paginatedSearchResultsList.scrollToPosition(position) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/PagedMediaAdapter.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/PagedMediaAdapter.kt index c1e641b560..c987b76c29 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/PagedMediaAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/PagedMediaAdapter.kt @@ -10,28 +10,41 @@ import fr.free.nrw.commons.databinding.LayoutCategoryImagesBinding import fr.free.nrw.commons.explore.paging.BaseViewHolder import fr.free.nrw.commons.explore.paging.inflate -class PagedMediaAdapter(private val onImageClicked: (Int) -> Unit) : - PagedListAdapter(object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Media, newItem: Media) = - oldItem.pageId == newItem.pageId - - override fun areContentsTheSame(oldItem: Media, newItem: Media) = - oldItem.pageId == newItem.pageId - }) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = - SearchImagesViewHolder( - parent.inflate(R.layout.layout_category_images), - onImageClicked - ) - - override fun onBindViewHolder(holder: SearchImagesViewHolder, position: Int) { +class PagedMediaAdapter( + private val onImageClicked: (Int) -> Unit, +) : PagedListAdapter( + object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: Media, + newItem: Media, + ) = oldItem.pageId == newItem.pageId + + override fun areContentsTheSame( + oldItem: Media, + newItem: Media, + ) = oldItem.pageId == newItem.pageId + }, + ) { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ) = SearchImagesViewHolder( + parent.inflate(R.layout.layout_category_images), + onImageClicked, + ) + + override fun onBindViewHolder( + holder: SearchImagesViewHolder, + position: Int, + ) { holder.bind(getItem(position)!! to position) } } -class SearchImagesViewHolder(containerView: View, val onImageClicked: (Int) -> Unit) : - BaseViewHolder>(containerView) { +class SearchImagesViewHolder( + containerView: View, + val onImageClicked: (Int) -> Unit, +) : BaseViewHolder>(containerView) { val binding = LayoutCategoryImagesBinding.bind(itemView) override fun bind(item: Pair) { @@ -47,5 +60,4 @@ class SearchImagesViewHolder(containerView: View, val onImageClicked: (Int) -> U binding.categoryImageAuthor.visibility = View.GONE } } - } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/SearchMediaFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/SearchMediaFragment.kt index 28709d8350..de16bddf6c 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/SearchMediaFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/SearchMediaFragment.kt @@ -12,4 +12,3 @@ class SearchMediaFragment : PageableMediaFragment() { override val injectedPresenter get() = presenter } - diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/SearchMediaFragmentPresenterImpl.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/SearchMediaFragmentPresenterImpl.kt index ea2c3c4d0f..9279f46689 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/SearchMediaFragmentPresenterImpl.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/SearchMediaFragmentPresenterImpl.kt @@ -2,16 +2,18 @@ package fr.free.nrw.commons.explore.media import fr.free.nrw.commons.Media import fr.free.nrw.commons.di.CommonsApplicationModule -import fr.free.nrw.commons.explore.paging.PagingContract import fr.free.nrw.commons.explore.paging.BasePagingPresenter +import fr.free.nrw.commons.explore.paging.PagingContract import io.reactivex.Scheduler import javax.inject.Inject import javax.inject.Named interface SearchMediaFragmentPresenter : PagingContract.Presenter -class SearchMediaFragmentPresenterImpl @Inject constructor( - @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, - dataSourceFactory: PageableMediaDataSource -) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), - SearchMediaFragmentPresenter +class SearchMediaFragmentPresenterImpl + @Inject + constructor( + @Named(CommonsApplicationModule.MAIN_THREAD) mainThreadScheduler: Scheduler, + dataSourceFactory: PageableMediaDataSource, + ) : BasePagingPresenter(mainThreadScheduler, dataSourceFactory), + SearchMediaFragmentPresenter diff --git a/app/src/main/java/fr/free/nrw/commons/explore/media/SimpleDataObserver.kt b/app/src/main/java/fr/free/nrw/commons/explore/media/SimpleDataObserver.kt index 4bd9e0f2b0..e5a7b24ab9 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/media/SimpleDataObserver.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/media/SimpleDataObserver.kt @@ -2,18 +2,27 @@ package fr.free.nrw.commons.explore.media import androidx.recyclerview.widget.RecyclerView -class SimpleDataObserver(private val onAnyChange: () -> Unit) : RecyclerView.AdapterDataObserver() { +class SimpleDataObserver( + private val onAnyChange: () -> Unit, +) : RecyclerView.AdapterDataObserver() { override fun onChanged() { super.onChanged() onAnyChange.invoke() } - override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) { + override fun onItemRangeRemoved( + positionStart: Int, + itemCount: Int, + ) { super.onItemRangeRemoved(positionStart, itemCount) onAnyChange.invoke() } - override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) { + override fun onItemRangeMoved( + fromPosition: Int, + toPosition: Int, + itemCount: Int, + ) { super.onItemRangeMoved(fromPosition, toPosition, itemCount) onAnyChange.invoke() } @@ -23,17 +32,27 @@ class SimpleDataObserver(private val onAnyChange: () -> Unit) : RecyclerView.Ada onAnyChange.invoke() } - override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { + override fun onItemRangeInserted( + positionStart: Int, + itemCount: Int, + ) { super.onItemRangeInserted(positionStart, itemCount) onAnyChange.invoke() } - override fun onItemRangeChanged(positionStart: Int, itemCount: Int) { + override fun onItemRangeChanged( + positionStart: Int, + itemCount: Int, + ) { super.onItemRangeChanged(positionStart, itemCount) onAnyChange.invoke() } - override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) { + override fun onItemRangeChanged( + positionStart: Int, + itemCount: Int, + payload: Any?, + ) { super.onItemRangeChanged(positionStart, itemCount, payload) onAnyChange.invoke() } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/models/RecentSearch.kt b/app/src/main/java/fr/free/nrw/commons/explore/models/RecentSearch.kt index 6c8c052304..2340865a68 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/models/RecentSearch.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/models/RecentSearch.kt @@ -1,29 +1,23 @@ package fr.free.nrw.commons.explore.models import android.net.Uri -import java.util.* +import java.util.Date /** * Represents a recently searched query * Example - query = "butterfly" */ -class RecentSearch -/** - * Constructor - * @param contentUri the content URI for this query - * @param query query name - * @param lastSearched last searched date - */( - /** - * Modifies the content URI - marking this query as already saved in the database - * - * @param contentUri the content URI - */ - var contentUri: Uri?, - /** - * Gets query name - * @return query name - */ - val query: String, var lastSearched: Date) { - -} \ No newline at end of file +class RecentSearch( + /** + * Modifies the content URI - marking this query as already saved in the database + * + * @param contentUri the content URI + */ + var contentUri: Uri?, + /** + * Gets query name + * @return query name + */ + val query: String, + var lastSearched: Date, +) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/paging/BasePagingFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/paging/BasePagingFragment.kt index d4e8f0e8f4..e3673f9deb 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/paging/BasePagingFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/paging/BasePagingFragment.kt @@ -5,7 +5,8 @@ import android.content.res.Configuration import android.os.Bundle import android.view.LayoutInflater import android.view.View -import android.view.View.* +import android.view.View.GONE +import android.view.View.VISIBLE import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData @@ -18,10 +19,9 @@ import fr.free.nrw.commons.databinding.FragmentSearchPaginatedBinding import fr.free.nrw.commons.di.CommonsDaggerSupportFragment import fr.free.nrw.commons.utils.ViewUtil - -abstract class BasePagingFragment : CommonsDaggerSupportFragment(), +abstract class BasePagingFragment : + CommonsDaggerSupportFragment(), PagingContract.View { - abstract val pagedListAdapter: PagedListAdapter abstract val injectedPresenter: PagingContract.Presenter abstract val errorTextId: Int @@ -29,18 +29,21 @@ abstract class BasePagingFragment : CommonsDaggerSupportFragment(), private val mergeAdapter by lazy { MergeAdapter(pagedListAdapter, loadingAdapter) } private var searchResults: LiveData>? = null - protected lateinit var binding : FragmentSearchPaginatedBinding + protected lateinit var binding: FragmentSearchPaginatedBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View? { binding = FragmentSearchPaginatedBinding.inflate(inflater, container, false) return binding.root } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) binding.paginatedSearchResultsList.apply { @@ -49,7 +52,7 @@ abstract class BasePagingFragment : CommonsDaggerSupportFragment(), } injectedPresenter.listFooterData.observe( viewLifecycleOwner, - Observer(loadingAdapter::submitList) + Observer(loadingAdapter::submitList), ) } @@ -66,9 +69,12 @@ abstract class BasePagingFragment : CommonsDaggerSupportFragment(), override fun observePagingResults(searchResults: LiveData>) { this.searchResults?.removeObservers(viewLifecycleOwner) this.searchResults = searchResults - searchResults.observe(viewLifecycleOwner, Observer { - pagedListAdapter.submitList(it) - }) + searchResults.observe( + viewLifecycleOwner, + Observer { + pagedListAdapter.submitList(it) + }, + ) } override fun onAttach(context: Context) { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/paging/BasePagingPresenter.kt b/app/src/main/java/fr/free/nrw/commons/explore/paging/BasePagingPresenter.kt index 22194adb83..6c2f0882c7 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/paging/BasePagingPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/paging/BasePagingPresenter.kt @@ -6,14 +6,12 @@ import io.reactivex.Scheduler import io.reactivex.disposables.CompositeDisposable import timber.log.Timber - abstract class BasePagingPresenter( val mainThreadScheduler: Scheduler, - val pageableBaseDataSource: PageableBaseDataSource + val pageableBaseDataSource: PageableBaseDataSource, ) : PagingContract.Presenter { - - private val DUMMY: PagingContract.View = proxy() - private var view: PagingContract.View = DUMMY + private val dummy: PagingContract.View = proxy() + private var view: PagingContract.View = dummy private val compositeDisposable = CompositeDisposable() override val listFooterData = MutableLiveData>().apply { value = emptyList() } @@ -25,41 +23,41 @@ abstract class BasePagingPresenter( pageableBaseDataSource.loadingStates .observeOn(mainThreadScheduler) .subscribe(::onLoadingState, Timber::e), - pageableBaseDataSource.noItemsLoadedQueries.subscribe(view::showEmptyText) + pageableBaseDataSource.noItemsLoadedEvent.subscribe(view::showEmptyText), ) } - private fun onLoadingState(it: LoadingState) = when (it) { - LoadingState.Loading -> { - view.hideEmptyText() - listFooterData.postValue(listOf(FooterItem.LoadingItem)) - } - LoadingState.Complete -> { - listFooterData.postValue(emptyList()) - view.hideInitialLoadProgress() - } - LoadingState.InitialLoad -> { - view.hideEmptyText() - view.showInitialLoadInProgress() + private fun onLoadingState(it: LoadingState) = + when (it) { + LoadingState.Loading -> { + view.hideEmptyText() + listFooterData.postValue(listOf(FooterItem.LoadingItem)) + } + LoadingState.Complete -> { + listFooterData.postValue(emptyList()) + view.hideInitialLoadProgress() + } + LoadingState.InitialLoad -> { + view.hideEmptyText() + view.showInitialLoadInProgress() + } + LoadingState.Error -> { + view.showSnackbar() + view.hideInitialLoadProgress() + listFooterData.postValue(listOf(FooterItem.RefreshItem)) + } } - LoadingState.Error -> { - view.showSnackbar() - view.hideInitialLoadProgress() - listFooterData.postValue(listOf(FooterItem.RefreshItem)) - } - } override fun retryFailedRequest() { pageableBaseDataSource.retryFailedRequest() } override fun onDetachView() { - view = DUMMY + view = dummy compositeDisposable.clear() } override fun onQueryUpdated(query: String) { pageableBaseDataSource.onQueryUpdated(query) } - } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/paging/BaseViewHolder.kt b/app/src/main/java/fr/free/nrw/commons/explore/paging/BaseViewHolder.kt index fda1408bf4..5a978c864f 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/paging/BaseViewHolder.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/paging/BaseViewHolder.kt @@ -4,7 +4,9 @@ import android.view.View import androidx.recyclerview.widget.RecyclerView import kotlinx.android.extensions.LayoutContainer -abstract class BaseViewHolder(override val containerView: View) : - RecyclerView.ViewHolder(containerView), LayoutContainer { +abstract class BaseViewHolder( + override val containerView: View, +) : RecyclerView.ViewHolder(containerView), + LayoutContainer { abstract fun bind(item: T) } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/paging/FooterAdapter.kt b/app/src/main/java/fr/free/nrw/commons/explore/paging/FooterAdapter.kt index 7c64609672..fc5f529b28 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/paging/FooterAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/paging/FooterAdapter.kt @@ -11,40 +11,58 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.databinding.ListItemLoadMoreBinding import kotlinx.android.extensions.LayoutContainer -class FooterAdapter(private val onRefreshClicked: () -> Unit) : - ListAdapter(object : - DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: FooterItem, newItem: FooterItem) = oldItem == newItem +class FooterAdapter( + private val onRefreshClicked: () -> Unit, +) : ListAdapter( + object : + DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: FooterItem, + newItem: FooterItem, + ) = oldItem == newItem - override fun areContentsTheSame(oldItem: FooterItem, newItem: FooterItem) = - oldItem == newItem - }) { + override fun areContentsTheSame( + oldItem: FooterItem, + newItem: FooterItem, + ) = oldItem == newItem + }, + ) { + override fun getItemViewType(position: Int): Int = getItem(position).ordinal - override fun getItemViewType(position: Int): Int { - return getItem(position).ordinal - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = - when (FooterItem.values()[viewType]) { - FooterItem.LoadingItem -> LoadingViewHolder( - parent.inflate(R.layout.list_item_progress) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ) = when (FooterItem.values()[viewType]) { + FooterItem.LoadingItem -> + LoadingViewHolder( + parent.inflate(R.layout.list_item_progress), ) - FooterItem.RefreshItem -> RefreshViewHolder( + FooterItem.RefreshItem -> + RefreshViewHolder( parent.inflate(R.layout.list_item_load_more), - onRefreshClicked + onRefreshClicked, ) - } + } - override fun onBindViewHolder(holder: FooterViewHolder, position: Int) {} + override fun onBindViewHolder( + holder: FooterViewHolder, + position: Int, + ) {} } -open class FooterViewHolder(override val containerView: View) : - RecyclerView.ViewHolder(containerView), +open class FooterViewHolder( + override val containerView: View, +) : RecyclerView.ViewHolder(containerView), LayoutContainer -class LoadingViewHolder(containerView: View) : FooterViewHolder(containerView) -class RefreshViewHolder(containerView: View, onRefreshClicked: () -> Unit) : - FooterViewHolder(containerView) { +class LoadingViewHolder( + containerView: View, +) : FooterViewHolder(containerView) + +class RefreshViewHolder( + containerView: View, + onRefreshClicked: () -> Unit, +) : FooterViewHolder(containerView) { val binding = ListItemLoadMoreBinding.bind(itemView) init { @@ -54,5 +72,7 @@ class RefreshViewHolder(containerView: View, onRefreshClicked: () -> Unit) : enum class FooterItem { LoadingItem, RefreshItem } -fun ViewGroup.inflate(@LayoutRes layoutId: Int, attachToRoot: Boolean = false): View = - LayoutInflater.from(context).inflate(layoutId, this, attachToRoot) +fun ViewGroup.inflate( + @LayoutRes layoutId: Int, + attachToRoot: Boolean = false, +): View = LayoutInflater.from(context).inflate(layoutId, this, attachToRoot) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingContract.kt b/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingContract.kt index d7169d4586..428fb36d6d 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingContract.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingContract.kt @@ -7,16 +7,23 @@ import fr.free.nrw.commons.BasePresenter interface PagingContract { interface View { fun showSnackbar() + fun observePagingResults(searchResults: LiveData>) + fun showInitialLoadInProgress() + fun hideInitialLoadProgress() + fun showEmptyText(query: String) + fun hideEmptyText() } interface Presenter : BasePresenter> { val listFooterData: LiveData> + fun onQueryUpdated(query: String) + fun retryFailedRequest() } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingDataSource.kt b/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingDataSource.kt index ed67a71b1b..84dda5c38f 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingDataSource.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingDataSource.kt @@ -8,38 +8,42 @@ import io.reactivex.schedulers.Schedulers import timber.log.Timber abstract class PagingDataSource( - private val loadingStates: PublishProcessor + private val loadingStates: PublishProcessor, ) : PositionalDataSource() { - private var lastExecutedRequest: (() -> Boolean)? = null + private fun storeAndExecute(function: () -> Boolean) { function.also { lastExecutedRequest = it }.invoke() } - private fun performWithTryCatch(function: () -> Unit) = try { - function.invoke() - loadingStates.offer(LoadingState.Complete) - } catch (e: Exception) { - Timber.e(e) - loadingStates.offer(LoadingState.Error) - } + private fun performWithTryCatch(function: () -> Unit) = + try { + function.invoke() + loadingStates.offer(LoadingState.Complete) + } catch (e: Exception) { + Timber.e(e) + loadingStates.offer(LoadingState.Error) + } override fun loadInitial( params: LoadInitialParams, - callback: LoadInitialCallback + callback: LoadInitialCallback, ) { storeAndExecute { loadingStates.offer(LoadingState.InitialLoad) performWithTryCatch { callback.onResult( getItems(params.requestedLoadSize, params.requestedStartPosition), - params.requestedStartPosition + params.requestedStartPosition, ) } } } - override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback) { + override fun loadRange( + params: LoadRangeParams, + callback: LoadRangeCallback, + ) { storeAndExecute { loadingStates.offer(LoadingState.Loading) performWithTryCatch { @@ -48,10 +52,14 @@ abstract class PagingDataSource( } } - protected abstract fun getItems(loadSize: Int, startPosition: Int): List + protected abstract fun getItems( + loadSize: Int, + startPosition: Int, + ): List fun retryFailedRequest() { - Completable.fromAction { lastExecutedRequest?.invoke() } + Completable + .fromAction { lastExecutedRequest?.invoke() } .subscribeOn(Schedulers.io()) .subscribe() } @@ -59,9 +67,10 @@ abstract class PagingDataSource( fun dataSource( loadingStates: PublishProcessor, - loadFunction: LoadFunction + loadFunction: LoadFunction, ) = object : PagingDataSource(loadingStates) { - override fun getItems(loadSize: Int, startPosition: Int): List { - return loadFunction(loadSize, startPosition) - } + override fun getItems( + loadSize: Int, + startPosition: Int, + ): List = loadFunction(loadSize, startPosition) } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingDataSourceFactory.kt b/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingDataSourceFactory.kt index 6cce5bcf62..10529e29f0 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingDataSourceFactory.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/paging/PagingDataSourceFactory.kt @@ -14,13 +14,14 @@ import javax.inject.Inject private const val PAGE_SIZE = 50 private const val INITIAL_LOAD_SIZE = 50 -abstract class PageableBaseDataSource(private val liveDataConverter: LiveDataConverter) { - +abstract class PageableBaseDataSource( + private val liveDataConverter: LiveDataConverter, +) { lateinit var query: String private val dataSourceFactoryFactory: () -> PagingDataSourceFactory = { dataSourceFactory( _loadingStates, - loadFunction + loadFunction, ) } private val _loadingStates = PublishProcessor.create() @@ -28,7 +29,7 @@ abstract class PageableBaseDataSource(private val liveDataConverter: LiveData private val _pagingResults = PublishProcessor.create>>() val pagingResults: Flowable>> = _pagingResults private val _noItemsLoadedEvent = PublishProcessor.create() - val noItemsLoadedQueries: Flowable = _noItemsLoadedEvent + val noItemsLoadedEvent: Flowable = _noItemsLoadedEvent private var currentFactory: PagingDataSourceFactory? = null abstract val loadFunction: LoadFunction @@ -38,7 +39,7 @@ abstract class PageableBaseDataSource(private val liveDataConverter: LiveData _pagingResults.offer( liveDataConverter.convert(dataSourceFactoryFactory().also { currentFactory = it }) { _noItemsLoadedEvent.offer(query) - } + }, ) } @@ -47,52 +48,58 @@ abstract class PageableBaseDataSource(private val liveDataConverter: LiveData } } -class LiveDataConverter @Inject constructor() { - fun convert( - dataSourceFactory: PagingDataSourceFactory, - zeroItemsLoadedFunction: () -> Unit - ): LiveData> { - return dataSourceFactory.toLiveData( - Config( - pageSize = PAGE_SIZE, - initialLoadSizeHint = INITIAL_LOAD_SIZE, - enablePlaceholders = false - ), - boundaryCallback = object : PagedList.BoundaryCallback() { - override fun onZeroItemsLoaded() { - zeroItemsLoadedFunction() - } - } - ) +class LiveDataConverter + @Inject + constructor() { + fun convert( + dataSourceFactory: PagingDataSourceFactory, + zeroItemsLoadedFunction: () -> Unit, + ): LiveData> = + dataSourceFactory.toLiveData( + Config( + pageSize = PAGE_SIZE, + initialLoadSizeHint = INITIAL_LOAD_SIZE, + enablePlaceholders = false, + ), + boundaryCallback = + object : PagedList.BoundaryCallback() { + override fun onZeroItemsLoaded() { + zeroItemsLoadedFunction() + } + }, + ) } -} - -abstract class PagingDataSourceFactory(val loadingStates: LoadingStates) : - DataSource.Factory() { +abstract class PagingDataSourceFactory( + val loadingStates: LoadingStates, +) : DataSource.Factory() { private var currentDataSource: PagingDataSource? = null abstract val loadFunction: LoadFunction override fun create() = dataSource( loadingStates, - loadFunction + loadFunction, ).also { currentDataSource = it } fun retryFailedRequest() { currentDataSource?.retryFailedRequest() } - } -fun dataSourceFactory(loadingStates: LoadingStates, loadFunction: LoadFunction) = - object : PagingDataSourceFactory(loadingStates) { - override val loadFunction: LoadFunction = loadFunction - } +fun dataSourceFactory( + loadingStates: LoadingStates, + loadFunction: LoadFunction, +) = object : PagingDataSourceFactory(loadingStates) { + override val loadFunction: LoadFunction = loadFunction +} sealed class LoadingState { object InitialLoad : LoadingState() + object Loading : LoadingState() + object Complete : LoadingState() + object Error : LoadingState() } diff --git a/app/src/main/java/fr/free/nrw/commons/media/IdAndCaptions.kt b/app/src/main/java/fr/free/nrw/commons/media/IdAndCaptions.kt index f9e6b4aeec..fe96eb8cb0 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/IdAndCaptions.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/IdAndCaptions.kt @@ -1,4 +1,6 @@ package fr.free.nrw.commons.media -data class IdAndCaptions(val id: String, val captions: Map) - +data class IdAndCaptions( + val id: String, + val captions: Map, +) diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt index 72ec08924e..3728a72029 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaClient.kt @@ -6,10 +6,10 @@ import fr.free.nrw.commons.category.ContinuationClient import fr.free.nrw.commons.explore.media.MediaConverter import fr.free.nrw.commons.utils.CommonsDateUtil import fr.free.nrw.commons.wikidata.model.Entities -import io.reactivex.Single import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse -import java.util.* +import io.reactivex.Single +import java.util.Date import javax.inject.Inject import javax.inject.Singleton @@ -22,199 +22,200 @@ const val RADIUS = 10000 * Media Client to handle custom calls to Commons MediaWiki APIs */ @Singleton -class MediaClient @Inject constructor( - private val mediaInterface: MediaInterface, - private val pageMediaInterface: PageMediaInterface, - private val mediaDetailInterface: MediaDetailInterface, - private val mediaConverter: MediaConverter -) : ContinuationClient() { - - fun getMediaById(id: String) = - responseMapper(mediaInterface.getMediaById(id)).map { it.first() } - - /** - * Checks if a page exists on Commons - * The same method can be used to check for file or talk page - * - * @param title File:Test.jpg or Commons:Deletion_requests/File:Test1.jpeg - */ - fun checkPageExistsUsingTitle(title: String?): Single { - return mediaInterface.checkPageExistsUsingTitle(title) - .map { it.query()!!.firstPage()!!.pageId() > 0 } - } - - /** - * Take the fileSha and returns whether a file with a matching SHA exists or not - * - * @param fileSha SHA of the file to be checked - */ - fun checkFileExistsUsingSha(fileSha: String?): Single { - return mediaInterface.checkFileExistsUsingSha(fileSha) - .map { it.query()!!.allImages().size > 0 } - } - - /** - * This method takes the category as input and returns a list of Media objects filtered using image generator query - * It uses the generator query API to get the images searched using a query, 10 at a time. - * - * @param category the search category. Must start with "Category:" - * @return - */ - fun getMediaListFromCategory(category: String): Single> { - return continuationRequest(CATEGORY_CONTINUATION_PREFIX, category) { - mediaInterface.getMediaListFromCategory(category, 10, it) - } - } - - /** - * This method takes the userName as input and returns a list of Media objects filtered using - * allimages query It uses the allimages query API to get the images contributed by the userName, - * 10 at a time. - * - * @param userName the username - * @return - */ - fun getMediaListForUser(userName: String): Single> { - return continuationRequest("user_", userName) { - mediaInterface.getMediaListForUser(userName, 10, it) - } - } - - /** - * This method takes a keyword as input and returns a list of Media objects filtered using image generator query - * It uses the generator query API to get the images searched using a query, 10 at a time. - * - * @param keyword the search keyword - * @param limit - * @param offset - * @return - */ - fun getMediaListFromSearch(keyword: String?, limit: Int, offset: Int) = - responseMapper(mediaInterface.getMediaListFromSearch(keyword, limit, offset)) - - /** - * This method takes coordinate as input and returns a list of Media objects. - * It uses the generator query API to get the images searched using a query. - * - * @param coordinate coordinate - * @return - */ - fun getMediaListFromGeoSearch(coordinate: String?) = - responseMapper(mediaInterface.getMediaListFromGeoSearch(coordinate, LIMIT, RADIUS)) - - /** - * @return list of images for a particular depict entity - */ - fun fetchImagesForDepictedItem( - query: String, - srlimit: Int, - sroffset: Int - ): Single> { - return responseMapper( - mediaInterface.fetchImagesForDepictedItem( - "haswbstatement:" + BuildConfig.DEPICTS_PROPERTY + "=" + query, - srlimit.toString(), - sroffset.toString() +class MediaClient + @Inject + constructor( + private val mediaInterface: MediaInterface, + private val pageMediaInterface: PageMediaInterface, + private val mediaDetailInterface: MediaDetailInterface, + private val mediaConverter: MediaConverter, + ) : ContinuationClient() { + fun getMediaById(id: String) = responseMapper(mediaInterface.getMediaById(id)).map { it.first() } + + /** + * Checks if a page exists on Commons + * The same method can be used to check for file or talk page + * + * @param title File:Test.jpg or Commons:Deletion_requests/File:Test1.jpeg + */ + fun checkPageExistsUsingTitle(title: String?): Single = + mediaInterface + .checkPageExistsUsingTitle(title) + .map { it.query()!!.firstPage()!!.pageId() > 0 } + + /** + * Take the fileSha and returns whether a file with a matching SHA exists or not + * + * @param fileSha SHA of the file to be checked + */ + fun checkFileExistsUsingSha(fileSha: String?): Single = + mediaInterface + .checkFileExistsUsingSha(fileSha) + .map { it.query()!!.allImages().size > 0 } + + /** + * This method takes the category as input and returns a list of Media objects filtered using image generator query + * It uses the generator query API to get the images searched using a query, 10 at a time. + * + * @param category the search category. Must start with "Category:" + * @return + */ + fun getMediaListFromCategory(category: String): Single> = + continuationRequest(CATEGORY_CONTINUATION_PREFIX, category) { + mediaInterface.getMediaListFromCategory(category, 10, it) + } + + /** + * This method takes the userName as input and returns a list of Media objects filtered using + * allimages query It uses the allimages query API to get the images contributed by the userName, + * 10 at a time. + * + * @param userName the username + * @return + */ + fun getMediaListForUser(userName: String): Single> = + continuationRequest("user_", userName) { + mediaInterface.getMediaListForUser(userName, 10, it) + } + + /** + * This method takes a keyword as input and returns a list of Media objects filtered using image generator query + * It uses the generator query API to get the images searched using a query, 10 at a time. + * + * @param keyword the search keyword + * @param limit + * @param offset + * @return + */ + fun getMediaListFromSearch( + keyword: String?, + limit: Int, + offset: Int, + ) = responseMapper(mediaInterface.getMediaListFromSearch(keyword, limit, offset)) + + /** + * This method takes coordinate as input and returns a list of Media objects. + * It uses the generator query API to get the images searched using a query. + * + * @param coordinate coordinate + * @return + */ + fun getMediaListFromGeoSearch(coordinate: String?) = + responseMapper(mediaInterface.getMediaListFromGeoSearch(coordinate, LIMIT, RADIUS)) + + /** + * @return list of images for a particular depict entity + */ + fun fetchImagesForDepictedItem( + query: String, + srlimit: Int, + sroffset: Int, + ): Single> = + responseMapper( + mediaInterface.fetchImagesForDepictedItem( + "haswbstatement:" + BuildConfig.DEPICTS_PROPERTY + "=" + query, + srlimit.toString(), + sroffset.toString(), + ), ) - ) - } - - /** - * Fetches Media object from the imageInfo API - * - * @param titles the tiles to be searched for. Can be filename or template name - * @return - */ - fun getMedia(titles: String?): Single { - return responseMapper(mediaInterface.getMedia(titles)) - .map { it.first() } - } - - /** - * Fetches Media object from the imageInfo API but suppress (known) errors - * - * @param titles the tiles to be searched for. Can be filename or template name - * @return - */ - fun getMediaSuppressingErrors(titles: String?): Single { - return responseMapper(mediaInterface.getMediaSuppressingErrors(titles)) - .map { it.first() } - } - - /** - * The method returns the picture of the day - * - * @return Media object corresponding to the picture of the day - */ - fun getPictureOfTheDay(): Single { - val date = CommonsDateUtil.getIso8601DateFormatShort().format(Date()) - return responseMapper(mediaInterface.getMediaWithGenerator("Template:Potd/$date")).map { it.first() } - } - - fun getPageHtml(title: String?): Single { - return mediaInterface.getPageHtml(title) - .map { obj: MwParseResponse -> obj.parse()?.text() ?: "" } - } - - fun getEntities(entityIds: List): Single { - return if (entityIds.isEmpty()) - Single.error(Exception("empty list passed for ids")) - else - mediaDetailInterface.getEntity(entityIds.joinToString("|")) - } - - fun doesPageContainMedia(title: String?): Single { - return pageMediaInterface.getMediaList(title) - .map { it.items.isNotEmpty() } - } - - fun resetCategoryContinuation(category: String) { - resetContinuation(CATEGORY_CONTINUATION_PREFIX, category) - } - /** - * Call the resetUserContinuation method - * - * @param userName the username - */ - fun resetUserNameContinuation(userName: String) = - resetUserContinuation("user_", userName) - - /** - * Get whole WikiText of required file - * @param title : Name of the file - * @return Observable - */ - fun getCurrentWikiText(title: String): Single { - return mediaDetailInterface.getWikiText(title).map { - it.query()?.pages()?.get(0)?.revisions()?.get(0)?.content() + /** + * Fetches Media object from the imageInfo API + * + * @param titles the tiles to be searched for. Can be filename or template name + * @return + */ + fun getMedia(titles: String?): Single = + responseMapper(mediaInterface.getMedia(titles)) + .map { it.first() } + + /** + * Fetches Media object from the imageInfo API but suppress (known) errors + * + * @param titles the tiles to be searched for. Can be filename or template name + * @return + */ + fun getMediaSuppressingErrors(titles: String?): Single = + responseMapper(mediaInterface.getMediaSuppressingErrors(titles)) + .map { it.first() } + + /** + * The method returns the picture of the day + * + * @return Media object corresponding to the picture of the day + */ + fun getPictureOfTheDay(): Single { + val date = CommonsDateUtil.getIso8601DateFormatShort().format(Date()) + return responseMapper(mediaInterface.getMediaWithGenerator("Template:Potd/$date")).map { it.first() } } - } - override fun responseMapper( - networkResult: Single, - key: String? - ): Single> { - return networkResult.map { - handleContinuationResponse(it.continuation(), key) - it.query()?.pages() ?: emptyList() - }.flatMap(::mediaFromPageAndEntity) - } + fun getPageHtml(title: String?): Single = + mediaInterface + .getPageHtml(title) + .map { obj: MwParseResponse -> obj.parse()?.text() ?: "" } + + fun getEntities(entityIds: List): Single = + if (entityIds.isEmpty()) { + Single.error(Exception("empty list passed for ids")) + } else { + mediaDetailInterface.getEntity(entityIds.joinToString("|")) + } + + fun doesPageContainMedia(title: String?): Single = + pageMediaInterface + .getMediaList(title) + .map { it.items.isNotEmpty() } + + fun resetCategoryContinuation(category: String) { + resetContinuation(CATEGORY_CONTINUATION_PREFIX, category) + } - private fun mediaFromPageAndEntity(pages: List): Single> { - return if (pages.isEmpty()) { - Single.just(emptyList()) - } else { - getEntities(pages.map { "$PAGE_ID_PREFIX${it.pageId()}" }) + /** + * Call the resetUserContinuation method + * + * @param userName the username + */ + fun resetUserNameContinuation(userName: String) = resetUserContinuation("user_", userName) + + /** + * Get whole WikiText of required file + * @param title : Name of the file + * @return Observable + */ + fun getCurrentWikiText(title: String): Single = + mediaDetailInterface.getWikiText(title).map { + it + .query() + ?.pages() + ?.get(0) + ?.revisions() + ?.get(0) + ?.content() + } + + override fun responseMapper( + networkResult: Single, + key: String?, + ): Single> = + networkResult .map { - pages.zip(it.entities().values) - .mapNotNull { (page, entity) -> - page.imageInfo()?.let { - mediaConverter.convert(page, entity, it) + handleContinuationResponse(it.continuation(), key) + it.query()?.pages() ?: emptyList() + }.flatMap(::mediaFromPageAndEntity) + + private fun mediaFromPageAndEntity(pages: List): Single> = + if (pages.isEmpty()) { + Single.just(emptyList()) + } else { + getEntities(pages.map { "$PAGE_ID_PREFIX${it.pageId()}" }) + .map { + pages + .zip(it.entities().values) + .mapNotNull { (page, entity) -> + page.imageInfo()?.let { + mediaConverter.convert(page, entity, it) + } } - } - } - } - + } + } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailInterface.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailInterface.kt index a77271d787..10b375340e 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailInterface.kt @@ -20,7 +20,7 @@ interface MediaDetailInterface { @GET("w/api.php?action=wbgetentities&props=labels&format=json&languagefallback=1&sites=commonswiki") fun fetchEntitiesByFileName( @Query("languages") language: String?, - @Query("titles") filename: String? + @Query("titles") filename: String?, ): Observable /** @@ -28,7 +28,9 @@ interface MediaDetailInterface { * @param entityId EntityId (Ex: Q81566) of the depict entity */ @GET("/w/api.php?format=json&action=wbgetentities&props=labels&languagefallback=1") - fun getEntity(@Query("ids") entityId: String?): Single + fun getEntity( + @Query("ids") entityId: String?, + ): Single /** * Fetches caption using wikibaseIdentifier @@ -38,16 +40,16 @@ interface MediaDetailInterface { @GET("/w/api.php?action=wbgetentities&props=labels&format=json&languagefallback=1&sites=commonswiki") fun getEntityForImage( @Query("languages") language: String?, - @Query("ids") wikibaseIdentifier: String? + @Query("ids") wikibaseIdentifier: String?, ): Observable /** * Fetches current wikitext * @param title file name * @return Single - */ + */ @GET(WikidataConstants.MW_API_PREFIX + "action=query&prop=revisions&rvprop=content|timestamp&rvlimit=1&converttitles=") fun getWikiText( - @Query("titles") title: String? + @Query("titles") title: String?, ): Single } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.kt index c1b2f7ab57..ef0ef1f9ca 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaInterface.kt @@ -19,7 +19,9 @@ interface MediaInterface { * @return */ @GET("w/api.php?action=query&format=json&formatversion=2") - fun checkPageExistsUsingTitle(@Query("titles") title: String?): Single + fun checkPageExistsUsingTitle( + @Query("titles") title: String?, + ): Single /** * Check if file exists @@ -28,7 +30,9 @@ interface MediaInterface { * @return */ @GET("w/api.php?action=query&format=json&formatversion=2&list=allimages") - fun checkFileExistsUsingSha(@Query("aisha1") aisha1: String?): Single + fun checkFileExistsUsingSha( + @Query("aisha1") aisha1: String?, + ): Single /** * This method retrieves a list of Media objects filtered using image generator query @@ -39,13 +43,13 @@ interface MediaInterface { * @return */ @GET( - "w/api.php?action=query&format=json&formatversion=2" + //Basic parameters - "&generator=categorymembers&gcmtype=file&gcmsort=timestamp&gcmdir=desc$MEDIA_PARAMS" //Category parameters + "w/api.php?action=query&format=json&formatversion=2" + // Basic parameters + "&generator=categorymembers&gcmtype=file&gcmsort=timestamp&gcmdir=desc$MEDIA_PARAMS", // Category parameters ) fun getMediaListFromCategory( @Query("gcmtitle") category: String?, @Query("gcmlimit") itemLimit: Int, - @QueryMap continuation: Map + @QueryMap continuation: Map, ): Single /** @@ -57,13 +61,13 @@ interface MediaInterface { * @return */ @GET( - "w/api.php?action=query&format=json&formatversion=2" + //Basic parameters - "&generator=allimages&gaisort=timestamp&gaidir=older$MEDIA_PARAMS" + "w/api.php?action=query&format=json&formatversion=2" + // Basic parameters + "&generator=allimages&gaisort=timestamp&gaidir=older$MEDIA_PARAMS", ) fun getMediaListForUser( @Query("gaiuser") username: String?, @Query("gailimit") itemLimit: Int, - @QueryMap(encoded = true) continuation: Map + @QueryMap(encoded = true) continuation: Map, ): Single /** @@ -75,12 +79,13 @@ interface MediaInterface { * @return */ @GET( - "w/api.php?action=query&format=json&formatversion=2" + //Basic parameters - "&generator=search&gsrwhat=text&gsrnamespace=6$MEDIA_PARAMS" //Search parameters + "w/api.php?action=query&format=json&formatversion=2" + // Basic parameters + "&generator=search&gsrwhat=text&gsrnamespace=6$MEDIA_PARAMS", // Search parameters ) fun getMediaListFromSearch( @Query("gsrsearch") keyword: String?, - @Query("gsrlimit") itemLimit: Int, @Query("gsroffset") offset: Int + @Query("gsrlimit") itemLimit: Int, + @Query("gsroffset") offset: Int, ): Single /** @@ -91,13 +96,13 @@ interface MediaInterface { * @return */ @GET( - "w/api.php?action=query&format=json&formatversion=2" + //Basic parameters - "&generator=geosearch&ggsnamespace=6$MEDIA_PARAMS" //Search parameters + "w/api.php?action=query&format=json&formatversion=2" + // Basic parameters + "&generator=geosearch&ggsnamespace=6$MEDIA_PARAMS", // Search parameters ) fun getMediaListFromGeoSearch( @Query("ggscoord") location: String?, @Query("ggslimit") itemLimit: Int, - @Query("ggsradius") radius: Int + @Query("ggsradius") radius: Int, ): Single /** @@ -107,7 +112,9 @@ interface MediaInterface { * @return */ @GET("w/api.php?action=query&format=json&formatversion=2$MEDIA_PARAMS_WITH_CATEGORY_DETAILS") - fun getMedia(@Query("titles") title: String?): Single + fun getMedia( + @Query("titles") title: String?, + ): Single /** * Fetches Media object from the imageInfo API but suppress (known) errors @@ -117,7 +124,9 @@ interface MediaInterface { */ @GET("w/api.php?action=query&format=json&formatversion=2$MEDIA_PARAMS_WITH_CATEGORY_DETAILS") @Headers(SUPPRESS_ERROR_LOG_HEADER) - fun getMediaSuppressingErrors(@Query("titles") title: String?): Single + fun getMediaSuppressingErrors( + @Query("titles") title: String?, + ): Single /** * Fetches Media object from the imageInfo API @@ -127,7 +136,9 @@ interface MediaInterface { */ @GET("w/api.php?action=query&format=json&formatversion=2$MEDIA_PARAMS") @Headers(SUPPRESS_ERROR_LOG_HEADER) - fun getMediaById(@Query("pageids") pageIds: String?): Single + fun getMediaById( + @Query("pageids") pageIds: String?, + ): Single /** * Fetches Media object from the imageInfo API @@ -137,11 +148,15 @@ interface MediaInterface { * @return */ @GET("w/api.php?action=query&format=json&formatversion=2&generator=images$MEDIA_PARAMS") - fun getMediaWithGenerator(@Query("titles") title: String?): Single + fun getMediaWithGenerator( + @Query("titles") title: String?, + ): Single @GET("w/api.php?format=json&action=parse&prop=text") @Headers(SUPPRESS_ERROR_LOG_HEADER) - fun getPageHtml(@Query("page") title: String?): Single + fun getPageHtml( + @Query("page") title: String?, + ): Single /** * Fetches caption using file name @@ -151,7 +166,7 @@ interface MediaInterface { @GET("w/api.php?action=wbgetentities&props=labels&format=json&languagefallback=1") fun fetchCaptionByFilename( @Query("language") language: String?, - @Query("titles") filename: String? + @Query("titles") filename: String?, ): Single /** @@ -161,12 +176,13 @@ interface MediaInterface { * @param sroffset number od depictions already fetched, this is useful in implementing pagination */ @GET( - "w/api.php?action=query&format=json&formatversion=2" + //Basic parameters - "&generator=search&gsrnamespace=6$MEDIA_PARAMS" //Search parameters + "w/api.php?action=query&format=json&formatversion=2" + // Basic parameters + "&generator=search&gsrnamespace=6$MEDIA_PARAMS", // Search parameters ) fun fetchImagesForDepictedItem( @Query("gsrsearch") query: String?, - @Query("gsrlimit") srlimit: String?, @Query("gsroffset") sroffset: String? + @Query("gsrlimit") srlimit: String?, + @Query("gsroffset") sroffset: String?, ): Single companion object { diff --git a/app/src/main/java/fr/free/nrw/commons/media/PageMediaInterface.kt b/app/src/main/java/fr/free/nrw/commons/media/PageMediaInterface.kt index 2e8927778a..c229c2df69 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/PageMediaInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/PageMediaInterface.kt @@ -15,5 +15,7 @@ interface PageMediaInterface { * @param title the title of the page */ @GET("api/rest_v1/page/media-list/{title}") - fun getMediaList(@Path("title") title: String?): Single + fun getMediaList( + @Path("title") title: String?, + ): Single } diff --git a/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaClient.kt b/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaClient.kt index 9fb7284435..2ad90d3f2f 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaClient.kt @@ -5,9 +5,9 @@ import fr.free.nrw.commons.Media import fr.free.nrw.commons.category.ContinuationClient import fr.free.nrw.commons.explore.media.MediaConverter import fr.free.nrw.commons.wikidata.model.Entities -import io.reactivex.Single import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse +import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -15,79 +15,79 @@ import javax.inject.Singleton * Media Client to handle custom calls to Commons MediaWiki APIs of production server */ @Singleton -class WikidataMediaClient @Inject constructor( - private val wikidataMediaInterface: WikidataMediaInterface, - private val mediaDetailInterface: MediaDetailInterface, - private val mediaConverter: MediaConverter -) : ContinuationClient() { - - /** - * Fetch images for depict ID - * - * @param query depictionEntityId ex. "Q9394" - * @param srlimit the number of items to fetch - * @param sroffset number of depictions already fetched, - * this is useful in implementing pagination - * @return list of images for a particular depict ID - */ - fun fetchImagesForDepictedItem( - query: String, - srlimit: Int, - sroffset: Int - ): Single> { - return responseMapper( - wikidataMediaInterface.fetchImagesForDepictedItem( - "haswbstatement:" + BetaConstants.DEPICTS_PROPERTY + "=" + query, - srlimit.toString(), - sroffset.toString() +class WikidataMediaClient + @Inject + constructor( + private val wikidataMediaInterface: WikidataMediaInterface, + private val mediaDetailInterface: MediaDetailInterface, + private val mediaConverter: MediaConverter, + ) : ContinuationClient() { + /** + * Fetch images for depict ID + * + * @param query depictionEntityId ex. "Q9394" + * @param srlimit the number of items to fetch + * @param sroffset number of depictions already fetched, + * this is useful in implementing pagination + * @return list of images for a particular depict ID + */ + fun fetchImagesForDepictedItem( + query: String, + srlimit: Int, + sroffset: Int, + ): Single> = + responseMapper( + wikidataMediaInterface.fetchImagesForDepictedItem( + "haswbstatement:" + BetaConstants.DEPICTS_PROPERTY + "=" + query, + srlimit.toString(), + sroffset.toString(), + ), ) - ) - } - /** - * Helps to map to the required data from the API response - * - * @param networkResult MwQueryResponse - * @param key for handling continuation request, this is null in this case - */ - override fun responseMapper( - networkResult: Single, - key: String? - ): Single> { - return networkResult.map { - handleContinuationResponse(it.continuation(), key) - it.query()?.pages() ?: emptyList() - }.flatMap(::mediaFromPageAndEntity) - } - - /** - * Gets list of Media from MwQueryPage - */ - private fun mediaFromPageAndEntity(pages: List): Single> { - return if (pages.isEmpty()) - Single.just(emptyList()) - else - getEntities(pages.map { "$PAGE_ID_PREFIX${it.pageId()}" }) + /** + * Helps to map to the required data from the API response + * + * @param networkResult MwQueryResponse + * @param key for handling continuation request, this is null in this case + */ + override fun responseMapper( + networkResult: Single, + key: String?, + ): Single> = + networkResult .map { - pages.zip(it.entities().values) - .mapNotNull { (page, entity) -> - page.imageInfo()?.let { - mediaConverter.convert(page, entity, it) + handleContinuationResponse(it.continuation(), key) + it.query()?.pages() ?: emptyList() + }.flatMap(::mediaFromPageAndEntity) + + /** + * Gets list of Media from MwQueryPage + */ + private fun mediaFromPageAndEntity(pages: List): Single> = + if (pages.isEmpty()) { + Single.just(emptyList()) + } else { + getEntities(pages.map { "$PAGE_ID_PREFIX${it.pageId()}" }) + .map { + pages + .zip(it.entities().values) + .mapNotNull { (page, entity) -> + page.imageInfo()?.let { + mediaConverter.convert(page, entity, it) + } } - } - } - } + } + } - /** - * Gets Entities from IDs - * - * @param entityIds list of IDs of pages/entities ex. {"M4254154", "M11413343"} - */ - fun getEntities(entityIds: List): Single { - return if (entityIds.isEmpty()) - Single.error(Exception("empty list passed for ids")) - else - mediaDetailInterface.getEntity(entityIds.joinToString("|")) + /** + * Gets Entities from IDs + * + * @param entityIds list of IDs of pages/entities ex. {"M4254154", "M11413343"} + */ + fun getEntities(entityIds: List): Single = + if (entityIds.isEmpty()) { + Single.error(Exception("empty list passed for ids")) + } else { + mediaDetailInterface.getEntity(entityIds.joinToString("|")) + } } - -} diff --git a/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaInterface.kt b/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaInterface.kt index f250f7ac75..323799e6fa 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/WikidataMediaInterface.kt @@ -17,13 +17,14 @@ interface WikidataMediaInterface { * @param sroffset number of depictions already fetched, * this is useful in implementing pagination * @return Single - */ + */ @GET( - "w/api.php?action=query&format=json&formatversion=2" + //Basic parameters - "&generator=search&gsrnamespace=6$MEDIA_PARAMS" //Search parameters + "w/api.php?action=query&format=json&formatversion=2" + // Basic parameters + "&generator=search&gsrnamespace=6$MEDIA_PARAMS", // Search parameters ) fun fetchImagesForDepictedItem( @Query("gsrsearch") query: String?, - @Query("gsrlimit") srlimit: String?, @Query("gsroffset") sroffset: String? + @Query("gsrlimit") srlimit: String?, + @Query("gsroffset") sroffset: String?, ): Single } diff --git a/app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.kt b/app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.kt index 615dd78ee8..d08e3048c3 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.kt @@ -10,10 +10,7 @@ import android.os.Bundle import android.view.View import android.view.Window import android.widget.Button -import android.widget.ProgressBar -import android.widget.TextView import android.widget.Toast -import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProvider import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.controller.BaseControllerListener @@ -38,12 +35,16 @@ import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorViewModel import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorViewModelFactory import fr.free.nrw.commons.databinding.ActivityZoomableBinding import fr.free.nrw.commons.media.zoomControllers.zoomable.DoubleTapGestureListener -import fr.free.nrw.commons.media.zoomControllers.zoomable.ZoomableDraweeView import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.upload.FileProcessor import fr.free.nrw.commons.upload.FileUtilsWrapper import fr.free.nrw.commons.utils.CustomSelectorUtils -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject import kotlin.collections.ArrayList @@ -53,7 +54,6 @@ import kotlin.collections.ArrayList * like zoom, and swap gestures */ class ZoomableActivity : BaseActivity() { - private lateinit var imageUri: Uri /** @@ -67,7 +67,7 @@ class ZoomableActivity : BaseActivity() { private lateinit var prefs: SharedPreferences private lateinit var binding: ActivityZoomableBinding - + var photoBackgroundColor: Int? = null /** @@ -126,36 +126,39 @@ class ZoomableActivity : BaseActivity() { lateinit var customSelectorViewModelFactory: CustomSelectorViewModelFactory /** - * Coroutine Dispatchers and Scope. - */ - private var defaultDispatcher : CoroutineDispatcher = Dispatchers.Default - private var ioDispatcher : CoroutineDispatcher = Dispatchers.IO - private val scope : CoroutineScope = MainScope() + * Coroutine Dispatchers and Scope. + */ + private var defaultDispatcher: CoroutineDispatcher = Dispatchers.Default + private var ioDispatcher: CoroutineDispatcher = Dispatchers.IO + private val scope: CoroutineScope = MainScope() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityZoomableBinding.inflate(layoutInflater) setContentView(binding.root) - prefs = applicationContext.getSharedPreferences( - ImageHelper.CUSTOM_SELECTOR_PREFERENCE_KEY, - MODE_PRIVATE - ) + prefs = + applicationContext.getSharedPreferences( + ImageHelper.CUSTOM_SELECTOR_PREFERENCE_KEY, + MODE_PRIVATE, + ) - selectedImages = intent.getParcelableArrayListExtra( - CustomSelectorConstants.TOTAL_SELECTED_IMAGES - ) + selectedImages = + intent.getParcelableArrayListExtra( + CustomSelectorConstants.TOTAL_SELECTED_IMAGES, + ) position = intent.getIntExtra(CustomSelectorConstants.PRESENT_POSITION, 0) bucketId = intent.getLongExtra(CustomSelectorConstants.BUCKET_ID, 0L) - viewModel = ViewModelProvider(this, customSelectorViewModelFactory).get( - CustomSelectorViewModel::class.java - ) + viewModel = + ViewModelProvider(this, customSelectorViewModelFactory).get( + CustomSelectorViewModel::class.java, + ) viewModel.fetchImages() viewModel.result.observe(this) { handleResult(it) } - val origin = intent.getStringExtra(ZoomableActivityConstants.ORIGIN); + val origin = intent.getStringExtra(ZoomableActivityConstants.ORIGIN) /** * If origin is "null" it means that ZoomableActivity was created by the custom picker @@ -166,15 +169,20 @@ class ZoomableActivity : BaseActivity() { if (prefs.getBoolean(CustomSelectorConstants.FULL_SCREEN_MODE_FIRST_LUNCH, true)) { // show welcome dialog on first launch showWelcomeDialog() - prefs.edit().putBoolean( - CustomSelectorConstants.FULL_SCREEN_MODE_FIRST_LUNCH, - false - ).apply() + prefs + .edit() + .putBoolean( + CustomSelectorConstants.FULL_SCREEN_MODE_FIRST_LUNCH, + false, + ).apply() } } - - val backgroundColor = intent.getIntExtra(ZoomableActivityConstants.PHOTO_BACKGROUND_COLOR, - MediaDetailFragment.DEFAULT_IMAGE_BACKGROUND_COLOR); + + val backgroundColor = + intent.getIntExtra( + ZoomableActivityConstants.PHOTO_BACKGROUND_COLOR, + MediaDetailFragment.DEFAULT_IMAGE_BACKGROUND_COLOR, + ) if (backgroundColor != MediaDetailFragment.DEFAULT_IMAGE_BACKGROUND_COLOR) { photoBackgroundColor = backgroundColor @@ -196,15 +204,16 @@ class ZoomableActivity : BaseActivity() { * Handle view model result. */ private fun handleResult(result: Result) { - if(result.status is CallbackStatus.SUCCESS){ + if (result.status is CallbackStatus.SUCCESS) { val images = result.images - if(images.isNotEmpty()) { + if (images.isNotEmpty()) { this@ZoomableActivity.images = ImageHelper.filterImages(images, bucketId) - imageUri = if (this@ZoomableActivity.images.isNullOrEmpty()) { - intent.data as Uri - } else { - this@ZoomableActivity.images!![position].uri - } + imageUri = + if (this@ZoomableActivity.images.isNullOrEmpty()) { + intent.data as Uri + } else { + this@ZoomableActivity.images!![position].uri + } Timber.d("URL = $imageUri") init(imageUri) onSwipe() @@ -225,34 +234,36 @@ class ZoomableActivity : BaseActivity() { sharedPreferences.getBoolean(ImageHelper.SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY, true) if (!images.isNullOrEmpty()) { - binding.zoomable!!.setOnTouchListener(object : OnSwipeTouchListener(this) { - // Swipe left to view next image in the folder. (if available) - override fun onSwipeLeft() { - super.onSwipeLeft() - onLeftSwiped(showAlreadyActionedImages) - } + binding.zoomable!!.setOnTouchListener( + object : OnSwipeTouchListener(this) { + // Swipe left to view next image in the folder. (if available) + override fun onSwipeLeft() { + super.onSwipeLeft() + onLeftSwiped(showAlreadyActionedImages) + } - // Swipe right to view previous image in the folder. (if available) - override fun onSwipeRight() { - super.onSwipeRight() - onRightSwiped(showAlreadyActionedImages) - } + // Swipe right to view previous image in the folder. (if available) + override fun onSwipeRight() { + super.onSwipeRight() + onRightSwiped(showAlreadyActionedImages) + } - // Swipe up to select the picture (the equivalent of tapping it in non-fullscreen mode) - // and show the next picture skipping pictures that have either already been uploaded or - // marked as not for upload - override fun onSwipeUp() { - super.onSwipeUp() - onUpSwiped() - } + // Swipe up to select the picture (the equivalent of tapping it in non-fullscreen mode) + // and show the next picture skipping pictures that have either already been uploaded or + // marked as not for upload + override fun onSwipeUp() { + super.onSwipeUp() + onUpSwiped() + } - // Swipe down to mark that picture as "Not for upload" (the equivalent of selecting it then - // tapping "Mark as not for upload" in non-fullscreen mode), and show the next picture. - override fun onSwipeDown() { - super.onSwipeDown() - onDownSwiped() - } - }) + // Swipe down to mark that picture as "Not for upload" (the equivalent of selecting it then + // tapping "Mark as not for upload" in non-fullscreen mode), and show the next picture. + override fun onSwipeDown() { + super.onSwipeDown() + onDownSwiped() + } + }, + ) } } @@ -260,58 +271,66 @@ class ZoomableActivity : BaseActivity() { * Handles down swipe action */ private fun onDownSwiped() { - if (binding.zoomable?.zoomableController?.isIdentity == false) + if (binding.zoomable?.zoomableController?.isIdentity == false) { return + } scope.launch { - val imageSHA1 = CustomSelectorUtils.getImageSHA1( - images!![position].uri, - ioDispatcher, - fileUtilsWrapper, - contentResolver - ) + val imageSHA1 = + CustomSelectorUtils.getImageSHA1( + images!![position].uri, + ioDispatcher, + fileUtilsWrapper, + contentResolver, + ) var isUploaded = uploadedStatusDao.findByImageSHA1(imageSHA1, true) if (isUploaded > 0) { - Toast.makeText( - this@ZoomableActivity, - getString(R.string.this_image_is_already_uploaded), - Toast.LENGTH_SHORT - ).show() - } else { - val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1( - images!![position], - defaultDispatcher, - this@ZoomableActivity, - fileProcessor, - fileUtilsWrapper - ) - isUploaded = uploadedStatusDao.findByModifiedImageSHA1( - imageModifiedSHA1, - true - ) - if (isUploaded > 0) { - Toast.makeText( + Toast + .makeText( this@ZoomableActivity, getString(R.string.this_image_is_already_uploaded), - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() + } else { + val imageModifiedSHA1 = + CustomSelectorUtils.generateModifiedSHA1( + images!![position], + defaultDispatcher, + this@ZoomableActivity, + fileProcessor, + fileUtilsWrapper, + ) + isUploaded = + uploadedStatusDao.findByModifiedImageSHA1( + imageModifiedSHA1, + true, + ) + if (isUploaded > 0) { + Toast + .makeText( + this@ZoomableActivity, + getString(R.string.this_image_is_already_uploaded), + Toast.LENGTH_SHORT, + ).show() } else { insertInNotForUpload(images!![position]) - Toast.makeText( - this@ZoomableActivity, - getString(R.string.image_marked_as_not_for_upload), - Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + this@ZoomableActivity, + getString(R.string.image_marked_as_not_for_upload), + Toast.LENGTH_SHORT, + ).show() shouldRefresh = true if (position < images!!.size - 1) { position++ init(images!![position].uri) } else { - Toast.makeText( - this@ZoomableActivity, - getString(R.string.no_more_images_found), - Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + this@ZoomableActivity, + getString(R.string.no_more_images_found), + Toast.LENGTH_SHORT, + ).show() } } } @@ -322,58 +341,66 @@ class ZoomableActivity : BaseActivity() { * Handles up swipe action */ private fun onUpSwiped() { - if (binding.zoomable?.zoomableController?.isIdentity == false) + if (binding.zoomable?.zoomableController?.isIdentity == false) { return + } scope.launch { - val imageSHA1 = CustomSelectorUtils.getImageSHA1( - images!![position].uri, - ioDispatcher, - fileUtilsWrapper, - contentResolver - ) + val imageSHA1 = + CustomSelectorUtils.getImageSHA1( + images!![position].uri, + ioDispatcher, + fileUtilsWrapper, + contentResolver, + ) var isNonActionable = notForUploadStatusDao.find(imageSHA1) if (isNonActionable > 0) { - Toast.makeText( - this@ZoomableActivity, - getString(R.string.can_not_select_this_image_for_upload), - Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + this@ZoomableActivity, + getString(R.string.can_not_select_this_image_for_upload), + Toast.LENGTH_SHORT, + ).show() } else { isNonActionable = uploadedStatusDao.findByImageSHA1(imageSHA1, true) if (isNonActionable > 0) { - Toast.makeText( - this@ZoomableActivity, - getString(R.string.this_image_is_already_uploaded), - Toast.LENGTH_SHORT - ).show() - } else { - val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1( - images!![position], - defaultDispatcher, - this@ZoomableActivity, - fileProcessor, - fileUtilsWrapper - ) - isNonActionable = uploadedStatusDao.findByModifiedImageSHA1( - imageModifiedSHA1, - true - ) - if (isNonActionable > 0) { - Toast.makeText( + Toast + .makeText( this@ZoomableActivity, getString(R.string.this_image_is_already_uploaded), - Toast.LENGTH_SHORT + Toast.LENGTH_SHORT, ).show() + } else { + val imageModifiedSHA1 = + CustomSelectorUtils.generateModifiedSHA1( + images!![position], + defaultDispatcher, + this@ZoomableActivity, + fileProcessor, + fileUtilsWrapper, + ) + isNonActionable = + uploadedStatusDao.findByModifiedImageSHA1( + imageModifiedSHA1, + true, + ) + if (isNonActionable > 0) { + Toast + .makeText( + this@ZoomableActivity, + getString(R.string.this_image_is_already_uploaded), + Toast.LENGTH_SHORT, + ).show() } else { if (!selectedImages!!.contains(images!![position])) { selectedImages!!.add(images!![position]) - Toast.makeText( - this@ZoomableActivity, - getString(R.string.image_selected), - Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + this@ZoomableActivity, + getString(R.string.image_selected), + Toast.LENGTH_SHORT, + ).show() } position = getNextActionableImage(position + 1) init(images!![position].uri) @@ -387,19 +414,21 @@ class ZoomableActivity : BaseActivity() { * Handles right swipe action */ private fun onRightSwiped(showAlreadyActionedImages: Boolean) { - if (binding.zoomable?.zoomableController?.isIdentity == false) + if (binding.zoomable?.zoomableController?.isIdentity == false) { return + } if (showAlreadyActionedImages) { if (position > 0) { position-- init(images!![position].uri) } else { - Toast.makeText( - this@ZoomableActivity, - getString(R.string.no_more_images_found), - Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + this@ZoomableActivity, + getString(R.string.no_more_images_found), + Toast.LENGTH_SHORT, + ).show() } } else { if (position > 0) { @@ -408,11 +437,12 @@ class ZoomableActivity : BaseActivity() { init(images!![position].uri) } } else { - Toast.makeText( - this@ZoomableActivity, - getString(R.string.no_more_images_found), - Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + this@ZoomableActivity, + getString(R.string.no_more_images_found), + Toast.LENGTH_SHORT, + ).show() } } } @@ -421,19 +451,21 @@ class ZoomableActivity : BaseActivity() { * Handles left swipe action */ private fun onLeftSwiped(showAlreadyActionedImages: Boolean) { - if (binding.zoomable?.zoomableController?.isIdentity == false) + if (binding.zoomable?.zoomableController?.isIdentity == false) { return + } if (showAlreadyActionedImages) { if (position < images!!.size - 1) { position++ init(images!![position].uri) } else { - Toast.makeText( - this@ZoomableActivity, - getString(R.string.no_more_images_found), - Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + this@ZoomableActivity, + getString(R.string.no_more_images_found), + Toast.LENGTH_SHORT, + ).show() } } else { if (position < images!!.size - 1) { @@ -442,11 +474,12 @@ class ZoomableActivity : BaseActivity() { init(images!![position].uri) } } else { - Toast.makeText( - this@ZoomableActivity, - getString(R.string.no_more_images_found), - Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + this@ZoomableActivity, + getString(R.string.no_more_images_found), + Toast.LENGTH_SHORT, + ).show() } } } @@ -459,29 +492,32 @@ class ZoomableActivity : BaseActivity() { */ private suspend fun getNextActionableImage(index: Int): Int { var nextPosition = position - for(i in index until images!!.size){ + for (i in index until images!!.size) { nextPosition = i - val imageSHA1 = CustomSelectorUtils.getImageSHA1( - images!![i].uri, - ioDispatcher, - fileUtilsWrapper, - contentResolver - ) + val imageSHA1 = + CustomSelectorUtils.getImageSHA1( + images!![i].uri, + ioDispatcher, + fileUtilsWrapper, + contentResolver, + ) var isNonActionable = notForUploadStatusDao.find(imageSHA1) if (isNonActionable <= 0) { isNonActionable = uploadedStatusDao.findByImageSHA1(imageSHA1, true) if (isNonActionable <= 0) { - val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1( - images!![i], - defaultDispatcher, - this@ZoomableActivity, - fileProcessor, - fileUtilsWrapper - ) - isNonActionable = uploadedStatusDao.findByModifiedImageSHA1( - imageModifiedSHA1, - true - ) + val imageModifiedSHA1 = + CustomSelectorUtils.generateModifiedSHA1( + images!![i], + defaultDispatcher, + this@ZoomableActivity, + fileProcessor, + fileUtilsWrapper, + ) + isNonActionable = + uploadedStatusDao.findByModifiedImageSHA1( + imageModifiedSHA1, + true, + ) if (isNonActionable <= 0) { return i } else { @@ -505,29 +541,32 @@ class ZoomableActivity : BaseActivity() { */ private suspend fun getPreviousActionableImage(index: Int): Int { var previousPosition = position - for(i in index downTo 0){ + for (i in index downTo 0) { previousPosition = i - val imageSHA1 = CustomSelectorUtils.getImageSHA1( - images!![i].uri, - ioDispatcher, - fileUtilsWrapper, - contentResolver - ) + val imageSHA1 = + CustomSelectorUtils.getImageSHA1( + images!![i].uri, + ioDispatcher, + fileUtilsWrapper, + contentResolver, + ) var isNonActionable = notForUploadStatusDao.find(imageSHA1) if (isNonActionable <= 0) { isNonActionable = uploadedStatusDao.findByImageSHA1(imageSHA1, true) if (isNonActionable <= 0) { - val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1( - images!![i], - defaultDispatcher, - this@ZoomableActivity, - fileProcessor, - fileUtilsWrapper - ) - isNonActionable = uploadedStatusDao.findByModifiedImageSHA1( - imageModifiedSHA1, - true - ) + val imageModifiedSHA1 = + CustomSelectorUtils.generateModifiedSHA1( + images!![i], + defaultDispatcher, + this@ZoomableActivity, + fileProcessor, + fileUtilsWrapper, + ) + isNonActionable = + uploadedStatusDao.findByModifiedImageSHA1( + imageModifiedSHA1, + true, + ) if (isNonActionable <= 0) { return i } else { @@ -561,9 +600,10 @@ class ZoomableActivity : BaseActivity() { /** * Get position of an image from list */ - private fun getImagePosition(list: ArrayList?, image: Image): Int { - return list!!.indexOf(image) - } + private fun getImagePosition( + list: ArrayList?, + image: Image, + ): Int = list!!.indexOf(image) /** * Two types of loading indicators have been added to the zoom activity: @@ -573,19 +613,25 @@ class ZoomableActivity : BaseActivity() { */ private val loadingListener: ControllerListener = object : BaseControllerListener() { - override fun onSubmit(id: String, callerContext: Any) { + override fun onSubmit( + id: String, + callerContext: Any, + ) { // Sometimes the spinner doesn't appear when rapidly switching between images, this fixes that binding.zoomProgressBar.visibility = View.VISIBLE } - override fun onIntermediateImageSet(id: String, imageInfo: ImageInfo?) { + override fun onIntermediateImageSet( + id: String, + imageInfo: ImageInfo?, + ) { binding.zoomProgressBar.visibility = View.GONE } override fun onFinalImageSet( id: String, imageInfo: ImageInfo?, - animatable: Animatable? + animatable: Animatable?, ) { binding.zoomProgressBar.visibility = View.GONE } @@ -593,23 +639,27 @@ class ZoomableActivity : BaseActivity() { private fun init(imageUri: Uri?) { if (imageUri != null) { - val hierarchy = GenericDraweeHierarchyBuilder.newInstance(resources) - .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER) - .setProgressBarImage(ProgressBarDrawable()) - .setProgressBarImageScaleType(ScalingUtils.ScaleType.FIT_CENTER) - .build() + val hierarchy = + GenericDraweeHierarchyBuilder + .newInstance(resources) + .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER) + .setProgressBarImage(ProgressBarDrawable()) + .setProgressBarImageScaleType(ScalingUtils.ScaleType.FIT_CENTER) + .build() with(binding.zoomable!!) { setHierarchy(hierarchy) setAllowTouchInterceptionWhileZoomed(true) setIsLongpressEnabled(false) setTapListener(DoubleTapGestureListener(this)) } - val controller: DraweeController = Fresco.newDraweeControllerBuilder() - .setUri(imageUri) - .setControllerListener(loadingListener) - .build() + val controller: DraweeController = + Fresco + .newDraweeControllerBuilder() + .setUri(imageUri) + .setControllerListener(loadingListener) + .build() binding.zoomable!!.controller = controller - + if (photoBackgroundColor != null) { binding.zoomable!!.setBackgroundColor(photoBackgroundColor!!) } @@ -630,16 +680,17 @@ class ZoomableActivity : BaseActivity() { * Inserts an image in Not For Upload table */ private suspend fun insertInNotForUpload(it: Image) { - val imageSHA1 = CustomSelectorUtils.getImageSHA1( - it.uri, - ioDispatcher, - fileUtilsWrapper, - contentResolver - ) + val imageSHA1 = + CustomSelectorUtils.getImageSHA1( + it.uri, + ioDispatcher, + fileUtilsWrapper, + contentResolver, + ) notForUploadStatusDao.insert( NotForUploadStatus( - imageSHA1 - ) + imageSHA1, + ), ) } @@ -651,7 +702,7 @@ class ZoomableActivity : BaseActivity() { val returnIntent = Intent() returnIntent.putParcelableArrayListExtra( CustomSelectorConstants.NEW_SELECTED_IMAGES, - selectedImages + selectedImages, ) returnIntent.putExtra(SHOULD_REFRESH, shouldRefresh) setResult(Activity.RESULT_OK, returnIntent) @@ -665,14 +716,14 @@ class ZoomableActivity : BaseActivity() { super.onDestroy() } - object ZoomableActivityConstants { + object ZoomableActivityConstants { /** * Key for Accessing Intent Data Named "Origin", The value indicates what fragment * ZoomableActivity was created by. It is null if ZoomableActivity was created by * the custom picker. */ - const val ORIGIN = "Origin"; - + const val ORIGIN = "Origin" + const val PHOTO_BACKGROUND_COLOR = "photo_background_color" } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/model/PageMediaListResponse.kt b/app/src/main/java/fr/free/nrw/commons/media/model/PageMediaListResponse.kt index 01306f05c4..98d51c9f37 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/model/PageMediaListResponse.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/model/PageMediaListResponse.kt @@ -3,7 +3,9 @@ package fr.free.nrw.commons.media.model data class PageMediaListResponse( val revision: String, val tid: String, - val items: List + val items: List, ) -data class PageMediaListItem(val title: String) +data class PageMediaListItem( + val title: String, +) diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/SparqlResponses.kt b/app/src/main/java/fr/free/nrw/commons/mwapi/SparqlResponses.kt index 84c86bf9f5..5f9772fac1 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/SparqlResponses.kt +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/SparqlResponses.kt @@ -1,14 +1,21 @@ package fr.free.nrw.commons.mwapi -data class SparqlResponse(val results: Result) +data class SparqlResponse( + val results: Result, +) -data class Result(val bindings: List) +data class Result( + val bindings: List, +) data class Binding( - val item: SparqInfo + val item: SparqInfo, ) { val id: String get() = item.value.substringAfterLast("/") } -data class SparqInfo(val type: String, val value: String) +data class SparqInfo( + val type: String, + val value: String, +) diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/UserClient.kt b/app/src/main/java/fr/free/nrw/commons/mwapi/UserClient.kt index b6e3b873b7..45a6a94674 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/UserClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/UserClient.kt @@ -2,35 +2,38 @@ package fr.free.nrw.commons.mwapi import fr.free.nrw.commons.utils.DateUtil import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResult -import fr.free.nrw.commons.wikidata.mwapi.UserInfo import io.reactivex.Single import java.text.ParseException import java.util.Date import javax.inject.Inject -class UserClient @Inject constructor(private val userInterface: UserInterface) { - /** - * Checks to see if a user is currently blocked from Commons - * - * @return whether or not the user is blocked from Commons - */ - fun isUserBlockedFromCommons(): Single = - userInterface.getUserBlockInfo() - .map(::processBlockExpiry) - .single(false) +class UserClient + @Inject + constructor( + private val userInterface: UserInterface, + ) { + /** + * Checks to see if a user is currently blocked from Commons + * + * @return whether or not the user is blocked from Commons + */ + fun isUserBlockedFromCommons(): Single = + userInterface + .getUserBlockInfo() + .map(::processBlockExpiry) + .single(false) - @Throws(ParseException::class) - private fun processBlockExpiry(response: MwQueryResponse): Boolean { - val blockExpiry = response.query()?.userInfo()?.blockexpiry() - return when { - blockExpiry.isNullOrEmpty() -> false - "infinite" == blockExpiry -> true - else -> { - val endDate = DateUtil.iso8601DateParse(blockExpiry) - val current = Date() - endDate.after(current) + @Throws(ParseException::class) + private fun processBlockExpiry(response: MwQueryResponse): Boolean { + val blockExpiry = response.query()?.userInfo()?.blockexpiry() + return when { + blockExpiry.isNullOrEmpty() -> false + "infinite" == blockExpiry -> true + else -> { + val endDate = DateUtil.iso8601DateParse(blockExpiry) + val current = Date() + endDate.after(current) + } } } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/UserInterface.kt b/app/src/main/java/fr/free/nrw/commons/mwapi/UserInterface.kt index cd8c7ecae0..c130db6759 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/UserInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/UserInterface.kt @@ -17,7 +17,7 @@ interface UserInterface { @GET(WikidataConstants.MW_API_PREFIX + "action=query&list=logevents&letype=upload&leprop=title|timestamp|ids&lelimit=500") fun getUserLogEvents( @Query("leuser") user: String?, - @QueryMap continuation: Map? + @QueryMap continuation: Map?, ): Observable /** diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/BottomSheetAdapter.kt b/app/src/main/java/fr/free/nrw/commons/nearby/BottomSheetAdapter.kt index ca21f25adb..8bcc21e405 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/BottomSheetAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/BottomSheetAdapter.kt @@ -20,24 +20,32 @@ import fr.free.nrw.commons.nearby.model.BottomSheetItem * @property itemList The list of BottomSheetItem objects to display. * @constructor Creates an instance of BottomSheetAdapter. */ -class BottomSheetAdapter(context: Context?, private val itemList: List) : - RecyclerView.Adapter() { +class BottomSheetAdapter( + context: Context?, + private val itemList: List, +) : RecyclerView.Adapter() { private val layoutInflater: LayoutInflater = LayoutInflater.from(context) private var itemClickListener: ItemClickListener? = null @NonNull - override fun onCreateViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder { + override fun onCreateViewHolder( + @NonNull parent: ViewGroup, + viewType: Int, + ): ViewHolder { val view: View = layoutInflater.inflate(R.layout.bottom_sheet_item_layout, parent, false) return ViewHolder(view) } - override fun onBindViewHolder(@NonNull holder: ViewHolder, position: Int) { + override fun onBindViewHolder( + @NonNull holder: ViewHolder, + position: Int, + ) { val item = itemList[position] holder.imageView.setImageDrawable( ContextCompat.getDrawable( getContext(), - item.imageResourceId - ) + item.imageResourceId, + ), ) holder.title.setText(item.title) } @@ -47,9 +55,7 @@ class BottomSheetAdapter(context: Context?, private val itemList: List - if (item.imageResourceId == R.drawable.ic_round_star_filled_24px || item.imageResourceId == R.drawable.ic_round_star_border_24px) { + if (item.imageResourceId == R.drawable.ic_round_star_filled_24px || + item.imageResourceId == R.drawable.ic_round_star_border_24px + ) { item.imageResourceId = icon this.notifyItemChanged(index) return @@ -66,8 +74,11 @@ class BottomSheetAdapter(context: Context?, private val itemList: List Unit)? = null, @@ -26,7 +27,7 @@ fun placeAdapterDelegate( onOverFlowLongPressed: () -> Boolean, onDirectionsClicked: (Place) -> Unit, onDirectionsLongPressed: () -> Boolean, - inAppCameraLocationPermissionLauncher: ActivityResultLauncher> + inAppCameraLocationPermissionLauncher: ActivityResultLauncher>, ) = adapterDelegateViewBinding({ layoutInflater, parent -> ItemPlaceBinding.inflate(layoutInflater, parent, false) }) { @@ -47,17 +48,17 @@ fun placeAdapterDelegate( nearbyButtonLayout.cameraButton.setOnLongClickListener { onCameraLongPressed() } nearbyButtonLayout.galleryButton.setOnClickListener { onGalleryClicked(item) } - nearbyButtonLayout.galleryButton.setOnLongClickListener{onGalleryLongPressed()} + nearbyButtonLayout.galleryButton.setOnLongClickListener { onGalleryLongPressed() } bookmarkButtonImage.setOnClickListener { val isBookmarked = bookmarkLocationDao.updateBookmarkLocation(item) bookmarkButtonImage.setImageResource( - if (isBookmarked) R.drawable.ic_round_star_filled_24px else R.drawable.ic_round_star_border_24px + if (isBookmarked) R.drawable.ic_round_star_filled_24px else R.drawable.ic_round_star_border_24px, ) onBookmarkClicked(item, isBookmarked) } - bookmarkButtonImage.setOnLongClickListener{onBookmarkLongPressed()} + bookmarkButtonImage.setOnLongClickListener { onBookmarkLongPressed() } nearbyButtonLayout.iconOverflow.setOnClickListener { onOverflowIconClicked(item, it) } - nearbyButtonLayout.iconOverflow.setOnLongClickListener{onOverFlowLongPressed()} + nearbyButtonLayout.iconOverflow.setOnLongClickListener { onOverFlowLongPressed() } nearbyButtonLayout.directionsButton.setOnClickListener { onDirectionsClicked(item) } bind { tvName.text = item.name @@ -68,23 +69,28 @@ fun placeAdapterDelegate( } else { // Remove the label and display only texts inside pharentheses (description) since too long tvDesc.text = - descriptionText.substringAfter(tvName.text.toString() + " (") - .substringBeforeLast(")"); + descriptionText + .substringAfter(tvName.text.toString() + " (") + .substringBeforeLast(")") } distance.text = item.distance icon.setImageResource(item.label.icon) nearbyButtonLayout.iconOverflow.visibility = - if (item.hasCommonsLink() || item.hasWikidataLink()) VISIBLE - else GONE + if (item.hasCommonsLink() || item.hasWikidataLink()) { + VISIBLE + } else { + GONE + } bookmarkButtonImage.setImageResource( - if (bookmarkLocationDao.findBookmarkLocation(item)) + if (bookmarkLocationDao.findBookmarkLocation(item)) { R.drawable.ic_round_star_filled_24px - else + } else { R.drawable.ic_round_star_border_24px + }, ) } - nearbyButtonLayout.directionsButton.setOnLongClickListener{onDirectionsLongPressed()} + nearbyButtonLayout.directionsButton.setOnLongClickListener { onDirectionsLongPressed() } } } @@ -101,7 +107,7 @@ private fun AdapterDelegateViewBindingViewHolder.showOr (recyclerView.layoutManager as LinearLayoutManager?) ?.scrollToPositionWithOffset( lastPosition, - nearbyButtonLayout.buttonLayout.height + nearbyButtonLayout.buttonLayout.height, ) } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt b/app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt index 060547d408..d238296d13 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt @@ -49,13 +49,15 @@ class WikidataFeedback : BaseActivity() { binding.radioButton1.setText( getString( R.string.does_not_exist_anymore_no_picture_can_ever_be_taken_of_it, - place - )) + place, + ), + ) binding.radioButton2.setText( getString( R.string.is_at_a_different_place_please_specify_the_correct_place_below_if_possible_tell_us_the_correct_latitude_longitude, - place - )) + place, + ), + ) binding.radioButton3.setText(getString(R.string.other_problem_or_information_please_explain_below)) setSupportActionBar(binding.toolbarBinding.toolbar) supportActionBar!!.setDisplayHomeAsUpEnabled(true) @@ -64,21 +66,29 @@ class WikidataFeedback : BaseActivity() { var desc = findViewById(binding.radioGroup.checkedRadioButtonId).text var det = binding.detailsEditText.text.toString() if (binding.radioGroup.checkedRadioButtonId == R.id.radioButton3 && binding.detailsEditText.text.isNullOrEmpty()) { - Toast.makeText( - this, - getString(R.string.please_enter_some_comments), Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + this, + getString(R.string.please_enter_some_comments), + Toast.LENGTH_SHORT, + ).show() } else { binding.radioGroup.clearCheck() binding.detailsEditText.setText("") - Single.defer(Callable> { - pageEditHelper.makePageEdit( - this, pageTitle, preText, - desc.toString(), - det, lat, lng - ) - } as Callable>) - .subscribeOn(Schedulers.io()) + Single + .defer( + Callable> { + pageEditHelper.makePageEdit( + this, + pageTitle, + preText, + desc.toString(), + det, + lat, + lng, + ) + } as Callable>, + ).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ aBoolean: Boolean? -> }, { throwable: Throwable? -> @@ -92,5 +102,4 @@ class WikidataFeedback : BaseActivity() { onBackPressed() return true } - -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/AdvanceQueryFragment.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/AdvanceQueryFragment.kt index ed3b741d3d..d1f8e94b6a 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/AdvanceQueryFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/AdvanceQueryFragment.kt @@ -12,9 +12,8 @@ import androidx.fragment.app.Fragment import fr.free.nrw.commons.databinding.FragmentAdvanceQueryBinding class AdvanceQueryFragment : Fragment() { - private var _binding: FragmentAdvanceQueryBinding? = null - private val binding get() = _binding + val binding get() = _binding lateinit var callback: Callback @@ -28,7 +27,7 @@ class AdvanceQueryFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View? { _binding = FragmentAdvanceQueryBinding.inflate(inflater, container, false) etQuery = binding?.etQuery @@ -38,7 +37,10 @@ class AdvanceQueryFragment : Fragment() { return binding?.root } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) setUi() @@ -79,7 +81,9 @@ class AdvanceQueryFragment : Fragment() { interface Callback { fun reset() + fun apply(query: String) + fun close() } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt index 289f25976f..f3eecf1160 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt @@ -21,109 +21,126 @@ import timber.log.Timber import javax.inject.Inject import javax.inject.Named +class CommonPlaceClickActions + @Inject + constructor( + @Named("default_preferences") private val applicationKvStore: JsonKvStore, + private val activity: Activity, + private val contributionController: ContributionController, + ) { + fun onCameraClicked(): (Place, ActivityResultLauncher>) -> Unit = + { place, launcher -> + if (applicationKvStore.getBoolean("login_skipped", false)) { + showLoginDialog() + } else { + Timber.d("Camera button tapped. Image title: ${place.getName()}Image desc: ${place.longDescription}") + storeSharedPrefs(place) + contributionController.initiateCameraPick(activity, launcher) + } + } -class CommonPlaceClickActions @Inject constructor( - @Named("default_preferences") private val applicationKvStore: JsonKvStore, - private val activity: Activity, - private val contributionController: ContributionController -) { + /** + * Shows the Label for the Icon when it's long pressed + **/ + fun onCameraLongPressed(): () -> Boolean = + { + Toast.makeText(activity, R.string.menu_from_camera, Toast.LENGTH_SHORT).show() + true + } - fun onCameraClicked(): (Place, ActivityResultLauncher>) -> Unit = { place, launcher -> - if (applicationKvStore.getBoolean("login_skipped", false)) { - showLoginDialog() - } else { - Timber.d("Camera button tapped. Image title: ${place.getName()}Image desc: ${place.longDescription}") - storeSharedPrefs(place) - contributionController.initiateCameraPick(activity, launcher) - } - } + fun onGalleryLongPressed(): () -> Boolean = + { + Toast.makeText(activity, R.string.menu_from_gallery, Toast.LENGTH_SHORT).show() + true + } - /** - * Shows the Label for the Icon when it's long pressed - **/ - fun onCameraLongPressed(): () -> Boolean = { - Toast.makeText(activity, R.string.menu_from_camera, Toast.LENGTH_SHORT).show() - true - } - fun onGalleryLongPressed(): () -> Boolean = { - Toast.makeText(activity, R.string.menu_from_gallery, Toast.LENGTH_SHORT).show() - true - } - fun onBookmarkLongPressed(): () -> Boolean = { - Toast.makeText(activity, R.string.menu_bookmark, Toast.LENGTH_SHORT).show() - true - } - fun onDirectionsLongPressed(): () -> Boolean = { - Toast.makeText(activity, R.string.nearby_directions, Toast.LENGTH_SHORT).show() - true - } - fun onOverflowLongPressed(): () -> Boolean = { - Toast.makeText(activity, R.string.more, Toast.LENGTH_SHORT).show() - true - } + fun onBookmarkLongPressed(): () -> Boolean = + { + Toast.makeText(activity, R.string.menu_bookmark, Toast.LENGTH_SHORT).show() + true + } - fun onGalleryClicked(): (Place) -> Unit = { - if (applicationKvStore.getBoolean("login_skipped", false)) { - showLoginDialog() - } else { - Timber.d("Gallery button tapped. Image title: ${it.getName()}Image desc: ${it.getLongDescription()}") - storeSharedPrefs(it) - contributionController.initiateGalleryPick(activity, false) - } - } + fun onDirectionsLongPressed(): () -> Boolean = + { + Toast.makeText(activity, R.string.nearby_directions, Toast.LENGTH_SHORT).show() + true + } + + fun onOverflowLongPressed(): () -> Boolean = + { + Toast.makeText(activity, R.string.more, Toast.LENGTH_SHORT).show() + true + } - fun onOverflowClicked(): (Place, View) -> Unit = { place, view -> - PopupMenu(view.context, view).apply { - inflate(R.menu.nearby_info_dialog_options) - enableBy(R.id.nearby_info_menu_commons_article, place.hasCommonsLink()) - enableBy(R.id.nearby_info_menu_wikidata_article, place.hasWikidataLink()) - enableBy(R.id.nearby_info_menu_wikipedia_article, place.hasWikipediaLink()) - setOnMenuItemClickListener { item: MenuItem -> - when (item.itemId) { - R.id.nearby_info_menu_commons_article -> openWebView(place.siteLinks.commonsLink) - R.id.nearby_info_menu_wikidata_article -> openWebView(place.siteLinks.wikidataLink) - R.id.nearby_info_menu_wikipedia_article -> openWebView(place.siteLinks.wikipediaLink) - else -> false + fun onGalleryClicked(): (Place) -> Unit = + { + if (applicationKvStore.getBoolean("login_skipped", false)) { + showLoginDialog() + } else { + Timber.d("Gallery button tapped. Image title: ${it.getName()}Image desc: ${it.getLongDescription()}") + storeSharedPrefs(it) + contributionController.initiateGalleryPick(activity, false) } } - }.show() - } - fun onDirectionsClicked(): (Place) -> Unit = { - Utils.handleGeoCoordinates(activity, it.getLocation()) - } + fun onOverflowClicked(): (Place, View) -> Unit = + { place, view -> + PopupMenu(view.context, view) + .apply { + inflate(R.menu.nearby_info_dialog_options) + enableBy(R.id.nearby_info_menu_commons_article, place.hasCommonsLink()) + enableBy(R.id.nearby_info_menu_wikidata_article, place.hasWikidataLink()) + enableBy(R.id.nearby_info_menu_wikipedia_article, place.hasWikipediaLink()) + setOnMenuItemClickListener { item: MenuItem -> + when (item.itemId) { + R.id.nearby_info_menu_commons_article -> openWebView(place.siteLinks.commonsLink) + R.id.nearby_info_menu_wikidata_article -> openWebView(place.siteLinks.wikidataLink) + R.id.nearby_info_menu_wikipedia_article -> openWebView(place.siteLinks.wikipediaLink) + else -> false + } + } + }.show() + } - private fun storeSharedPrefs(selectedPlace: Place) { - Timber.d("Store place object %s", selectedPlace.toString()) - applicationKvStore.putJson(WikidataConstants.PLACE_OBJECT, selectedPlace) - } + fun onDirectionsClicked(): (Place) -> Unit = + { + Utils.handleGeoCoordinates(activity, it.getLocation()) + } - private fun openWebView(link: Uri): Boolean { - Utils.handleWebUrl(activity, link) - return true - } + private fun storeSharedPrefs(selectedPlace: Place) { + Timber.d("Store place object %s", selectedPlace.toString()) + applicationKvStore.putJson(WikidataConstants.PLACE_OBJECT, selectedPlace) + } - private fun PopupMenu.enableBy(menuId: Int, hasLink: Boolean) { - menu.findItem(menuId).isEnabled = hasLink - } + private fun openWebView(link: Uri): Boolean { + Utils.handleWebUrl(activity, link) + return true + } - private fun showLoginDialog() { - AlertDialog.Builder(activity) - .setMessage(R.string.login_alert_message) - .setPositiveButton(R.string.login) { dialog, which -> - setPositiveButton() - } - .show() - } + private fun PopupMenu.enableBy( + menuId: Int, + hasLink: Boolean, + ) { + menu.findItem(menuId).isEnabled = hasLink + } + + private fun showLoginDialog() { + AlertDialog + .Builder(activity) + .setMessage(R.string.login_alert_message) + .setPositiveButton(R.string.login) { dialog, which -> + setPositiveButton() + }.show() + } - private fun setPositiveButton() { - ActivityUtils.startActivityWithFlags( - activity, - LoginActivity::class.java, - Intent.FLAG_ACTIVITY_CLEAR_TOP, - Intent.FLAG_ACTIVITY_SINGLE_TOP - ) - applicationKvStore.putBoolean("login_skipped", false) - activity.finish() + private fun setPositiveButton() { + ActivityUtils.startActivityWithFlags( + activity, + LoginActivity::class.java, + Intent.FLAG_ACTIVITY_CLEAR_TOP, + Intent.FLAG_ACTIVITY_SINGLE_TOP, + ) + applicationKvStore.putBoolean("login_skipped", false) + activity.finish() + } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt index e3d66e34a5..689aa7efca 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/PlaceAdapter.kt @@ -11,9 +11,8 @@ class PlaceAdapter( onPlaceClicked: ((Place) -> Unit)? = null, onBookmarkClicked: (Place, Boolean) -> Unit, commonPlaceClickActions: CommonPlaceClickActions, - inAppCameraLocationPermissionLauncher: ActivityResultLauncher> -) : - BaseDelegateAdapter( + inAppCameraLocationPermissionLauncher: ActivityResultLauncher>, +) : BaseDelegateAdapter( placeAdapterDelegate( bookmarkLocationsDao, onPlaceClicked, @@ -27,7 +26,7 @@ class PlaceAdapter( commonPlaceClickActions.onOverflowLongPressed(), commonPlaceClickActions.onDirectionsClicked(), commonPlaceClickActions.onDirectionsLongPressed(), - inAppCameraLocationPermissionLauncher + inAppCameraLocationPermissionLauncher, ), - areItemsTheSame = {oldItem, newItem -> oldItem.wikiDataEntityId == newItem.wikiDataEntityId } + areItemsTheSame = { oldItem, newItem -> oldItem.wikiDataEntityId == newItem.wikiDataEntityId }, ) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/BottomSheetItem.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/BottomSheetItem.kt index bf30be6ec3..bf09df7cad 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/model/BottomSheetItem.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/BottomSheetItem.kt @@ -1,3 +1,6 @@ package fr.free.nrw.commons.nearby.model -class BottomSheetItem(var imageResourceId: Int, val title: String) +class BottomSheetItem( + var imageResourceId: Int, + val title: String, +) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResponse.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResponse.kt index 20b38c75fb..629b931623 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResponse.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResponse.kt @@ -1,3 +1,5 @@ package fr.free.nrw.commons.nearby.model -class NearbyResponse(val results: NearbyResults) \ No newline at end of file +class NearbyResponse( + val results: NearbyResults, +) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt index 4da8740cc0..f28cc833e6 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResultItem.kt @@ -2,78 +2,50 @@ package fr.free.nrw.commons.nearby.model import com.google.gson.annotations.SerializedName -class NearbyResultItem(private val item: ResultTuple?, - private val wikipediaArticle: ResultTuple?, - private val commonsArticle: ResultTuple?, - private val location: ResultTuple?, - private val label: ResultTuple?, - @field:SerializedName("streetAddress") private val address: ResultTuple?, - private val icon: ResultTuple?, @field:SerializedName("class") private val className: ResultTuple?, - @field:SerializedName("classLabel") private val classLabel: ResultTuple?, - @field:SerializedName("commonsCategory") private val commonsCategory: ResultTuple?, - @field:SerializedName("pic") private val pic: ResultTuple?, - @field:SerializedName("destroyed") private val destroyed: ResultTuple?, - @field:SerializedName("description") private val description: ResultTuple?, - @field:SerializedName("endTime") private val endTime: ResultTuple?, - @field:SerializedName("monument") private val monument: ResultTuple?) { - - fun getItem(): ResultTuple { - return item ?: ResultTuple() - } - - fun getWikipediaArticle(): ResultTuple { - return wikipediaArticle ?: ResultTuple() - } - - fun getCommonsArticle(): ResultTuple { - return commonsArticle ?: ResultTuple() - } - - fun getLocation(): ResultTuple { - return location ?: ResultTuple() - } - - fun getLabel(): ResultTuple { - return label ?: ResultTuple() - } - - fun getIcon(): ResultTuple { - return icon ?: ResultTuple() - } - - fun getClassName(): ResultTuple { - return className ?: ResultTuple() - } - - fun getClassLabel(): ResultTuple { - return classLabel ?: ResultTuple() - } - - fun getCommonsCategory(): ResultTuple { - return commonsCategory ?: ResultTuple() - } - - fun getPic(): ResultTuple { - return pic ?: ResultTuple() - } - - fun getDestroyed(): ResultTuple { - return destroyed ?: ResultTuple() - } - - fun getDescription(): ResultTuple { - return description ?: ResultTuple() - } - - fun getEndTime(): ResultTuple { - return endTime ?: ResultTuple() - } - - fun getAddress(): String { - return address?.value?:"" - } - - fun getMonument():ResultTuple?{ - return monument - } -} \ No newline at end of file +class NearbyResultItem( + private val item: ResultTuple?, + private val wikipediaArticle: ResultTuple?, + private val commonsArticle: ResultTuple?, + private val location: ResultTuple?, + private val label: ResultTuple?, + @field:SerializedName("streetAddress") private val address: ResultTuple?, + private val icon: ResultTuple?, + @field:SerializedName("class") private val className: ResultTuple?, + @field:SerializedName("classLabel") private val classLabel: ResultTuple?, + @field:SerializedName("commonsCategory") private val commonsCategory: ResultTuple?, + @field:SerializedName("pic") private val pic: ResultTuple?, + @field:SerializedName("destroyed") private val destroyed: ResultTuple?, + @field:SerializedName("description") private val description: ResultTuple?, + @field:SerializedName("endTime") private val endTime: ResultTuple?, + @field:SerializedName("monument") private val monument: ResultTuple?, +) { + fun getItem(): ResultTuple = item ?: ResultTuple() + + fun getWikipediaArticle(): ResultTuple = wikipediaArticle ?: ResultTuple() + + fun getCommonsArticle(): ResultTuple = commonsArticle ?: ResultTuple() + + fun getLocation(): ResultTuple = location ?: ResultTuple() + + fun getLabel(): ResultTuple = label ?: ResultTuple() + + fun getIcon(): ResultTuple = icon ?: ResultTuple() + + fun getClassName(): ResultTuple = className ?: ResultTuple() + + fun getClassLabel(): ResultTuple = classLabel ?: ResultTuple() + + fun getCommonsCategory(): ResultTuple = commonsCategory ?: ResultTuple() + + fun getPic(): ResultTuple = pic ?: ResultTuple() + + fun getDestroyed(): ResultTuple = destroyed ?: ResultTuple() + + fun getDescription(): ResultTuple = description ?: ResultTuple() + + fun getEndTime(): ResultTuple = endTime ?: ResultTuple() + + fun getAddress(): String = address?.value ?: "" + + fun getMonument(): ResultTuple? = monument +} diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResults.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResults.kt index 4a8832831a..53a07ffca6 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResults.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/NearbyResults.kt @@ -1,3 +1,5 @@ package fr.free.nrw.commons.nearby.model -class NearbyResults(val bindings: List) \ No newline at end of file +class NearbyResults( + val bindings: List, +) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/PlaceBindings.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/PlaceBindings.kt index ccbdd156c4..857af27531 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/model/PlaceBindings.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/PlaceBindings.kt @@ -6,41 +6,40 @@ data class PlaceBindings( @SerializedName("item") val item: Item, @SerializedName("label") val label: Label, @SerializedName("location") val location: Location, - @SerializedName("class") val clas: Clas + @SerializedName("class") val clas: Clas, ) data class ItemsClass( @SerializedName("head") val head: Head, - @SerializedName("results") val results: Results + @SerializedName("results") val results: Results, ) data class Label( @SerializedName("xml:lang") val xml: String, @SerializedName("type") val type: String, - @SerializedName("value") val value: String + @SerializedName("value") val value: String, ) data class Location( @SerializedName("datatype") val datatype: String, @SerializedName("type") val type: String, - @SerializedName("value") val value: String + @SerializedName("value") val value: String, ) data class Results( - @SerializedName("bindings") val bindings: List + @SerializedName("bindings") val bindings: List, ) data class Item( @SerializedName("type") val type: String, - @SerializedName("value") val value: String + @SerializedName("value") val value: String, ) data class Head( - @SerializedName("vars") val vars: List + @SerializedName("vars") val vars: List, ) - data class Clas( @SerializedName("type") val type: String, - @SerializedName("value") val value: String -) \ No newline at end of file + @SerializedName("value") val value: String, +) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/ResultTuple.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/ResultTuple.kt index 161b0c07fb..bd411c9388 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/model/ResultTuple.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/ResultTuple.kt @@ -19,5 +19,4 @@ class ResultTuple { type = "" value = "" } - -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificatinAdapter.kt b/app/src/main/java/fr/free/nrw/commons/notification/NotificatinAdapter.kt index 6ad6876a32..41d7d48830 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificatinAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificatinAdapter.kt @@ -3,8 +3,9 @@ package fr.free.nrw.commons.notification import fr.free.nrw.commons.notification.models.Notification import fr.free.nrw.commons.upload.categories.BaseDelegateAdapter -internal class NotificatinAdapter(onNotificationClicked: (Notification) -> Unit) : - BaseDelegateAdapter( +internal class NotificatinAdapter( + onNotificationClicked: (Notification) -> Unit, +) : BaseDelegateAdapter( notificationDelegate(onNotificationClicked), - areItemsTheSame = { oldItem, newItem -> oldItem.notificationId == newItem.notificationId } + areItemsTheSame = { oldItem, newItem -> oldItem.notificationId == newItem.notificationId }, ) diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationAdapterDelegates.kt b/app/src/main/java/fr/free/nrw/commons/notification/NotificationAdapterDelegates.kt index 7dedf1d55d..133ad3fa26 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationAdapterDelegates.kt +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationAdapterDelegates.kt @@ -1,11 +1,10 @@ package fr.free.nrw.commons.notification import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding -import fr.free.nrw.commons.notification.models.Notification import fr.free.nrw.commons.databinding.ItemNotificationBinding +import fr.free.nrw.commons.notification.models.Notification import fr.free.nrw.commons.utils.StringUtil - fun notificationDelegate(onNotificationClicked: (Notification) -> Unit) = adapterDelegateViewBinding({ layoutInflater, parent -> ItemNotificationBinding.inflate(layoutInflater, parent, false) @@ -15,11 +14,12 @@ fun notificationDelegate(onNotificationClicked: (Notification) -> Unit) = binding.title.text = item.processedNotificationText binding.time.text = item.date } - } private val Notification.processedNotificationText: CharSequence - get() = notificationText.trim() - .replace("(^\\s*)|(\\s*$)".toRegex(), "") - .let { StringUtil.fromHtml(it).toString() } - .let { if (it.length > 280) "${it.substring(0, 279)}..." else it } + " " + get() = + notificationText + .trim() + .replace("(^\\s*)|(\\s*$)".toRegex(), "") + .let { StringUtil.fromHtml(it).toString() } + .let { if (it.length > 280) "${it.substring(0, 279)}..." else it } + " " diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt b/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt index 6e29575ea8..aa998ffb56 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationClient.kt @@ -1,59 +1,63 @@ package fr.free.nrw.commons.notification +import fr.free.nrw.commons.auth.csrf.CsrfTokenClient +import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException import fr.free.nrw.commons.di.NetworkingModule import fr.free.nrw.commons.notification.models.Notification import fr.free.nrw.commons.notification.models.NotificationType +import fr.free.nrw.commons.utils.DateUtil +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse import io.reactivex.Observable import io.reactivex.Single -import fr.free.nrw.commons.auth.csrf.CsrfTokenClient -import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse -import fr.free.nrw.commons.utils.DateUtil import javax.inject.Inject import javax.inject.Named import javax.inject.Singleton import fr.free.nrw.commons.wikidata.model.notifications.Notification as WikimediaNotification @Singleton -class NotificationClient @Inject constructor( - @param:Named(NetworkingModule.NAMED_COMMONS_CSRF) private val csrfTokenClient: CsrfTokenClient, - private val service: NotificationInterface -) { - fun getNotifications(archived: Boolean): Single> = - service.getAllNotifications( - wikiList = "wikidatawiki|commonswiki|enwiki", - filter = if (archived) "read" else "!read", - continueStr = null - ).map { - it.query()?.notifications()?.list() ?: emptyList() - }.flatMap { - Observable.fromIterable(it) - }.map { - it.toCommonsNotification() - }.toList() +class NotificationClient + @Inject + constructor( + @param:Named(NetworkingModule.NAMED_COMMONS_CSRF) private val csrfTokenClient: CsrfTokenClient, + private val service: NotificationInterface, + ) { + fun getNotifications(archived: Boolean): Single> = + service + .getAllNotifications( + wikiList = "wikidatawiki|commonswiki|enwiki", + filter = if (archived) "read" else "!read", + continueStr = null, + ).map { + it.query()?.notifications()?.list() ?: emptyList() + }.flatMap { + Observable.fromIterable(it) + }.map { + it.toCommonsNotification() + }.toList() - fun markNotificationAsRead(notificationId: String?): Observable { - return try { - service.markRead( - token = csrfTokenClient.getTokenBlocking(), - readList = notificationId, - unreadList = "" - ).map(MwQueryResponse::success) - } catch (throwable: Throwable) { - if (throwable is InvalidLoginTokenException) { - Observable.error(throwable) - } else { - Observable.just(false) + fun markNotificationAsRead(notificationId: String?): Observable = + try { + service + .markRead( + token = csrfTokenClient.getTokenBlocking(), + readList = notificationId, + unreadList = "", + ).map(MwQueryResponse::success) + } catch (throwable: Throwable) { + if (throwable is InvalidLoginTokenException) { + Observable.error(throwable) + } else { + Observable.just(false) + } } - } - } - private fun WikimediaNotification.toCommonsNotification() = Notification( - notificationType = NotificationType.UNKNOWN, - notificationText = contents?.compactHeader ?: "", - date = DateUtil.getMonthOnlyDateString(timestamp), - link = contents?.links?.primary?.url ?: "", - iconUrl = "", - notificationId = id().toString() - ) -} + private fun WikimediaNotification.toCommonsNotification() = + Notification( + notificationType = NotificationType.UNKNOWN, + notificationText = contents?.compactHeader ?: "", + date = DateUtil.getMonthOnlyDateString(timestamp), + link = contents?.links?.primary?.url ?: "", + iconUrl = "", + notificationId = id().toString(), + ) + } diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationInterface.kt b/app/src/main/java/fr/free/nrw/commons/notification/NotificationInterface.kt index 0b9fab865d..2d2a5c0ab4 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationInterface.kt @@ -1,8 +1,8 @@ package fr.free.nrw.commons.notification import fr.free.nrw.commons.wikidata.WikidataConstants.MW_API_PREFIX -import io.reactivex.Observable import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse +import io.reactivex.Observable import retrofit2.http.Field import retrofit2.http.FormUrlEncoded import retrofit2.http.GET @@ -11,13 +11,12 @@ import retrofit2.http.POST import retrofit2.http.Query interface NotificationInterface { - @Headers("Cache-Control: no-cache") @GET(MW_API_PREFIX + "action=query&meta=notifications¬format=model¬limit=max") fun getAllNotifications( @Query("notwikis") wikiList: String?, @Query("notfilter") filter: String?, - @Query("notcontinue") continueStr: String? + @Query("notcontinue") continueStr: String?, ): Observable @FormUrlEncoded @@ -26,6 +25,6 @@ interface NotificationInterface { fun markRead( @Field("token") token: String, @Field("list") readList: String?, - @Field("unreadlist") unreadList: String? + @Field("unreadlist") unreadList: String?, ): Observable -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/notification/models/Notification.kt b/app/src/main/java/fr/free/nrw/commons/notification/models/Notification.kt index eddc22e5b5..b3bdc9d1eb 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/models/Notification.kt +++ b/app/src/main/java/fr/free/nrw/commons/notification/models/Notification.kt @@ -9,5 +9,5 @@ data class Notification( var date: String, var link: String, var iconUrl: String, - var notificationId: String -) \ No newline at end of file + var notificationId: String, +) diff --git a/app/src/main/java/fr/free/nrw/commons/profile/achievements/Achievements.kt b/app/src/main/java/fr/free/nrw/commons/profile/achievements/Achievements.kt index 5c6cbcf14b..861040fcff 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/achievements/Achievements.kt +++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/Achievements.kt @@ -5,54 +5,44 @@ package fr.free.nrw.commons.profile.achievements */ class Achievements { /** - * getter function to get count of unique images used by wiki - * @return - */ - /** - * setter function to set count of uniques images used by wiki - * @param uniqueUsedImages + * The count of unique images used by the wiki. + * @return The count of unique images used. + * @param uniqueUsedImages The count to set for unique images used. */ var uniqueUsedImages = 0 private var articlesUsingImages = 0 + /** - * getter function to get count of thanks received - * @return - */ - /** - * setter function to set count of thanks received - * @param thanksReceived + * The count of thanks received. + * @return The count of thanks received. + * @param thanksReceived The count to set for thanks received. */ var thanksReceived = 0 + /** - * getter function to get count of featured images - * @return - */ - /** - * setter function to set count of featured images - * @param featuredImages + * The count of featured images. + * @return The count of featured images. + * @param featuredImages The count to set for featured images. */ var featuredImages = 0 + /** - * getter function to get count of featured images - * @return - */ - /** - * setter function to set count of featured images - * @param featuredImages + * The count of quality images. + * @return The count of quality images. + * @param qualityImages The count to set for quality images. */ var qualityImages = 0 + /** - * getter function to get count of images uploaded - * @return - */ - /** - * setter function to count of images uploaded - * @param imagesUploaded + * The count of images uploaded. + * @return The count of images uploaded. + * @param imagesUploaded The count to set for images uploaded. */ var imagesUploaded = 0 private var revertCount = 0 constructor() {} + /** * constructor for achievements class to set its data members * @param uniqueUsedImages @@ -62,13 +52,15 @@ class Achievements { * @param imagesUploaded * @param revertCount */ - constructor(uniqueUsedImages: Int, - articlesUsingImages: Int, - thanksReceived: Int, - featuredImages: Int, - qualityImages: Int, - imagesUploaded: Int, - revertCount: Int) { + constructor( + uniqueUsedImages: Int, + articlesUsingImages: Int, + thanksReceived: Int, + featuredImages: Int, + qualityImages: Int, + imagesUploaded: Int, + revertCount: Int, + ) { this.uniqueUsedImages = uniqueUsedImages this.articlesUsingImages = articlesUsingImages this.thanksReceived = thanksReceived @@ -83,11 +75,12 @@ class Achievements { * @return */ val notRevertPercentage: Int - get() = try { - (imagesUploaded - revertCount) * 100 / imagesUploaded - } catch (divideByZero: ArithmeticException) { - 100 - } + get() = + try { + (imagesUploaded - revertCount) * 100 / imagesUploaded + } catch (divideByZero: ArithmeticException) { + 100 + } companion object { /** @@ -97,15 +90,15 @@ class Achievements { * @return */ @JvmStatic - fun from(response: FeedbackResponse): Achievements { - return Achievements( + fun from(response: FeedbackResponse): Achievements = + Achievements( response.uniqueUsedImages, response.articlesUsingImages, response.thanksReceived, response.featuredImages.featuredPicturesOnWikimediaCommons, - response.featuredImages.qualityImages, 0, - response.deletedUploads + response.featuredImages.qualityImages, + 0, + response.deletedUploads, ) - } } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeaturedImages.kt b/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeaturedImages.kt index 0a54a778fb..4784103fdb 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeaturedImages.kt +++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeaturedImages.kt @@ -8,5 +8,5 @@ import com.google.gson.annotations.SerializedName */ class FeaturedImages( @field:SerializedName("Quality_images") val qualityImages: Int, - @field:SerializedName("Featured_pictures_on_Wikimedia_Commons") val featuredPicturesOnWikimediaCommons: Int -) \ No newline at end of file + @field:SerializedName("Featured_pictures_on_Wikimedia_Commons") val featuredPicturesOnWikimediaCommons: Int, +) diff --git a/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeedbackResponse.kt b/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeedbackResponse.kt index f86ca3e9b1..5510db7c51 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeedbackResponse.kt +++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/FeedbackResponse.kt @@ -3,9 +3,11 @@ package fr.free.nrw.commons.profile.achievements /** * Represent the Feedback Response of the user */ -data class FeedbackResponse(val uniqueUsedImages: Int, - val articlesUsingImages: Int, - val deletedUploads: Int, - val featuredImages: FeaturedImages, - val thanksReceived: Int, - val user: String) \ No newline at end of file +data class FeedbackResponse( + val uniqueUsedImages: Int, + val articlesUsingImages: Int, + val deletedUploads: Int, + val featuredImages: FeaturedImages, + val thanksReceived: Int, + val user: String, +) diff --git a/app/src/main/java/fr/free/nrw/commons/profile/achievements/LevelController.kt b/app/src/main/java/fr/free/nrw/commons/profile/achievements/LevelController.kt index 414bf271d7..6f852854b4 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/achievements/LevelController.kt +++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/LevelController.kt @@ -8,21 +8,55 @@ import fr.free.nrw.commons.R class LevelController { var level: LevelInfo? = null - enum class LevelInfo(val levelNumber: Int, - val levelStyle: Int, - val maxUniqueImages: Int, - val maxUploadCount: Int, - val minNonRevertPercentage: Int) { - LEVEL_1(1, R.style.LevelOne, 5, 20, 85), LEVEL_2(2, R.style.LevelTwo, 10, 30, 86), LEVEL_3(3, R.style.LevelThree, 15, 40, 87), LEVEL_4(4, R.style.LevelFour, 20, 50, 88), LEVEL_5(5, R.style.LevelFive, 25, 60, 89), LEVEL_6(6, R.style.LevelOne, 30, 70, 90), LEVEL_7(7, R.style.LevelTwo, 40, 80, 90), LEVEL_8(8, R.style.LevelThree, 45, 90, 90), LEVEL_9(9, R.style.LevelFour, 50, 100, 90), LEVEL_10(10, R.style.LevelFive, 55, 110, 90), LEVEL_11(11, R.style.LevelOne, 60, 120, 90), LEVEL_12(12, R.style.LevelTwo, 65, 130, 90), LEVEL_13(13, R.style.LevelThree, 70, 140, 90), LEVEL_14(14, R.style.LevelFour, 75, 150, 90), LEVEL_15(15, R.style.LevelFive, 80, 160, 90), LEVEL_16(16, R.style.LevelOne, 160, 320, 91), LEVEL_17(17, R.style.LevelTwo, 320, 640, 92), LEVEL_18(18, R.style.LevelThree, 640, 1280, 93), LEVEL_19(19, R.style.LevelFour, 1280, 2560, 94), LEVEL_20(20, R.style.LevelFive, 2560, 5120, 95), LEVEL_21(21, R.style.LevelOne, 5120, 10240, 96), LEVEL_22(22, R.style.LevelTwo, 10240, 20480, 97), LEVEL_23(23, R.style.LevelThree, 20480, 40960, 98), LEVEL_24(24, R.style.LevelFour, 40960, 81920, 98), LEVEL_25(25, R.style.LevelFive, 81920, 163840, 98), LEVEL_26(26, R.style.LevelOne, 163840, 327680, 98), LEVEL_27(27, R.style.LevelTwo, 327680, 655360, 98); + enum class LevelInfo( + val levelNumber: Int, + val levelStyle: Int, + val maxUniqueImages: Int, + val maxUploadCount: Int, + val minNonRevertPercentage: Int, + ) { + LEVEL_1(1, R.style.LevelOne, 5, 20, 85), + LEVEL_2(2, R.style.LevelTwo, 10, 30, 86), + LEVEL_3(3, R.style.LevelThree, 15, 40, 87), + LEVEL_4(4, R.style.LevelFour, 20, 50, 88), + LEVEL_5(5, R.style.LevelFive, 25, 60, 89), + LEVEL_6(6, R.style.LevelOne, 30, 70, 90), + LEVEL_7(7, R.style.LevelTwo, 40, 80, 90), + LEVEL_8(8, R.style.LevelThree, 45, 90, 90), + LEVEL_9(9, R.style.LevelFour, 50, 100, 90), + LEVEL_10(10, R.style.LevelFive, 55, 110, 90), + LEVEL_11(11, R.style.LevelOne, 60, 120, 90), + LEVEL_12(12, R.style.LevelTwo, 65, 130, 90), + LEVEL_13(13, R.style.LevelThree, 70, 140, 90), + LEVEL_14(14, R.style.LevelFour, 75, 150, 90), + LEVEL_15(15, R.style.LevelFive, 80, 160, 90), + LEVEL_16(16, R.style.LevelOne, 160, 320, 91), + LEVEL_17(17, R.style.LevelTwo, 320, 640, 92), + LEVEL_18(18, R.style.LevelThree, 640, 1280, 93), + LEVEL_19(19, R.style.LevelFour, 1280, 2560, 94), + LEVEL_20(20, R.style.LevelFive, 2560, 5120, 95), + LEVEL_21(21, R.style.LevelOne, 5120, 10240, 96), + LEVEL_22(22, R.style.LevelTwo, 10240, 20480, 97), + LEVEL_23(23, R.style.LevelThree, 20480, 40960, 98), + LEVEL_24(24, R.style.LevelFour, 40960, 81920, 98), + LEVEL_25(25, R.style.LevelFive, 81920, 163840, 98), + LEVEL_26(26, R.style.LevelOne, 163840, 327680, 98), + LEVEL_27(27, R.style.LevelTwo, 327680, 655360, 98), + ; companion object { @JvmStatic - fun from(imagesUploaded: Int, - uniqueImagesUsed: Int, - nonRevertRate: Int): LevelInfo { + fun from( + imagesUploaded: Int, + uniqueImagesUsed: Int, + nonRevertRate: Int, + ): LevelInfo { var level = LEVEL_15 for (levelInfo in values()) { - if (imagesUploaded < levelInfo.maxUploadCount || uniqueImagesUsed < levelInfo.maxUniqueImages || nonRevertRate < levelInfo.minNonRevertPercentage) { + if (imagesUploaded < levelInfo.maxUploadCount || + uniqueImagesUsed < levelInfo.maxUniqueImages || + nonRevertRate < levelInfo.minNonRevertPercentage + ) { level = levelInfo return level } @@ -30,6 +64,5 @@ class LevelController { return level } } - } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/quiz/QuizQuestion.kt b/app/src/main/java/fr/free/nrw/commons/quiz/QuizQuestion.kt index 0dbc9061e6..fd989c9742 100644 --- a/app/src/main/java/fr/free/nrw/commons/quiz/QuizQuestion.kt +++ b/app/src/main/java/fr/free/nrw/commons/quiz/QuizQuestion.kt @@ -5,13 +5,16 @@ import android.net.Uri /** * class contains information about all the quiz questions */ -class QuizQuestion internal constructor(var questionNumber: Int, var question: String, private var url: String, var isAnswer: Boolean, var answerMessage: String) { - fun getUrl(): Uri { - return Uri.parse(url) - } +class QuizQuestion internal constructor( + var questionNumber: Int, + var question: String, + private var url: String, + var isAnswer: Boolean, + var answerMessage: String, +) { + fun getUrl(): Uri = Uri.parse(url) fun setUrl(url: String) { this.url = url } - -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/recentlanguages/Language.kt b/app/src/main/java/fr/free/nrw/commons/recentlanguages/Language.kt index 9e7aa3ae84..c843492337 100644 --- a/app/src/main/java/fr/free/nrw/commons/recentlanguages/Language.kt +++ b/app/src/main/java/fr/free/nrw/commons/recentlanguages/Language.kt @@ -1,3 +1,6 @@ package fr.free.nrw.commons.recentlanguages -data class Language(val languageName: String, val languageCode: String) +data class Language( + val languageName: String, + val languageCode: String, +) diff --git a/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapter.kt b/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapter.kt index 79b9f23dee..81ef5533d9 100644 --- a/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapter.kt @@ -17,22 +17,26 @@ import java.util.HashMap class RecentLanguagesAdapter constructor( context: Context, var recentLanguages: List, - private val selectedLanguages: HashMap<*, String> + private val selectedLanguages: HashMap<*, String>, ) : ArrayAdapter(context, R.layout.row_item_languages_spinner) { - /** * Selected language code in UploadMediaDetailAdapter * Used for marking selected ones */ var selectedLangCode = "" - override fun isEnabled(position: Int) = recentLanguages[position].languageCode.let { - it.isNotEmpty() && !selectedLanguages.containsValue(it) && it != selectedLangCode - } + override fun isEnabled(position: Int) = + recentLanguages[position].languageCode.let { + it.isNotEmpty() && !selectedLanguages.containsValue(it) && it != selectedLangCode + } override fun getCount() = recentLanguages.size - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + override fun getView( + position: Int, + convertView: View?, + parent: ViewGroup, + ): View { val binding: RowItemLanguagesSpinnerBinding var rowView = convertView @@ -55,7 +59,7 @@ class RecentLanguagesAdapter constructor( } else { it.text = "${StringUtils.capitalize(languageName)}" + - " [${LangCodeUtils.fixLanguageCode(languageCode)}]" + " [${LangCodeUtils.fixLanguageCode(languageCode)}]" } } return rowView @@ -64,14 +68,10 @@ class RecentLanguagesAdapter constructor( /** * Provides code of a language from recent languages for a specific position */ - fun getLanguageCode(position: Int): String { - return recentLanguages[position].languageCode - } + fun getLanguageCode(position: Int): String = recentLanguages[position].languageCode /** * Provides name of a language from recent languages for a specific position */ - fun getLanguageName(position: Int): String { - return recentLanguages[position].languageName - } -} \ No newline at end of file + fun getLanguageName(position: Int): String = recentLanguages[position].languageName +} diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.kt b/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.kt index 16b8d26de2..8a77c11edb 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.kt +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.kt @@ -1,138 +1,149 @@ package fr.free.nrw.commons.review -import androidx.annotation.VisibleForTesting import fr.free.nrw.commons.Media import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage.Revision -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse import io.reactivex.Completable import io.reactivex.Observable import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers -import org.apache.commons.lang3.StringUtils import timber.log.Timber -import java.util.Collections import javax.inject.Inject import javax.inject.Singleton @Singleton -class ReviewHelper @Inject constructor( - private val mediaClient: MediaClient, - private val reviewInterface: ReviewInterface -) { - @JvmField @Inject var dao: ReviewDao? = null +class ReviewHelper + @Inject + constructor( + private val mediaClient: MediaClient, + private val reviewInterface: ReviewInterface, + ) { + @JvmField @Inject + var dao: ReviewDao? = null - /** - * Fetches recent changes from MediaWiki API - * Calls the API to get the latest 50 changes - * When more results are available, the query gets continued beyond this range - * - * @return - */ - private fun getRecentChanges() = reviewInterface.getRecentChanges() - .map { it.query()?.pages() } - .map(MutableList::shuffled) - .flatMapIterable { changes: List? -> changes } - .filter { isChangeReviewable(it) } + /** + * Fetches recent changes from MediaWiki API + * Calls the API to get the latest 50 changes + * When more results are available, the query gets continued beyond this range + * + * @return + */ + private fun getRecentChanges() = + reviewInterface + .getRecentChanges() + .map { it.query()?.pages() } + .map(MutableList::shuffled) + .flatMapIterable { changes: List? -> changes } + .filter { isChangeReviewable(it) } - /** - * Gets a random file change for review. Checks if the image has already been shown to the user - * - Picks a random file from those changes - * - Checks if the file is nominated for deletion - * - Retries upto 5 times for getting a file which is not nominated for deletion - * - * @return Random file change - */ - fun getRandomMedia(): Single = getRecentChanges() - .flatMapSingle(::getRandomMediaFromRecentChange) - .filter { !it.filename.isNullOrBlank() && !getReviewStatus(it.pageId) } - .firstOrError() + /** + * Gets a random file change for review. Checks if the image has already been shown to the user + * - Picks a random file from those changes + * - Checks if the file is nominated for deletion + * - Retries upto 5 times for getting a file which is not nominated for deletion + * + * @return Random file change + */ + fun getRandomMedia(): Single = + getRecentChanges() + .flatMapSingle(::getRandomMediaFromRecentChange) + .filter { !it.filename.isNullOrBlank() && !getReviewStatus(it.pageId) } + .firstOrError() - /** - * Returns a proper Media object if the file is not already nominated for deletion - * Else it returns an empty Media object - * - * @param recentChange - * @return - */ - private fun getRandomMediaFromRecentChange(recentChange: MwQueryPage) = - Single.just(recentChange) - .flatMap { mediaClient.checkPageExistsUsingTitle("Commons:Deletion_requests/${it.title()}") } - .flatMap { - if (it) { - Single.error(Exception("${recentChange.title()} is deleted")) - } else { - mediaClient.getMedia(recentChange.title()) + /** + * Returns a proper Media object if the file is not already nominated for deletion + * Else it returns an empty Media object + * + * @param recentChange + * @return + */ + private fun getRandomMediaFromRecentChange(recentChange: MwQueryPage) = + Single + .just(recentChange) + .flatMap { mediaClient.checkPageExistsUsingTitle("Commons:Deletion_requests/${it.title()}") } + .flatMap { + if (it) { + Single.error(Exception("${recentChange.title()} is deleted")) + } else { + mediaClient.getMedia(recentChange.title()) + } } - } - /** - * Checks if the image exists in the reviewed images entity - * - * @param image - * @return - */ - fun getReviewStatus(image: String?): Boolean = - dao?.isReviewedAlready(image) ?: false + /** + * Checks if the image exists in the reviewed images entity + * + * @param image + * @return + */ + fun getReviewStatus(image: String?): Boolean = dao?.isReviewedAlready(image) ?: false - /** - * Gets the first revision of the file from filename - * - * @param filename - * @return - */ - fun getFirstRevisionOfFile(filename: String?): Observable = - reviewInterface.getFirstRevisionOfFile(filename) - .map { it.query()?.firstPage()?.revisions()?.get(0) } + /** + * Gets the first revision of the file from filename + * + * @param filename + * @return + */ + fun getFirstRevisionOfFile(filename: String?): Observable = + reviewInterface + .getFirstRevisionOfFile(filename) + .map { + it + .query() + ?.firstPage() + ?.revisions() + ?.get(0) + } - /** - * Checks Whether Given File is used in any Wiki page or not - * by calling api for given file - * - * @param filename - * @return - */ - fun checkFileUsage(filename: String?): Observable = - reviewInterface.getGlobalUsageInfo(filename) - .map { it.query()?.firstPage()?.checkWhetherFileIsUsedInWikis() } + /** + * Checks Whether Given File is used in any Wiki page or not + * by calling api for given file + * + * @param filename + * @return + */ + fun checkFileUsage(filename: String?): Observable = + reviewInterface + .getGlobalUsageInfo(filename) + .map { it.query()?.firstPage()?.checkWhetherFileIsUsedInWikis() } - /** - * Checks if the change is reviewable or not. - * - checks the type and revisionId of the change - * - checks supported image extensions - * - * @param recentChange - * @return - */ - private fun isChangeReviewable(recentChange: MwQueryPage): Boolean { - for (extension in imageExtensions) { - if (recentChange.title().endsWith(extension)) { - return true + /** + * Checks if the change is reviewable or not. + * - checks the type and revisionId of the change + * - checks supported image extensions + * + * @param recentChange + * @return + */ + private fun isChangeReviewable(recentChange: MwQueryPage): Boolean { + for (extension in imageExtensions) { + if (recentChange.title().endsWith(extension)) { + return true + } } + return false } - return false - } - /** - * Adds reviewed/skipped images to the database - * - * @param imageId - */ - fun addViewedImagesToDB(imageId: String?) { - Completable.fromAction { dao!!.insert(ReviewEntity(imageId)) } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { - // Inserted successfully - Timber.i("Image inserted successfully.") - } - ) { throwable: Throwable? -> Timber.e("Image not inserted into the reviewed images database") } - } + /** + * Adds reviewed/skipped images to the database + * + * @param imageId + */ + fun addViewedImagesToDB(imageId: String?) { + Completable + .fromAction { dao!!.insert(ReviewEntity(imageId)) } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + // Inserted successfully + Timber.i("Image inserted successfully.") + }, + ) { throwable: Throwable? -> Timber.e("Image not inserted into the reviewed images database") } + } - companion object { - private val imageExtensions = arrayOf(".jpg", ".jpeg", ".png") + companion object { + private val imageExtensions = arrayOf(".jpg", ".jpeg", ".png") + } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewInterface.kt b/app/src/main/java/fr/free/nrw/commons/review/ReviewInterface.kt index 1fe9e1107b..f65b41b392 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewInterface.kt @@ -18,12 +18,18 @@ interface ReviewInterface { * the limit is increased from 10 to 50 using gcmlimit * */ - @GET("w/api.php?action=query&format=json&formatversion=2&generator=categorymembers&gcmtype=file&gcmsort=timestamp&gcmdir=desc&gcmtitle=Category:Uploaded_with_Mobile/Android&gcmlimit=50") + @GET( + "w/api.php?action=query&format=json&formatversion=2&generator=categorymembers&gcmtype=file&gcmsort=timestamp&gcmdir=desc&gcmtitle=Category:Uploaded_with_Mobile/Android&gcmlimit=50", + ) fun getRecentChanges(): Observable @GET("w/api.php?action=query&format=json&formatversion=2&prop=revisions&rvprop=timestamp|ids|user&rvdir=newer&rvlimit=1") - fun getFirstRevisionOfFile(@Query("titles") titles: String?): Observable + fun getFirstRevisionOfFile( + @Query("titles") titles: String?, + ): Observable @GET("w/api.php?action=query&format=json&formatversion=2&prop=fileusage|globalusage") - fun getGlobalUsageInfo(@Query("titles") title: String?): Observable + fun getGlobalUsageInfo( + @Query("titles") title: String?, + ): Observable } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/CountingRequestBody.kt b/app/src/main/java/fr/free/nrw/commons/upload/CountingRequestBody.kt index a777982013..e486685c4e 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/CountingRequestBody.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/CountingRequestBody.kt @@ -1,9 +1,12 @@ package fr.free.nrw.commons.upload -import fr.free.nrw.commons.contributions.ChunkInfo import okhttp3.MediaType import okhttp3.RequestBody -import okio.* +import okio.Buffer +import okio.BufferedSink +import okio.ForwardingSink +import okio.Sink +import okio.buffer import java.io.IOException /** @@ -17,12 +20,11 @@ class CountingRequestBody( protected var delegate: RequestBody, protected var listener: Listener, var offset: Long, - var totalContentLength: Long + var totalContentLength: Long, ) : RequestBody() { protected var countingSink: CountingSink? = null - override fun contentType(): MediaType? { - return delegate.contentType() - } + + override fun contentType(): MediaType? = delegate.contentType() override fun contentLength(): Long { try { @@ -41,11 +43,16 @@ class CountingRequestBody( bufferedSink.flush() } - protected inner class CountingSink(delegate: Sink?) : ForwardingSink(delegate!!) { + protected inner class CountingSink( + delegate: Sink?, + ) : ForwardingSink(delegate!!) { private var bytesWritten: Long = 0 @Throws(IOException::class) - override fun write(source: Buffer, byteCount: Long) { + override fun write( + source: Buffer, + byteCount: Long, + ) { super.write(source, byteCount) bytesWritten += byteCount listener.onRequestProgress(offset + bytesWritten, totalContentLength) @@ -58,7 +65,9 @@ class CountingRequestBody( * @param bytesWritten * @param contentLength */ - fun onRequestProgress(bytesWritten: Long, contentLength: Long) + fun onRequestProgress( + bytesWritten: Long, + contentLength: Long, + ) } - -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/Description.kt b/app/src/main/java/fr/free/nrw/commons/upload/Description.kt index 09bfa81d23..83902aae41 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/Description.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/Description.kt @@ -5,27 +5,27 @@ package fr.free.nrw.commons.upload */ class Description { /** - * @return The language code ie. "en" or "fr" - */ - /** - * @param languageCode The language code ie. "en" or "fr" + * The language code, e.g., "en" or "fr". + * @param languageCode The language code. */ var languageCode: String? = null - var descriptionText: String? = null + /** - * @return the index of the language selected in a spinner with [SpinnerLanguagesAdapter] + * The description text for the item being uploaded. + * @param descriptionText The description text. */ + var descriptionText: String? = null + /** - * @param selectedLanguageIndex the index of the language selected in a spinner with [SpinnerLanguagesAdapter] + * The index of the language selected in a spinner with [SpinnerLanguagesAdapter]. + * @param selectedLanguageIndex The index of the selected language. */ var selectedLanguageIndex = -1 + /** - * returns if the description was added manually (by the user, or we have added it programaticallly) - * @return - */ - /** - * sets to true if the description was manually added by the user - * @param manuallyAdded + * Indicates if the description was added manually (by the user or programmatically). + * @param manuallyAdded Sets to true if the description was manually added by the user. + * @return True if the description was manually added. */ var isManuallyAdded = false @@ -44,12 +44,16 @@ class Description { val descListString = StringBuilder() for (description in descriptions) { if (!description.isEmpty) { - val individualDescription = String.format("{{%s|1=%s}}", description.languageCode, - description.descriptionText) + val individualDescription = + String.format( + "{{%s|1=%s}}", + description.languageCode, + description.descriptionText, + ) descListString.append(individualDescription) } } return descListString.toString() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FailedUploadsAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/FailedUploadsAdapter.kt index aa0d6bd3de..fc57ffe41d 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FailedUploadsAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/FailedUploadsAdapter.kt @@ -25,14 +25,18 @@ import java.io.File * @param callback The callback to handle user actions such as Delete Uploads and Restart Uploads * on failed uploads. */ -class FailedUploadsAdapter(callback: Callback) : - PagedListAdapter(ContributionDiffCallback()) { +class FailedUploadsAdapter( + callback: Callback, +) : PagedListAdapter(ContributionDiffCallback()) { private var callback: Callback = callback /** * Creates a new ViewHolder instance. Inflates the layout for each item in the list. */ - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): ViewHolder { val view: View = LayoutInflater.from(parent.context).inflate(R.layout.item_failed_upload, parent, false) return ViewHolder(view) @@ -42,7 +46,10 @@ class FailedUploadsAdapter(callback: Callback) : * Binds data to the provided ViewHolder. Sets up the item view with data from the * contribution at the specified position. */ - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder( + holder: ViewHolder, + position: Int, + ) { val item: Contribution? = getItem(position) if (item != null) { holder.titleTextView.setText(item.media.displayTitle) @@ -86,7 +93,9 @@ class FailedUploadsAdapter(callback: Callback) : /** * ViewHolder for the failed upload item. Holds references to the views for each item. */ - class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + class ViewHolder( + itemView: View, + ) : RecyclerView.ViewHolder(itemView) { var itemImage: com.facebook.drawee.view.SimpleDraweeView = itemView.findViewById(R.id.itemImage) var titleTextView: TextView = itemView.findViewById(R.id.titleTextView) @@ -100,22 +109,22 @@ class FailedUploadsAdapter(callback: Callback) : * Returns the ID of the item at the specified position. Uses the pageId of the contribution * for unique identification. */ - override fun getItemId(position: Int): Long { - return getItem(position)?.pageId?.hashCode()?.toLong() ?: position.toLong() - } + override fun getItemId(position: Int): Long = getItem(position)?.pageId?.hashCode()?.toLong() ?: position.toLong() /** * Uses DiffUtil to calculate the changes in the list * It has methods that check pageId and the content of the items to determine if its a new item */ class ContributionDiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Contribution, newItem: Contribution): Boolean { - return oldItem.pageId.hashCode() == newItem.pageId.hashCode() - } + override fun areItemsTheSame( + oldItem: Contribution, + newItem: Contribution, + ): Boolean = oldItem.pageId.hashCode() == newItem.pageId.hashCode() - override fun areContentsTheSame(oldItem: Contribution, newItem: Contribution): Boolean { - return oldItem.transferred == newItem.transferred - } + override fun areContentsTheSame( + oldItem: Contribution, + newItem: Contribution, + ): Boolean = oldItem.transferred == newItem.transferred } /** diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FailedUploadsFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/FailedUploadsFragment.kt index e2bea2ab6c..876fb3cd3f 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FailedUploadsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/FailedUploadsFragment.kt @@ -5,10 +5,8 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.Fragment import androidx.paging.PagedList import androidx.recyclerview.widget.LinearLayoutManager -import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.R import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.contributions.Contribution @@ -26,9 +24,10 @@ import javax.inject.Inject * Fragment for displaying a list of failed uploads in Upload Progress Activity. This fragment provides * functionality for the user to retry or cancel failed uploads. */ -class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsContract.View, +class FailedUploadsFragment : + CommonsDaggerSupportFragment(), + PendingUploadsContract.View, FailedUploadsAdapter.Callback { - @Inject lateinit var pendingUploadsPresenter: PendingUploadsPresenter @@ -57,7 +56,7 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - //Now that we are allowing this fragment to be started for + // Now that we are allowing this fragment to be started for // any userName- we expect it to be passed as an argument if (arguments != null) { userName = requireArguments().getString(ProfileActivity.KEY_USERNAME) @@ -69,8 +68,9 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont } override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, ): View? { binding = FragmentFailedUploadsBinding.inflate(layoutInflater) pendingUploadsPresenter.onAttachView(this) @@ -82,7 +82,10 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont adapter = FailedUploadsAdapter(this) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) initRecyclerView() } @@ -95,7 +98,7 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont binding.failedUploadsRecyclerView.adapter = adapter pendingUploadsPresenter!!.getFailedContributions() pendingUploadsPresenter!!.failedContributionList.observe( - viewLifecycleOwner + viewLifecycleOwner, ) { list: PagedList -> adapter.submitList(list) contributionsList = ArrayList() @@ -125,7 +128,7 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont pendingUploadsPresenter.restartUploads( contributionsList, 0, - this.requireContext().applicationContext + this.requireContext().applicationContext, ) } } @@ -138,7 +141,7 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont pendingUploadsPresenter.restartUpload( contributionsList, index, - this.requireContext().applicationContext + this.requireContext().applicationContext, ) } } @@ -151,11 +154,11 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont requireActivity(), String.format( Locale.getDefault(), - requireActivity().getString(R.string.cancelling_upload) + requireActivity().getString(R.string.cancelling_upload), ), String.format( Locale.getDefault(), - requireActivity().getString(R.string.cancel_upload_dialog) + requireActivity().getString(R.string.cancel_upload_dialog), ), String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), @@ -163,10 +166,10 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont ViewUtil.showShortToast(context, R.string.cancelling_upload) pendingUploadsPresenter.deleteUpload( contribution, - this.requireContext().applicationContext + this.requireContext().applicationContext, ) }, - {} + {}, ) } @@ -179,11 +182,11 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont requireActivity(), String.format( Locale.getDefault(), - requireActivity().getString(R.string.cancelling_all_the_uploads) + requireActivity().getString(R.string.cancelling_all_the_uploads), ), String.format( Locale.getDefault(), - requireActivity().getString(R.string.are_you_sure_that_you_want_cancel_all_the_uploads) + requireActivity().getString(R.string.are_you_sure_that_you_want_cancel_all_the_uploads), ), String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), @@ -191,11 +194,11 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont ViewUtil.showShortToast(context, R.string.cancelling_upload) uploadProgressActivity.hidePendingIcons() pendingUploadsPresenter.deleteUploads( - listOf(Contribution.STATE_FAILED) + listOf(Contribution.STATE_FAILED), ) }, - {} + {}, ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.kt b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.kt index d73384b35c..68c6f13fbe 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.kt @@ -18,7 +18,7 @@ import io.reactivex.schedulers.Schedulers import timber.log.Timber import java.io.File import java.io.IOException -import java.util.* +import java.util.Locale import javax.inject.Inject import javax.inject.Named @@ -31,194 +31,205 @@ private const val MAX_SUGGESTION_RADIUS_IN_METRES = 1000 private const val RADIUS_STEP_SIZE_IN_METRES = 100 private const val MIN_NEARBY_RESULTS = 5 -class FileProcessor @Inject constructor( - private val context: Context, - private val contentResolver: ContentResolver, - private val gpsCategoryModel: GpsCategoryModel, - private val depictsModel: DepictModel, - @param:Named("default_preferences") private val defaultKvStore: JsonKvStore, - private val apiCall: CategoryApi, - private val okHttpJsonApiClient: OkHttpJsonApiClient -) { - private val compositeDisposable = CompositeDisposable() - - fun cleanup() { - compositeDisposable.clear() - } +class FileProcessor + @Inject + constructor( + private val context: Context, + private val contentResolver: ContentResolver, + private val gpsCategoryModel: GpsCategoryModel, + private val depictsModel: DepictModel, + @param:Named("default_preferences") private val defaultKvStore: JsonKvStore, + private val apiCall: CategoryApi, + private val okHttpJsonApiClient: OkHttpJsonApiClient, + ) { + private val compositeDisposable = CompositeDisposable() - /** - * Processes filePath coordinates, either from EXIF data or user location - */ - fun processFileCoordinates( - similarImageInterface: SimilarImageInterface, - filePath: String?, inAppPictureLocation: LatLng? - ) - : ImageCoordinates { - val exifInterface: ExifInterface? = try { - ExifInterface(filePath!!) - } catch (e: IOException) { - Timber.e(e) - null - } - // Redact EXIF data as indicated in preferences. - redactExifTags(exifInterface, getExifTagsToRedact()) - Timber.d("Calling GPSExtractor") - val originalImageCoordinates = ImageCoordinates(exifInterface, inAppPictureLocation) - if (originalImageCoordinates.decimalCoords == null) { - //Find other photos taken around the same time which has gps coordinates - findOtherImages( - File(filePath), - similarImageInterface - ) - } else { - prePopulateCategoriesAndDepictionsBy(originalImageCoordinates) + fun cleanup() { + compositeDisposable.clear() } - return originalImageCoordinates - } - - /** - * Gets EXIF Tags from preferences to be redacted. - * - * @return tags to be redacted - */ - fun getExifTagsToRedact(): Set { - val prefManageEXIFTags = - defaultKvStore.getStringSet(Prefs.MANAGED_EXIF_TAGS) ?: emptySet() - val redactTags: Set = - context.resources.getStringArray(R.array.pref_exifTag_values).toSet() - return redactTags - prefManageEXIFTags - } - /** - * Redacts EXIF metadata as indicated in preferences. - * - * @param exifInterface ExifInterface object - * @param redactTags tags to be redacted - */ - fun redactExifTags(exifInterface: ExifInterface?, redactTags: Set) { - compositeDisposable.add( - Observable.fromIterable(redactTags) - .flatMap { Observable.fromArray(*FileMetadataUtils.getTagsFromPref(it)) } - .subscribe( - { redactTag(exifInterface, it) }, - { Timber.d(it) }, - { save(exifInterface) } + /** + * Processes filePath coordinates, either from EXIF data or user location + */ + fun processFileCoordinates( + similarImageInterface: SimilarImageInterface, + filePath: String?, + inAppPictureLocation: LatLng?, + ): ImageCoordinates { + val exifInterface: ExifInterface? = + try { + ExifInterface(filePath!!) + } catch (e: IOException) { + Timber.e(e) + null + } + // Redact EXIF data as indicated in preferences. + redactExifTags(exifInterface, getExifTagsToRedact()) + Timber.d("Calling GPSExtractor") + val originalImageCoordinates = ImageCoordinates(exifInterface, inAppPictureLocation) + if (originalImageCoordinates.decimalCoords == null) { + // Find other photos taken around the same time which has gps coordinates + findOtherImages( + File(filePath), + similarImageInterface, ) - ) - } - - private fun save(exifInterface: ExifInterface?) { - try { - exifInterface?.saveAttributes() - } catch (e: IOException) { - Timber.w("EXIF redaction failed: %s", e.toString()) + } else { + prePopulateCategoriesAndDepictionsBy(originalImageCoordinates) + } + return originalImageCoordinates } - } - private fun redactTag(exifInterface: ExifInterface?, tag: String) { - Timber.d("Checking for tag: %s", tag) - exifInterface?.getAttribute(tag) - ?.takeIf { it.isNotEmpty() } - ?.let { attributeName -> - exifInterface.setAttribute(tag, null).also { - Timber.d("Exif tag $tag with value $attributeName redacted.") - } - } - } + /** + * Gets EXIF Tags from preferences to be redacted. + * + * @return tags to be redacted + */ + fun getExifTagsToRedact(): Set { + val prefManageEXIFTags = + defaultKvStore.getStringSet(Prefs.MANAGED_EXIF_TAGS) ?: emptySet() + val redactTags: Set = + context.resources.getStringArray(R.array.pref_exifTag_values).toSet() + return redactTags - prefManageEXIFTags + } - /** - * Find other images around the same location that were taken within the last 20 sec - * - * @param originalImageCoordinates - * @param fileBeingProcessed - * @param similarImageInterface - */ - private fun findOtherImages( - fileBeingProcessed: File, - similarImageInterface: SimilarImageInterface - ) { - val oneHundredAndTwentySeconds = 120 * 1000L - //Time when the original image was created - val timeOfCreation = fileBeingProcessed.lastModified() - LongRange - val timeOfCreationRange = - timeOfCreation - oneHundredAndTwentySeconds..timeOfCreation + oneHundredAndTwentySeconds - fileBeingProcessed.parentFile - .listFiles() - .asSequence() - .filter { it.lastModified() in timeOfCreationRange } - .map { Pair(it, readImageCoordinates(it)) } - .firstOrNull { it.second?.decimalCoords != null } - ?.let { fileCoordinatesPair -> - similarImageInterface.showSimilarImageFragment( - fileBeingProcessed.path, - fileCoordinatesPair.first.absolutePath, - fileCoordinatesPair.second - ) - } - } + /** + * Redacts EXIF metadata as indicated in preferences. + * + * @param exifInterface ExifInterface object + * @param redactTags tags to be redacted + */ + fun redactExifTags( + exifInterface: ExifInterface?, + redactTags: Set, + ) { + compositeDisposable.add( + Observable + .fromIterable(redactTags) + .flatMap { Observable.fromArray(*FileMetadataUtils.getTagsFromPref(it)) } + .subscribe( + { redactTag(exifInterface, it) }, + { Timber.d(it) }, + { save(exifInterface) }, + ), + ) + } - private fun readImageCoordinates(file: File) = - try { - /* Used null location as location for similar images captured before is not available - in case it is not present in the EXIF. */ - ImageCoordinates(contentResolver.openInputStream(Uri.fromFile(file))!!, null) - } catch (e: IOException) { - Timber.e(e) + private fun save(exifInterface: ExifInterface?) { try { - ImageCoordinates(file.absolutePath, null) - } catch (ex: IOException) { - Timber.e(ex) - null + exifInterface?.saveAttributes() + } catch (e: IOException) { + Timber.w("EXIF redaction failed: %s", e.toString()) } } - /** - * Initiates retrieval of image coordinates or user coordinates, and caching of coordinates. Then - * initiates the calls to MediaWiki API through an instance of CategoryApi. - * - * @param imageCoordinates - */ - fun prePopulateCategoriesAndDepictionsBy(imageCoordinates: ImageCoordinates) { - requireNotNull(imageCoordinates.decimalCoords) - compositeDisposable.add( - apiCall.request(imageCoordinates.decimalCoords) - .subscribeOn(Schedulers.io()) - .observeOn(Schedulers.io()) - .subscribe( - gpsCategoryModel::setCategoriesFromLocation, - { - Timber.e(it) - gpsCategoryModel.clear() + private fun redactTag( + exifInterface: ExifInterface?, + tag: String, + ) { + Timber.d("Checking for tag: %s", tag) + exifInterface + ?.getAttribute(tag) + ?.takeIf { it.isNotEmpty() } + ?.let { attributeName -> + exifInterface.setAttribute(tag, null).also { + Timber.d("Exif tag $tag with value $attributeName redacted.") } - ) - ) - - compositeDisposable.add( - suggestNearbyDepictions(imageCoordinates) - ) - } + } + } - private val radiiProgressionInMetres = - (DEFAULT_SUGGESTION_RADIUS_IN_METRES..MAX_SUGGESTION_RADIUS_IN_METRES step RADIUS_STEP_SIZE_IN_METRES) - - private fun suggestNearbyDepictions(imageCoordinates: ImageCoordinates): Disposable { - return Observable.fromIterable(radiiProgressionInMetres.map { it / 1000.0 }) - .concatMap { - Observable.fromCallable { - okHttpJsonApiClient.getNearbyPlaces( - imageCoordinates.latLng, - Locale.getDefault().language, - it + /** + * Find other images around the same location that were taken within the last 20 sec + * + * @param originalImageCoordinates + * @param fileBeingProcessed + * @param similarImageInterface + */ + private fun findOtherImages( + fileBeingProcessed: File, + similarImageInterface: SimilarImageInterface, + ) { + val oneHundredAndTwentySeconds = 120 * 1000L + // Time when the original image was created + val timeOfCreation = fileBeingProcessed.lastModified() + LongRange + val timeOfCreationRange = + timeOfCreation - oneHundredAndTwentySeconds..timeOfCreation + oneHundredAndTwentySeconds + fileBeingProcessed.parentFile + .listFiles() + .asSequence() + .filter { it.lastModified() in timeOfCreationRange } + .map { Pair(it, readImageCoordinates(it)) } + .firstOrNull { it.second?.decimalCoords != null } + ?.let { fileCoordinatesPair -> + similarImageInterface.showSimilarImageFragment( + fileBeingProcessed.path, + fileCoordinatesPair.first.absolutePath, + fileCoordinatesPair.second, ) } + } + + private fun readImageCoordinates(file: File) = + try { + /* Used null location as location for similar images captured before is not available + in case it is not present in the EXIF. */ + ImageCoordinates(contentResolver.openInputStream(Uri.fromFile(file))!!, null) + } catch (e: IOException) { + Timber.e(e) + try { + ImageCoordinates(file.absolutePath, null) + } catch (ex: IOException) { + Timber.e(ex) + null + } } - .subscribeOn(Schedulers.io()) - .filter { it.size >= MIN_NEARBY_RESULTS } - .take(1) - .subscribe( - { depictsModel.nearbyPlaces.offer(it) }, - { Timber.e(it) } + + /** + * Initiates retrieval of image coordinates or user coordinates, and caching of coordinates. Then + * initiates the calls to MediaWiki API through an instance of CategoryApi. + * + * @param imageCoordinates + */ + fun prePopulateCategoriesAndDepictionsBy(imageCoordinates: ImageCoordinates) { + requireNotNull(imageCoordinates.decimalCoords) + compositeDisposable.add( + apiCall + .request(imageCoordinates.decimalCoords) + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) + .subscribe( + gpsCategoryModel::setCategoriesFromLocation, + { + Timber.e(it) + gpsCategoryModel.clear() + }, + ), + ) + + compositeDisposable.add( + suggestNearbyDepictions(imageCoordinates), ) + } + + private val radiiProgressionInMetres = + (DEFAULT_SUGGESTION_RADIUS_IN_METRES..MAX_SUGGESTION_RADIUS_IN_METRES step RADIUS_STEP_SIZE_IN_METRES) + + private fun suggestNearbyDepictions(imageCoordinates: ImageCoordinates): Disposable = + Observable + .fromIterable(radiiProgressionInMetres.map { it / 1000.0 }) + .concatMap { + Observable.fromCallable { + okHttpJsonApiClient.getNearbyPlaces( + imageCoordinates.latLng, + Locale.getDefault().language, + it, + ) + } + }.subscribeOn(Schedulers.io()) + .filter { it.size >= MIN_NEARBY_RESULTS } + .take(1) + .subscribe( + { depictsModel.nearbyPlaces.offer(it) }, + { Timber.e(it) }, + ) } -} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.kt b/app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.kt index 4fab5e6f5c..377c1a2d2d 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.kt @@ -6,14 +6,16 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class GpsCategoryModel @Inject constructor() { - val categoriesFromLocation = BehaviorSubject.createDefault(emptyList()) +class GpsCategoryModel + @Inject + constructor() { + val categoriesFromLocation = BehaviorSubject.createDefault(emptyList()) - fun clear() { - categoriesFromLocation.onNext(emptyList()) - } + fun clear() { + categoriesFromLocation.onNext(emptyList()) + } - fun setCategoriesFromLocation(categoryList: List) { - categoriesFromLocation.onNext(categoryList) + fun setCategoriesFromLocation(categoryList: List) { + categoriesFromLocation.onNext(categoryList) + } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ImageCoordinates.kt b/app/src/main/java/fr/free/nrw/commons/upload/ImageCoordinates.kt index 3f2ab36a60..a6976407f5 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ImageCoordinates.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/ImageCoordinates.kt @@ -12,23 +12,29 @@ import java.io.InputStream * Otherwise, if current user location is available while using the in-app camera, * use it to set image coordinates */ -class ImageCoordinates internal constructor(exif: ExifInterface?, inAppPictureLocation: LatLng?) { +class ImageCoordinates internal constructor( + exif: ExifInterface?, + inAppPictureLocation: LatLng?, +) { var decLatitude = 0.0 var decLongitude = 0.0 var imageCoordsExists = false + /** * @return string of `"[decLatitude]|[decLongitude]"` or null if coordinates do not exist */ var decimalCoords: String? = null - var zoomLevel : Double = 16.0 + /** * @return double value of zoom or 16.0 by default */ + var zoomLevel: Double = 16.0 /** * Construct from a stream. */ internal constructor(stream: InputStream, inAppPictureLocation: LatLng?) : this(ExifInterface(stream), inAppPictureLocation) + /** * Construct from the file path of the image. * @param path file path of the image @@ -37,8 +43,8 @@ class ImageCoordinates internal constructor(exif: ExifInterface?, inAppPictureLo internal constructor(path: String, inAppPictureLocation: LatLng?) : this(ExifInterface(path), inAppPictureLocation) init { - //If image has no EXIF data and user has enabled GPS setting, get user's location - //Always return null as a temporary fix for #1599 + // If image has no EXIF data and user has enabled GPS setting, get user's location + // Always return null as a temporary fix for #1599 if (exif != null) { val latAndLong = exif.latLong if (latAndLong != null) { @@ -51,16 +57,22 @@ class ImageCoordinates internal constructor(exif: ExifInterface?, inAppPictureLo val longitudeRef = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF) if (latitude != null && longitude != null && latitudeRef != null && longitudeRef != null) { decLatitude = - if (ExifInterface.LATITUDE_NORTH == latitudeRef) convertToDegree(latitude) - else 0 - convertToDegree(latitude) + if (ExifInterface.LATITUDE_NORTH == latitudeRef) { + convertToDegree(latitude) + } else { + 0 - convertToDegree(latitude) + } decLongitude = - if (ExifInterface.LONGITUDE_EAST == longitudeRef) convertToDegree(longitude) - else 0 - convertToDegree(longitude) + if (ExifInterface.LONGITUDE_EAST == longitudeRef) { + convertToDegree(longitude) + } else { + 0 - convertToDegree(longitude) + } } } if (!(decLatitude == 0.0 && decLongitude == 0.0)) { decimalCoords = "$decLatitude|$decLongitude" - //If image has EXIF data, extract image coords + // If image has EXIF data, extract image coords imageCoordsExists = true Timber.d("EXIF data has location info") } else if (inAppPictureLocation != null) { @@ -91,6 +103,5 @@ class ImageCoordinates internal constructor(exif: ExifInterface?, inAppPictureLo degrees + minutes / 60 + seconds / 3600 } - private fun evaluateExpression(dm: String) = - dm.split("/").let { it[0].toDouble() / it[1].toDouble() } + private fun evaluateExpression(dm: String) = dm.split("/").let { it[0].toDouble() / it[1].toDouble() } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/Language.kt b/app/src/main/java/fr/free/nrw/commons/upload/Language.kt index 78b7f593d7..47389349a4 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/Language.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/Language.kt @@ -1,8 +1,9 @@ package fr.free.nrw.commons.upload -import java.util.* +import java.util.Locale -class Language(var locale: Locale) { +class Language( + var locale: Locale, +) { var isSet = false - -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/LanguagesAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/LanguagesAdapter.kt index 9037e8a991..2847fa0c0b 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/LanguagesAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/LanguagesAdapter.kt @@ -9,10 +9,10 @@ import android.widget.Filter import androidx.core.os.ConfigurationCompat import fr.free.nrw.commons.R import fr.free.nrw.commons.databinding.RowItemLanguagesSpinnerBinding +import fr.free.nrw.commons.language.AppLanguageLookUpTable import fr.free.nrw.commons.utils.LangCodeUtils import org.apache.commons.lang3.StringUtils -import fr.free.nrw.commons.language.AppLanguageLookUpTable -import java.util.* +import java.util.Locale /** * This class handles the display of language dialog and their views for UploadMediaDetailFragment @@ -23,9 +23,8 @@ import java.util.* */ class LanguagesAdapter constructor( context: Context, - private val selectedLanguages: HashMap<*, String> + private val selectedLanguages: HashMap<*, String>, ) : ArrayAdapter(context, R.layout.row_item_languages_spinner) { - companion object { /** * Represents the default index for the language list. By default, this index corresponds to the @@ -41,6 +40,7 @@ class LanguagesAdapter constructor( var language: AppLanguageLookUpTable = AppLanguageLookUpTable(context) + init { languageNamesList = language.localizedNames languageCodesList = language.codes @@ -49,14 +49,18 @@ class LanguagesAdapter constructor( private val filter = LanguageFilter() var selectedLangCode = "" - override fun isEnabled(position: Int) = languageCodesList[position].let { - it.isNotEmpty() && !selectedLanguages.containsValue(it) && it != selectedLangCode - } + override fun isEnabled(position: Int) = + languageCodesList[position].let { + it.isNotEmpty() && !selectedLanguages.containsValue(it) && it != selectedLangCode + } override fun getCount() = languageNamesList.size - - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + override fun getView( + position: Int, + convertView: View?, + parent: ViewGroup, + ): View { val binding: RowItemLanguagesSpinnerBinding var rowView = convertView @@ -79,22 +83,18 @@ class LanguagesAdapter constructor( } else { it.text = "${StringUtils.capitalize(languageName)}" + - " [${LangCodeUtils.fixLanguageCode(languageCode)}]" + " [${LangCodeUtils.fixLanguageCode(languageCode)}]" } } return rowView } - fun getLanguageCode(position: Int): String { - return languageCodesList[position] - } + fun getLanguageCode(position: Int): String = languageCodesList[position] /** * Provides name of a language from languages for a specific position */ - fun getLanguageName(position: Int): String { - return languageNamesList[position] - } + fun getLanguageName(position: Int): String = languageNamesList[position] /** * Retrieves the index of the user's default locale from the list of available languages. @@ -116,21 +116,15 @@ class LanguagesAdapter constructor( * Future contributors are advised not to simplify this function without addressing this concern. */ fun getIndexOfUserDefaultLocale(context: Context): Int { - val userLanguageCode = context.locale?.language ?: return DEFAULT_INDEX return language.codes.indexOf(userLanguageCode).takeIf { it >= 0 } ?: DEFAULT_INDEX } - fun getIndexOfLanguageCode(languageCode: String): Int { - - return languageCodesList.indexOf(languageCode) - } - + fun getIndexOfLanguageCode(languageCode: String): Int = languageCodesList.indexOf(languageCode) override fun getFilter() = filter inner class LanguageFilter : Filter() { - override fun performFiltering(constraint: CharSequence?): FilterResults { val filterResults = FilterResults() val temp: LinkedHashMap = LinkedHashMap() @@ -141,9 +135,14 @@ class LanguagesAdapter constructor( val key: String = language.codes[i] val value: String = language.localizedNames[i] val defaultlanguagecode = getIndexOfUserDefaultLocale(context) - if(value.contains(constraint, true) || Locale(key).getDisplayName( - Locale(language.codes[defaultlanguagecode])).contains(constraint, true)) + if (value.contains(constraint, true) || + Locale(key) + .getDisplayName( + Locale(language.codes[defaultlanguagecode]), + ).contains(constraint, true) + ) { temp[key] = value + } i++ } filterResults.values = temp @@ -152,7 +151,10 @@ class LanguagesAdapter constructor( return filterResults } - override fun publishResults(constraint: CharSequence?, results: FilterResults) { + override fun publishResults( + constraint: CharSequence?, + results: FilterResults, + ) { if (results.count > 0) { languageCodesList = ArrayList((results.values as LinkedHashMap).keys) @@ -164,11 +166,8 @@ class LanguagesAdapter constructor( languageNamesList = ArrayList() notifyDataSetChanged() } - } - } - } private val Context.locale: Locale? diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsAdapter.kt index 49e6f592d9..8edfcb472b 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsAdapter.kt @@ -15,7 +15,6 @@ import androidx.recyclerview.widget.RecyclerView import com.facebook.imagepipeline.request.ImageRequest import fr.free.nrw.commons.R import fr.free.nrw.commons.contributions.Contribution -import timber.log.Timber import java.io.File /** @@ -25,15 +24,20 @@ import java.io.File * * @param callback The callback to handle user actions such as Delete Uploads on pending uploads. */ -class PendingUploadsAdapter(private val callback: Callback) : - PagedListAdapter(ContributionDiffCallback()) { - +class PendingUploadsAdapter( + private val callback: Callback, +) : PagedListAdapter(ContributionDiffCallback()) { /** * Creates a new ViewHolder instance. Inflates the layout for each item in the list. */ - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view: View = LayoutInflater.from(parent.context) - .inflate(R.layout.item_pending_upload, parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): ViewHolder { + val view: View = + LayoutInflater + .from(parent.context) + .inflate(R.layout.item_pending_upload, parent, false) return ViewHolder(view) } @@ -41,14 +45,19 @@ class PendingUploadsAdapter(private val callback: Callback) : * Binds data to the provided ViewHolder. Sets up the item view with data from the * contribution at the specified position utilizing payloads. */ - override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList) { + override fun onBindViewHolder( + holder: ViewHolder, + position: Int, + payloads: MutableList, + ) { if (payloads.isNotEmpty()) { when (val latestPayload = payloads.lastOrNull()) { - is ContributionChangePayload.Progress -> holder.bindProgress( - latestPayload.transferred, - latestPayload.total, - getItem(position)!!.state - ) + is ContributionChangePayload.Progress -> + holder.bindProgress( + latestPayload.transferred, + latestPayload.total, + getItem(position)!!.state, + ) is ContributionChangePayload.State -> holder.bindState(latestPayload.state) else -> onBindViewHolder(holder, position) @@ -62,7 +71,10 @@ class PendingUploadsAdapter(private val callback: Callback) : * Binds data to the provided ViewHolder. Sets up the item view with data from the * contribution at the specified position. */ - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder( + holder: ViewHolder, + position: Int, + ) { val contribution = getItem(position) contribution?.let { holder.bind(it) @@ -75,7 +87,9 @@ class PendingUploadsAdapter(private val callback: Callback) : /** * ViewHolder class for holding and binding item views. */ - class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + class ViewHolder( + itemView: View, + ) : RecyclerView.ViewHolder(itemView) { var itemImage: com.facebook.drawee.view.SimpleDraweeView = itemView.findViewById(R.id.itemImage) var titleTextView: TextView = itemView.findViewById(R.id.titleTextView) @@ -117,7 +131,11 @@ class PendingUploadsAdapter(private val callback: Callback) : } } - fun bindProgress(transferred: Long, total: Long, state: Int) { + fun bindProgress( + transferred: Long, + total: Long, + state: Int, + ) { if (transferred == 0L) { errorTextView.text = "Queued" errorTextView.visibility = View.VISIBLE @@ -165,9 +183,10 @@ class PendingUploadsAdapter(private val callback: Callback) : * @param newItem The new contribution item. * @return True if the items are the same, false otherwise. */ - override fun areItemsTheSame(oldItem: Contribution, newItem: Contribution): Boolean { - return oldItem.pageId.hashCode() == newItem.pageId.hashCode() - } + override fun areItemsTheSame( + oldItem: Contribution, + newItem: Contribution, + ): Boolean = oldItem.pageId.hashCode() == newItem.pageId.hashCode() /** * Checks if the content of two items is the same. @@ -175,9 +194,10 @@ class PendingUploadsAdapter(private val callback: Callback) : * @param newItem The new contribution item. * @return True if the contents are the same, false otherwise. */ - override fun areContentsTheSame(oldItem: Contribution, newItem: Contribution): Boolean { - return oldItem.transferred == newItem.transferred - } + override fun areContentsTheSame( + oldItem: Contribution, + newItem: Contribution, + ): Boolean = oldItem.transferred == newItem.transferred /** * Returns a payload representing the change between the old and new items. @@ -185,8 +205,11 @@ class PendingUploadsAdapter(private val callback: Callback) : * @param newItem The new contribution item. * @return An object representing the change, or null if there are no changes. */ - override fun getChangePayload(oldItem: Contribution, newItem: Contribution): Any? { - return when { + override fun getChangePayload( + oldItem: Contribution, + newItem: Contribution, + ): Any? = + when { oldItem.transferred != newItem.transferred -> { ContributionChangePayload.Progress(newItem.transferred, newItem.dataLength) } @@ -197,7 +220,6 @@ class PendingUploadsAdapter(private val callback: Callback) : else -> super.getChangePayload(oldItem, newItem) } - } } /** @@ -205,9 +227,7 @@ class PendingUploadsAdapter(private val callback: Callback) : * @param position The position of the item. * @return The unique item ID. */ - override fun getItemId(position: Int): Long { - return getItem(position)?.pageId?.hashCode()?.toLong() ?: position.toLong() - } + override fun getItemId(position: Int): Long = getItem(position)?.pageId?.hashCode()?.toLong() ?: position.toLong() /** * Sealed interface representing different types of changes to a contribution. @@ -218,12 +238,17 @@ class PendingUploadsAdapter(private val callback: Callback) : * @param transferred The amount of data transferred. * @param total The total amount of data. */ - data class Progress(val transferred: Long, val total: Long) : ContributionChangePayload + data class Progress( + val transferred: Long, + val total: Long, + ) : ContributionChangePayload /** * Represents a change in the state of a contribution. * @param state The state of the contribution. */ - data class State(val state: Int) : ContributionChangePayload + data class State( + val state: Int, + ) : ContributionChangePayload } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsFragment.kt index 788d1ed57e..4d79bc88e8 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsFragment.kt @@ -1,29 +1,19 @@ package fr.free.nrw.commons.upload import android.content.Context -import android.os.AsyncTask -import android.os.Build.VERSION -import android.os.Build.VERSION_CODES import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.paging.PagedList -import androidx.paging.PositionalDataSource import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.R -import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.databinding.FragmentPendingUploadsBinding import fr.free.nrw.commons.di.CommonsDaggerSupportFragment -import fr.free.nrw.commons.media.MediaClient -import fr.free.nrw.commons.profile.ProfileActivity import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog import fr.free.nrw.commons.utils.ViewUtil -import org.apache.commons.lang3.StringUtils -import timber.log.Timber import java.util.Locale import javax.inject.Inject @@ -31,9 +21,10 @@ import javax.inject.Inject * Fragment for showing pending uploads in Upload Progress Activity. This fragment provides * functionality for the user to pause uploads. */ -class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsContract.View, +class PendingUploadsFragment : + CommonsDaggerSupportFragment(), + PendingUploadsContract.View, PendingUploadsAdapter.Callback { - @Inject lateinit var pendingUploadsPresenter: PendingUploadsPresenter @@ -54,8 +45,9 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon } override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, ): View? { super.onCreate(savedInstanceState) binding = FragmentPendingUploadsBinding.inflate(inflater, container, false) @@ -68,7 +60,10 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon adapter = PendingUploadsAdapter(this) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { super.onViewCreated(view, savedInstanceState) initRecyclerView() } @@ -81,21 +76,21 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon binding.pendingUploadsRecyclerView.adapter = adapter pendingUploadsPresenter!!.setup() pendingUploadsPresenter!!.totalContributionList.observe( - viewLifecycleOwner + viewLifecycleOwner, ) { list: PagedList -> contributionsSize = list.size contributionsList = ArrayList() var pausedOrQueuedUploads = 0 list.forEach { if (it != null) { - if (it.state == Contribution.STATE_PAUSED - || it.state == Contribution.STATE_QUEUED - || it.state == Contribution.STATE_IN_PROGRESS + if (it.state == Contribution.STATE_PAUSED || + it.state == Contribution.STATE_QUEUED || + it.state == Contribution.STATE_IN_PROGRESS ) { contributionsList.add(it) } - if (it.state == Contribution.STATE_PAUSED - || it.state == Contribution.STATE_QUEUED + if (it.state == Contribution.STATE_PAUSED || + it.state == Contribution.STATE_QUEUED ) { pausedOrQueuedUploads++ } @@ -127,11 +122,11 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon requireActivity(), String.format( Locale.getDefault(), - requireActivity().getString(R.string.cancelling_upload) + requireActivity().getString(R.string.cancelling_upload), ), String.format( Locale.getDefault(), - requireActivity().getString(R.string.cancel_upload_dialog) + requireActivity().getString(R.string.cancel_upload_dialog), ), String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), @@ -139,10 +134,10 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon ViewUtil.showShortToast(context, R.string.cancelling_upload) pendingUploadsPresenter.deleteUpload( contribution, - this.requireContext().applicationContext + this.requireContext().applicationContext, ) }, - {} + {}, ) } @@ -154,7 +149,7 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon pendingUploadsPresenter.restartUploads( contributionsList, 0, - this.requireContext().applicationContext + this.requireContext().applicationContext, ) } } @@ -174,11 +169,11 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon requireActivity(), String.format( Locale.getDefault(), - requireActivity().getString(R.string.cancelling_all_the_uploads) + requireActivity().getString(R.string.cancelling_all_the_uploads), ), String.format( Locale.getDefault(), - requireActivity().getString(R.string.are_you_sure_that_you_want_cancel_all_the_uploads) + requireActivity().getString(R.string.are_you_sure_that_you_want_cancel_all_the_uploads), ), String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), @@ -189,12 +184,11 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon listOf( Contribution.STATE_QUEUED, Contribution.STATE_IN_PROGRESS, - Contribution.STATE_PAUSED - ) + Contribution.STATE_PAUSED, + ), ) }, - {} + {}, ) - } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/StashUploadResult.kt b/app/src/main/java/fr/free/nrw/commons/upload/StashUploadResult.kt index 91f17da07c..4baf52f993 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/StashUploadResult.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/StashUploadResult.kt @@ -3,12 +3,12 @@ package fr.free.nrw.commons.upload data class StashUploadResult( val state: StashUploadState, val fileKey: String?, - val errorMessage : String? + val errorMessage: String?, ) enum class StashUploadState { SUCCESS, PAUSED, FAILED, - CANCELLED + CANCELLED, } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadClient.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadClient.kt index 6da8da2dad..54f9b112fd 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadClient.kt @@ -29,257 +29,276 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class UploadClient @Inject constructor( - private val uploadInterface: UploadInterface, - private val csrfTokenClient: CsrfTokenClient, - private val pageContentsCreator: PageContentsCreator, - private val fileUtilsWrapper: FileUtilsWrapper, - private val gson: Gson, private val timeProvider: TimeProvider, - private val contributionDao: ContributionDao -) { - private val CHUNK_SIZE = 512 * 1024 // 512 KB +class UploadClient + @Inject + constructor( + private val uploadInterface: UploadInterface, + private val csrfTokenClient: CsrfTokenClient, + private val pageContentsCreator: PageContentsCreator, + private val fileUtilsWrapper: FileUtilsWrapper, + private val gson: Gson, + private val timeProvider: TimeProvider, + private val contributionDao: ContributionDao, + ) { + private val chunkSize = 512 * 1024 // 512 KB - //This is maximum duration for which a stash is persisted on MediaWiki - // https://www.mediawiki.org/wiki/Manual:$wgUploadStashMaxAge - private val MAX_CHUNK_AGE = 6 * 3600 * 1000 // 6 hours - private val compositeDisposable = CompositeDisposable() + // This is maximum duration for which a stash is persisted on MediaWiki + // https://www.mediawiki.org/wiki/Manual:$wgUploadStashMaxAge + private val maxChunkAge = 6 * 3600 * 1000 // 6 hours + private val compositeDisposable = CompositeDisposable() - /** - * Upload file to stash in chunks of specified size. Uploading files in chunks will make - * handling of large files easier. Also, it will be useful in supporting pause/resume of - * uploads - */ - @Throws(IOException::class) - fun uploadFileToStash( - filename: String, contribution: Contribution, - notificationUpdater: NotificationUpdateProgressListener - ): Observable { - if (contribution.isCompleted()) { - return Observable.just( - StashUploadResult(StashUploadState.SUCCESS, contribution.fileKey,null) - ) - } + /** + * Upload file to stash in chunks of specified size. Uploading files in chunks will make + * handling of large files easier. Also, it will be useful in supporting pause/resume of + * uploads + */ + @Throws(IOException::class) + fun uploadFileToStash( + filename: String, + contribution: Contribution, + notificationUpdater: NotificationUpdateProgressListener, + ): Observable { + if (contribution.isCompleted()) { + return Observable.just( + StashUploadResult(StashUploadState.SUCCESS, contribution.fileKey, null), + ) + } - val file = contribution.localUriPath - val fileChunks = fileUtilsWrapper.getFileChunks(file, CHUNK_SIZE) - val mediaType = fileUtilsWrapper.getMimeType(file).toMediaTypeOrNull() + val file = contribution.localUriPath + val fileChunks = fileUtilsWrapper.getFileChunks(file, chunkSize) + val mediaType = fileUtilsWrapper.getMimeType(file).toMediaTypeOrNull() - val chunkInfo = AtomicReference() - if (isStashValid(contribution)) { - chunkInfo.set(contribution.chunkInfo) - Timber.d( - "Chunk: Next Chunk: %s, Total Chunks: %s", - contribution.chunkInfo!!.indexOfNextChunkToUpload, - contribution.chunkInfo!!.totalChunks - ) - } + val chunkInfo = AtomicReference() + if (isStashValid(contribution)) { + chunkInfo.set(contribution.chunkInfo) + Timber.d( + "Chunk: Next Chunk: %s, Total Chunks: %s", + contribution.chunkInfo!!.indexOfNextChunkToUpload, + contribution.chunkInfo!!.totalChunks, + ) + } - val index = AtomicInteger() - val failures = AtomicBoolean() - val errorMessage = AtomicReference() - compositeDisposable.add( - Observable.fromIterable(fileChunks).forEach { chunkFile: File -> - if (canProcess(contributionDao, contribution, failures)) { - if (contributionDao.getContribution(contribution.pageId) == null) { - compositeDisposable.clear() - return@forEach - } else { - processChunk( - filename, - contribution, - notificationUpdater, - chunkFile, - failures, - chunkInfo, - index, - errorMessage, - mediaType!!, - file!!, - fileChunks.size - ) + val index = AtomicInteger() + val failures = AtomicBoolean() + val errorMessage = AtomicReference() + compositeDisposable.add( + Observable.fromIterable(fileChunks).forEach { chunkFile: File -> + if (canProcess(contributionDao, contribution, failures)) { + if (contributionDao.getContribution(contribution.pageId) == null) { + compositeDisposable.clear() + return@forEach + } else { + processChunk( + filename, + contribution, + notificationUpdater, + chunkFile, + failures, + chunkInfo, + index, + errorMessage, + mediaType!!, + file!!, + fileChunks.size, + ) + } } - } - } - ) + }, + ) - return when { - contributionDao.getContribution(contribution.pageId) == null -> { - return Observable.just(StashUploadResult(StashUploadState.CANCELLED, null, "Upload cancelled")) - } - contributionDao.getContribution(contribution.pageId).state == Contribution.STATE_PAUSED - || CommonsApplication.isPaused -> { - Timber.d("Upload stash paused %s", contribution.pageId) - Observable.just(StashUploadResult(StashUploadState.PAUSED, null, null)) - } - failures.get() -> { - Timber.d("Upload stash contains failures %s", contribution.pageId) - Observable.just(StashUploadResult(StashUploadState.FAILED, null, errorMessage.get())) - } - chunkInfo.get() != null -> { - Timber.d("Upload stash success %s", contribution.pageId) - Observable.just( - StashUploadResult( - StashUploadState.SUCCESS, - chunkInfo.get()!!.uploadResult!!.filekey, - "success" + return when { + contributionDao.getContribution(contribution.pageId) == null -> { + return Observable.just(StashUploadResult(StashUploadState.CANCELLED, null, "Upload cancelled")) + } + contributionDao.getContribution(contribution.pageId).state == Contribution.STATE_PAUSED || + CommonsApplication.isPaused -> { + Timber.d("Upload stash paused %s", contribution.pageId) + Observable.just(StashUploadResult(StashUploadState.PAUSED, null, null)) + } + failures.get() -> { + Timber.d("Upload stash contains failures %s", contribution.pageId) + Observable.just(StashUploadResult(StashUploadState.FAILED, null, errorMessage.get())) + } + chunkInfo.get() != null -> { + Timber.d("Upload stash success %s", contribution.pageId) + Observable.just( + StashUploadResult( + StashUploadState.SUCCESS, + chunkInfo.get()!!.uploadResult!!.filekey, + "success", + ), ) - ) - } - else -> { - Timber.d("Upload stash failed %s", contribution.pageId) - Observable.just(StashUploadResult(StashUploadState.FAILED, null,null)) + } + else -> { + Timber.d("Upload stash failed %s", contribution.pageId) + Observable.just(StashUploadResult(StashUploadState.FAILED, null, null)) + } } } - } - private fun processChunk( - filename: String, contribution: Contribution, - notificationUpdater: NotificationUpdateProgressListener, chunkFile: File, - failures: AtomicBoolean, chunkInfo: AtomicReference, index: AtomicInteger, - errorMessage : AtomicReference, mediaType: MediaType, file: File, totalChunks: Int - ) { - if (shouldSkip(chunkInfo, index)) { - index.incrementAndGet() - Timber.d("Chunk: Increment and return: %s", index.get()) - return - } + private fun processChunk( + filename: String, + contribution: Contribution, + notificationUpdater: NotificationUpdateProgressListener, + chunkFile: File, + failures: AtomicBoolean, + chunkInfo: AtomicReference, + index: AtomicInteger, + errorMessage: AtomicReference, + mediaType: MediaType, + file: File, + totalChunks: Int, + ) { + if (shouldSkip(chunkInfo, index)) { + index.incrementAndGet() + Timber.d("Chunk: Increment and return: %s", index.get()) + return + } - index.getAndIncrement() + index.getAndIncrement() - val offset = if (chunkInfo.get() != null) chunkInfo.get()!!.uploadResult!!.offset else 0 - Timber.d("Chunk: Sending Chunk number: %s, offset: %s", index.get(), offset) + val offset = if (chunkInfo.get() != null) chunkInfo.get()!!.uploadResult!!.offset else 0 + Timber.d("Chunk: Sending Chunk number: %s, offset: %s", index.get(), offset) - val filekey = chunkInfo.get()?.let { it.uploadResult!!.filekey } - val requestBody = chunkFile.asRequestBody(mediaType) - val listener = { transferred: Long, total: Long -> - notificationUpdater.onProgress(transferred, total) - } - val countingRequestBody = CountingRequestBody(requestBody, listener, offset.toLong(), file.length()) + val filekey = chunkInfo.get()?.let { it.uploadResult!!.filekey } + val requestBody = chunkFile.asRequestBody(mediaType) + val listener = { transferred: Long, total: Long -> + notificationUpdater.onProgress(transferred, total) + } + val countingRequestBody = CountingRequestBody(requestBody, listener, offset.toLong(), file.length()) - compositeDisposable.add( - uploadChunkToStash( - filename, file.length(), offset.toLong(), filekey, countingRequestBody - ).subscribe( - { uploadResult: UploadResult -> - Timber.d( - "Chunk: Received Chunk number: %s, offset: %s", - index.get(), - uploadResult.offset - ) - chunkInfo.set(ChunkInfo(uploadResult, index.get(), totalChunks)) - notificationUpdater.onChunkUploaded(contribution, chunkInfo.get()) - }, { throwable: Throwable? -> - Timber.e(throwable, "Received error in chunk upload") - errorMessage.set(throwable?.message) - failures.set(true) - } + compositeDisposable.add( + uploadChunkToStash( + filename, + file.length(), + offset.toLong(), + filekey, + countingRequestBody, + ).subscribe( + { uploadResult: UploadResult -> + Timber.d( + "Chunk: Received Chunk number: %s, offset: %s", + index.get(), + uploadResult.offset, + ) + chunkInfo.set(ChunkInfo(uploadResult, index.get(), totalChunks)) + notificationUpdater.onChunkUploaded(contribution, chunkInfo.get()) + }, + { throwable: Throwable? -> + Timber.e(throwable, "Received error in chunk upload") + errorMessage.set(throwable?.message) + failures.set(true) + }, + ), ) - ) - } + } - /** - * Stash is valid for 6 hours. This function checks the validity of stash - * - * @param contribution - * @return - */ - private fun isStashValid(contribution: Contribution): Boolean { - return contribution.chunkInfo != null && - contribution.dateModified!!.after(Date( - timeProvider.currentTimeMillis() - MAX_CHUNK_AGE)) - } + /** + * Stash is valid for 6 hours. This function checks the validity of stash + * + * @param contribution + * @return + */ + private fun isStashValid(contribution: Contribution): Boolean = + contribution.chunkInfo != null && + contribution.dateModified!!.after( + Date( + timeProvider.currentTimeMillis() - maxChunkAge, + ), + ) - /** - * Uploads a file chunk to stash - * - * @param filename The name of the file being uploaded - * @param fileSize The total size of the file - * @param offset The offset returned by the previous chunk upload - * @param fileKey The filekey returned by the previous chunk upload - * @param countingRequestBody Request body with chunk file - * @return - */ - fun uploadChunkToStash( - filename: String?, - fileSize: Long, - offset: Long, - fileKey: String?, - countingRequestBody: CountingRequestBody - ): Observable { - val filePart: MultipartBody.Part - return try { - filePart = MultipartBody.Part.createFormData( - "chunk", - URLEncoder.encode(filename, "utf-8"), - countingRequestBody - ) - uploadInterface.uploadFileToStash( - toRequestBody(filename), - toRequestBody(fileSize.toString()), - toRequestBody(offset.toString()), - toRequestBody(fileKey), - toRequestBody(csrfTokenClient.getTokenBlocking()), - filePart - ).map(UploadResponse::upload) - } catch (throwable: Throwable) { - Timber.e(throwable, "Failed to upload chunk to stash") - Observable.error(throwable) + /** + * Uploads a file chunk to stash + * + * @param filename The name of the file being uploaded + * @param fileSize The total size of the file + * @param offset The offset returned by the previous chunk upload + * @param fileKey The filekey returned by the previous chunk upload + * @param countingRequestBody Request body with chunk file + * @return + */ + fun uploadChunkToStash( + filename: String?, + fileSize: Long, + offset: Long, + fileKey: String?, + countingRequestBody: CountingRequestBody, + ): Observable { + val filePart: MultipartBody.Part + return try { + filePart = + MultipartBody.Part.createFormData( + "chunk", + URLEncoder.encode(filename, "utf-8"), + countingRequestBody, + ) + uploadInterface + .uploadFileToStash( + toRequestBody(filename), + toRequestBody(fileSize.toString()), + toRequestBody(offset.toString()), + toRequestBody(fileKey), + toRequestBody(csrfTokenClient.getTokenBlocking()), + filePart, + ).map(UploadResponse::upload) + } catch (throwable: Throwable) { + Timber.e(throwable, "Failed to upload chunk to stash") + Observable.error(throwable) + } } - } - /** - * Converts string value to request body - */ - private fun toRequestBody(value: String?): RequestBody? { - return value?.toRequestBody(MultipartBody.FORM) - } + /** + * Converts string value to request body + */ + private fun toRequestBody(value: String?): RequestBody? = value?.toRequestBody(MultipartBody.FORM) - fun uploadFileFromStash( - contribution: Contribution?, - uniqueFileName: String?, - fileKey: String? - ): Observable { - return try { - uploadInterface.uploadFileFromStash( - csrfTokenClient.getTokenBlocking(), - pageContentsCreator.createFrom(contribution), - CommonsApplication.DEFAULT_EDIT_SUMMARY, - uniqueFileName!!, - fileKey!! - ).map { uploadResponse: JsonObject? -> - val uploadResult = gson.fromJson(uploadResponse, UploadResponse::class.java) - if (uploadResult.upload == null) { - val exception = gson.fromJson(uploadResponse, MwException::class.java) - Timber.e(exception, "Error in uploading file from stash") - throw Exception(exception.getErrorCode()) + fun uploadFileFromStash( + contribution: Contribution?, + uniqueFileName: String?, + fileKey: String?, + ): Observable = + try { + uploadInterface + .uploadFileFromStash( + csrfTokenClient.getTokenBlocking(), + pageContentsCreator.createFrom(contribution), + CommonsApplication.DEFAULT_EDIT_SUMMARY, + uniqueFileName!!, + fileKey!!, + ).map { uploadResponse: JsonObject? -> + val uploadResult = gson.fromJson(uploadResponse, UploadResponse::class.java) + if (uploadResult.upload == null) { + val exception = gson.fromJson(uploadResponse, MwException::class.java) + Timber.e(exception, "Error in uploading file from stash") + throw Exception(exception.getErrorCode()) + } + uploadResult.upload } - uploadResult.upload - } - } catch (throwable: Throwable) { - Timber.e(throwable, "Exception occurred in uploading file from stash") - Observable.error(throwable) - } - } + } catch (throwable: Throwable) { + Timber.e(throwable, "Exception occurred in uploading file from stash") + Observable.error(throwable) + } - fun interface TimeProvider { - fun currentTimeMillis(): Long + fun interface TimeProvider { + fun currentTimeMillis(): Long + } } -} private fun canProcess( contributionDao: ContributionDao, contribution: Contribution, - failures: AtomicBoolean + failures: AtomicBoolean, ): Boolean { // As long as the contribution hasn't been paused and there are no errors, // we can process the current chunk. - return !(contributionDao.getContribution(contribution.pageId).state == Contribution.STATE_PAUSED - || failures.get() || CommonsApplication.isPaused) + return !( + contributionDao.getContribution(contribution.pageId).state == Contribution.STATE_PAUSED || + failures.get() || + CommonsApplication.isPaused + ) } private fun shouldSkip( chunkInfo: AtomicReference, - index: AtomicInteger -): Boolean { - return chunkInfo.get() != null && index.get() < chunkInfo.get()!!.indexOfNextChunkToUpload -} + index: AtomicInteger, +): Boolean = chunkInfo.get() != null && index.get() < chunkInfo.get()!!.indexOfNextChunkToUpload diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadInterface.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadInterface.kt index a9386fc6aa..c756f89dab 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadInterface.kt @@ -21,7 +21,7 @@ interface UploadInterface { @Part("offset") offset: RequestBody?, @Part("filekey") fileKey: RequestBody?, @Part("token") token: RequestBody?, - @Part filePart: MultipartBodyPart + @Part filePart: MultipartBodyPart, ): Observable @Headers("Cache-Control: no-cache") @@ -32,6 +32,6 @@ interface UploadInterface { @Field("text") text: String, @Field("comment") comment: String, @Field("filename") filename: String, - @Field("filekey") filekey: String + @Field("filekey") filekey: String, ): Observable } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.kt index dd66a01abe..3756cee5b5 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadMediaDetail.kt @@ -10,37 +10,40 @@ import kotlinx.parcelize.Parcelize @Parcelize data class UploadMediaDetail constructor( /** - * @return The language code ie. "en" or "fr" + * The language code ie. "en" or "fr". + * @param languageCode The language code ie. "en" or "fr". */ + var languageCode: String? = null, /** - * @param languageCode The language code ie. "en" or "fr" + * The description text for the item being uploaded. + * @param descriptionText The description text. */ - var languageCode: String? = null, var descriptionText: String = "", - var captionText: String = "" + /** + * The caption text for the item being uploaded. + * @param captionText The caption text. + */ + var captionText: String = "", ) : Parcelable { fun javaCopy() = copy() constructor(place: Place) : this( place.language, place.longDescription, - place.name + place.name, ) + /** - * @return the index of the language selected in a spinner with [SpinnerLanguagesAdapter] - */ - /** - * @param selectedLanguageIndex the index of the language selected in a spinner with [SpinnerLanguagesAdapter] + * The index of the language selected in a spinner with [SpinnerLanguagesAdapter]. + * @return The index of the selected language. + * @param selectedLanguageIndex The index of the language selected. */ var selectedLanguageIndex: Int = -1 + /** - * returns if the description was added manually (by the user, or we have added it programaticallly) - * @return - */ - /** - * sets to true if the description was manually added by the user - * @param manuallyAdded + * Returns if the description was added manually (by the user, or programmatically). + * @return True if the description was manually added. + * @param manuallyAdded Sets to true if the description was manually added. */ var isManuallyAdded: Boolean = false - } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadProgressActivity.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadProgressActivity.kt index 82483fa3cf..aeaefa3022 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadProgressActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadProgressActivity.kt @@ -1,6 +1,5 @@ package fr.free.nrw.commons.upload -import android.annotation.SuppressLint import android.os.Bundle import android.view.Menu import android.view.MenuItem @@ -8,13 +7,9 @@ import androidx.fragment.app.Fragment import androidx.viewpager.widget.ViewPager import fr.free.nrw.commons.R import fr.free.nrw.commons.ViewPagerAdapter -import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.ContributionDao import fr.free.nrw.commons.databinding.ActivityUploadProgressBinding import fr.free.nrw.commons.theme.BaseActivity -import io.reactivex.functions.Consumer -import io.reactivex.schedulers.Schedulers -import timber.log.Timber import javax.inject.Inject /** @@ -24,7 +19,6 @@ import javax.inject.Inject * failed uploads respectively. */ class UploadProgressActivity : BaseActivity() { - private lateinit var binding: ActivityUploadProgressBinding private var pendingUploadsFragment: PendingUploadsFragment? = null private var failedUploadsFragment: FailedUploadsFragment? = null @@ -52,26 +46,29 @@ class UploadProgressActivity : BaseActivity() { setSupportActionBar(binding.toolbarBinding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) - binding.uploadProgressViewPager.addOnPageChangeListener(object : - ViewPager.OnPageChangeListener { - override fun onPageScrolled( - position: Int, positionOffset: Float, - positionOffsetPixels: Int - ) { - } + binding.uploadProgressViewPager.addOnPageChangeListener( + object : + ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, + positionOffset: Float, + positionOffsetPixels: Int, + ) { + } - override fun onPageSelected(position: Int) { - updateMenuItems(position) - if (position == 2) { - binding.uploadProgressViewPager.setCanScroll(false) - } else { - binding.uploadProgressViewPager.setCanScroll(true) + override fun onPageSelected(position: Int) { + updateMenuItems(position) + if (position == 2) { + binding.uploadProgressViewPager.setCanScroll(false) + } else { + binding.uploadProgressViewPager.setCanScroll(true) + } } - } - override fun onPageScrollStateChanged(state: Int) { - } - }) + override fun onPageScrollStateChanged(state: Int) { + } + }, + ) setTabs() } @@ -119,75 +116,72 @@ class UploadProgressActivity : BaseActivity() { if (isPendingIconsVisible) { if (!isPaused) { if (menu!!.findItem(R.id.pause_icon) == null) { - menu!!.add( - Menu.NONE, - R.id.pause_icon, - Menu.NONE, - getString(R.string.pause) - ) - .setIcon(R.drawable.pause_icon) + menu!! + .add( + Menu.NONE, + R.id.pause_icon, + Menu.NONE, + getString(R.string.pause), + ).setIcon(R.drawable.pause_icon) .setOnMenuItemClickListener { pendingUploadsFragment!!.pauseUploads() setPausedIcon(true) true - } - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + }.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) } if (menu!!.findItem(R.id.cancel_icon) == null) { - menu!!.add( - Menu.NONE, - R.id.cancel_icon, - Menu.NONE, - getString(R.string.cancel) - ) - .setIcon(R.drawable.ic_cancel_upload) + menu!! + .add( + Menu.NONE, + R.id.cancel_icon, + Menu.NONE, + getString(R.string.cancel), + ).setIcon(R.drawable.ic_cancel_upload) .setOnMenuItemClickListener { pendingUploadsFragment!!.deleteUploads() true - } - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + }.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) } } else { if (menu!!.findItem(R.id.resume_icon) == null) { - menu!!.add( - Menu.NONE, - R.id.resume_icon, - Menu.NONE, - getString(R.string.resume) - ) - .setIcon(R.drawable.play_icon) + menu!! + .add( + Menu.NONE, + R.id.resume_icon, + Menu.NONE, + getString(R.string.resume), + ).setIcon(R.drawable.play_icon) .setOnMenuItemClickListener { pendingUploadsFragment!!.restartUploads() setPausedIcon(false) true - } - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + }.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) } } } } else if (currentPosition == 1) { if (isErrorIconsVisisble) { if (menu!!.findItem(R.id.retry_icon) == null) { - menu!!.add(Menu.NONE, R.id.retry_icon, Menu.NONE, getString(R.string.retry)) - .setIcon(R.drawable.ic_refresh_24dp).setOnMenuItemClickListener { + menu!! + .add(Menu.NONE, R.id.retry_icon, Menu.NONE, getString(R.string.retry)) + .setIcon(R.drawable.ic_refresh_24dp) + .setOnMenuItemClickListener { failedUploadsFragment!!.restartUploads() true - } - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + }.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) } if (menu!!.findItem(R.id.cancel_icon) == null) { - menu!!.add( - Menu.NONE, - R.id.cancel_icon, - Menu.NONE, - getString(R.string.cancel) - ) - .setIcon(R.drawable.ic_cancel_upload) + menu!! + .add( + Menu.NONE, + R.id.cancel_icon, + Menu.NONE, + getString(R.string.cancel), + ).setIcon(R.drawable.ic_cancel_upload) .setOnMenuItemClickListener { failedUploadsFragment!!.deleteUploads() true - } - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + }.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) } } } @@ -219,5 +213,4 @@ class UploadProgressActivity : BaseActivity() { isErrorIconsVisisble = visible updateMenuItems(binding.uploadProgressViewPager.currentItem) } - } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadResponse.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadResponse.kt index ff9dcc5b70..f1c5306ba0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadResponse.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadResponse.kt @@ -1,3 +1,5 @@ package fr.free.nrw.commons.upload -class UploadResponse(val upload: UploadResult?) \ No newline at end of file +class UploadResponse( + val upload: UploadResult?, +) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadResult.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadResult.kt index 3f6d6d8180..dfd8cc2eae 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadResult.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadResult.kt @@ -5,42 +5,39 @@ import android.os.Parcelable private const val RESULT_SUCCESS = "Success" - data class UploadResult( val result: String, val filekey: String, val offset: Int, - val filename: String + val filename: String, ) : Parcelable { constructor(parcel: Parcel) : this( - parcel.readString()?:"", - parcel.readString()?:"", - parcel.readInt()?:0, - parcel.readString()?:"" + parcel.readString() ?: "", + parcel.readString() ?: "", + parcel.readInt() ?: 0, + parcel.readString() ?: "", ) { } fun isSuccessful(): Boolean = result == RESULT_SUCCESS fun createCanonicalFileName() = "File:$filename" - override fun writeToParcel(parcel: Parcel, flags: Int) { + + override fun writeToParcel( + parcel: Parcel, + flags: Int, + ) { parcel.writeString(result) parcel.writeString(filekey) parcel.writeInt(offset) parcel.writeString(filename) } - override fun describeContents(): Int { - return 0 - } + override fun describeContents(): Int = 0 companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): UploadResult { - return UploadResult(parcel) - } + override fun createFromParcel(parcel: Parcel): UploadResult = UploadResult(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/fr/free/nrw/commons/upload/WikiBaseInterface.kt b/app/src/main/java/fr/free/nrw/commons/upload/WikiBaseInterface.kt index 1bc5f84fcc..42484306d2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/WikiBaseInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/WikiBaseInterface.kt @@ -22,7 +22,7 @@ interface WikiBaseInterface { fun postEditEntity( @Field("id") fileEntityId: String, @Field("token") editToken: String, - @Field("data") data: String + @Field("data") data: String, ): Observable /** @@ -32,18 +32,20 @@ interface WikiBaseInterface { * @param editToken editToken for the file * @param data data of the depicts to be uploaded * @return Observable - */ + */ @Headers("Cache-Control: no-cache") @FormUrlEncoded @POST(WikidataConstants.MW_API_PREFIX + "action=wbeditentity&site=commonswiki") fun postEditEntityByFilename( @Field("title") filename: String, @Field("token") editToken: String, - @Field("data") data: String + @Field("data") data: String, ): Observable @GET(WikidataConstants.MW_API_PREFIX + "action=query&prop=info") - fun getFileEntityId(@Query("titles") fileName: String?): Observable + fun getFileEntityId( + @Query("titles") fileName: String?, + ): Observable /** * Upload Captions for the image when upload is successful @@ -58,14 +60,14 @@ interface WikiBaseInterface { @Field("id") fileEntityId: String?, @Field("token") editToken: String?, @Field("language") language: String?, - @Field("value") captionValue: String? + @Field("value") captionValue: String?, ): Observable @GET(WikidataConstants.MW_API_PREFIX + "action=wbgetclaims") fun getClaimsByProperty( @Query("entity") entityId: String, - @Query("property") property: String - ) : Observable + @Query("property") property: String, + ): Observable @Headers("Cache-Control: no-cache") @FormUrlEncoded @@ -73,6 +75,6 @@ interface WikiBaseInterface { fun postDeleteClaims( @Field("token") editToken: String, @Field("id") entityId: String, - @Field("data") data: String + @Field("data") data: String, ): Observable } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/WikidataItem.kt b/app/src/main/java/fr/free/nrw/commons/upload/WikidataItem.kt index 5c8a639dfc..66320efb0e 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/WikidataItem.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/WikidataItem.kt @@ -1,6 +1,6 @@ package fr.free.nrw.commons.upload interface WikidataItem { -val id:String -val name:String + val id: String + val name: String } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/WikidataPlace.kt b/app/src/main/java/fr/free/nrw/commons/upload/WikidataPlace.kt index f0b6ec3005..5019dd4e72 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/WikidataPlace.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/WikidataPlace.kt @@ -12,26 +12,22 @@ data class WikidataPlace( val imageValue: String?, val wikipediaArticle: String?, val location: LatLng? = null, - var isMonumentUpload : Boolean =false -) : - WikidataItem, Parcelable { + var isMonumentUpload: Boolean = false, +) : WikidataItem, + Parcelable { constructor(place: Place) : this( place.wikiDataEntityId!!, place.name, place.pic.takeIf { it.isNotBlank() }, place.siteLinks.wikipediaLink?.toString() ?: "", place.location, - isMonumentUpload=place.isMonument + isMonumentUpload = place.isMonument, ) companion object { @JvmStatic - fun from(place: Place?): WikidataPlace? { - return place?.let { WikidataPlace(it) } - } + fun from(place: Place?): WikidataPlace? = place?.let { WikidataPlace(it) } } - fun getWikipediaPageTitle(): String? { - return wikipediaArticle?.substringAfterLast("/") - } + fun getWikipediaPageTitle(): String? = wikipediaArticle?.substringAfterLast("/") } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt index 6937096906..f1e4917a0d 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt @@ -4,22 +4,24 @@ import androidx.recyclerview.widget.DiffUtil import com.hannesdorfmann.adapterdelegates4.AdapterDelegate import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter - abstract class BaseDelegateAdapter( vararg delegates: AdapterDelegate>, areItemsTheSame: (T, T) -> Boolean, - areContentsTheSame: (T, T) -> Boolean = { old, new -> old == new } + areContentsTheSame: (T, T) -> Boolean = { old, new -> old == new }, ) : AsyncListDifferDelegationAdapter( - object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: T, newItem: T) = - areItemsTheSame(oldItem, newItem) - - override fun areContentsTheSame(oldItem: T, newItem: T) = - areContentsTheSame(oldItem, newItem) - }, - *delegates -) { - + object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: T, + newItem: T, + ) = areItemsTheSame(oldItem, newItem) + + override fun areContentsTheSame( + oldItem: T, + newItem: T, + ) = areContentsTheSame(oldItem, newItem) + }, + *delegates, + ) { fun addAll(newResults: List) { items = itemsOrEmpty + newResults } @@ -38,4 +40,3 @@ abstract class BaseDelegateAdapter( private val itemsOrEmpty get() = items ?: emptyList() } - diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.kt b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.kt index 49aa41c9eb..1822df8302 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.kt @@ -26,271 +26,289 @@ import javax.inject.Singleton * The presenter class for UploadCategoriesFragment */ @Singleton -class CategoriesPresenter @Inject constructor( - private val repository: UploadRepository, - @param:Named(CommonsApplicationModule.IO_THREAD) private val ioScheduler: Scheduler, - @param:Named(CommonsApplicationModule.MAIN_THREAD) private val mainThreadScheduler: Scheduler -) : CategoriesContract.UserActionListener { +class CategoriesPresenter + @Inject + constructor( + private val repository: UploadRepository, + @param:Named(CommonsApplicationModule.IO_THREAD) private val ioScheduler: Scheduler, + @param:Named(CommonsApplicationModule.MAIN_THREAD) private val mainThreadScheduler: Scheduler, + ) : CategoriesContract.UserActionListener { + companion object { + private val DUMMY: CategoriesContract.View = proxy() + } - companion object { - private val DUMMY: CategoriesContract.View = proxy() - } + var view = DUMMY + private val compositeDisposable = CompositeDisposable() + private val searchTerms = PublishSubject.create() + private var categoryList: MutableLiveData> = MutableLiveData() - var view = DUMMY - private val compositeDisposable = CompositeDisposable() - private val searchTerms = PublishSubject.create() - private var categoryList: MutableLiveData> = MutableLiveData() - /** - * Current media - */ - private var media: Media? = null + /** + * Current media + */ + private var media: Media? = null - /** - * helper class for editing categories - */ - @Inject - lateinit var categoryEditHelper: CategoryEditHelper + /** + * helper class for editing categories + */ + @Inject + lateinit var categoryEditHelper: CategoryEditHelper - override fun onAttachView(view: CategoriesContract.View) { - this.view = view - compositeDisposable.add( - searchTerms - .observeOn(mainThreadScheduler) - .doOnNext { - view.showProgress(true) - } - .switchMap(::searchResults) - .map { repository.selectedCategories + it } - .map { it.distinctBy { categoryItem -> categoryItem.name } } - .observeOn(mainThreadScheduler) - .subscribe( - { - setCategoryListValue(it) - view.showProgress(false) - if (it.isEmpty()) { + override fun onAttachView(view: CategoriesContract.View) { + this.view = view + compositeDisposable.add( + searchTerms + .observeOn(mainThreadScheduler) + .doOnNext { + view.showProgress(true) + }.switchMap(::searchResults) + .map { repository.selectedCategories + it } + .map { it.distinctBy { categoryItem -> categoryItem.name } } + .observeOn(mainThreadScheduler) + .subscribe( + { + setCategoryListValue(it) + view.showProgress(false) + if (it.isEmpty()) { + view.showError(R.string.no_categories_found) + } + }, + { t: Throwable? -> + view.showProgress(false) view.showError(R.string.no_categories_found) - } - }, - { t: Throwable? -> - view.showProgress(false) - view.showError(R.string.no_categories_found) - Timber.e(t) - } - ) - ) - } + Timber.e(t) + }, + ), + ) + } - /** - * If media is null : Fetches categories from server according to the term - * Else : Fetches existing categories by their name, fetches categories from server according - * to the term and combines both in a list - */ - private fun searchResults(term: String): Observable>? { - if (media == null) { - return repository.searchAll(term, getImageTitleList(), repository.selectedDepictions) - .subscribeOn(ioScheduler) - .map { it.filter { categoryItem -> !repository.isSpammyCategory(categoryItem.name) - || categoryItem.name==term } } - } else { - return Observable.zip( - repository.getCategories(repository.selectedExistingCategories) - .map { list -> list.map { - CategoryItem(it.name, it.description, it.thumbnail, true) + /** + * If media is null : Fetches categories from server according to the term + * Else : Fetches existing categories by their name, fetches categories from server according + * to the term and combines both in a list + */ + private fun searchResults(term: String): Observable>? { + if (media == null) { + return repository + .searchAll(term, getImageTitleList(), repository.selectedDepictions) + .subscribeOn(ioScheduler) + .map { + it.filter { categoryItem -> + !repository.isSpammyCategory(categoryItem.name) || + categoryItem.name == term + } } - }, - repository.searchAll(term, getImageTitleList(), repository.selectedDepictions) - ) { it1, it2 -> - it1 + it2 + } else { + return Observable + .zip( + repository + .getCategories(repository.selectedExistingCategories) + .map { list -> + list.map { + CategoryItem(it.name, it.description, it.thumbnail, true) + } + }, + repository.searchAll(term, getImageTitleList(), repository.selectedDepictions), + ) { it1, it2 -> + it1 + it2 + }.subscribeOn(ioScheduler) + .map { + it.filter { categoryItem -> + !repository.isSpammyCategory(categoryItem.name) || + categoryItem.name == term + } + }.map { it.filterNot { categoryItem -> categoryItem.thumbnail == "hidden" } } } - .subscribeOn(ioScheduler) - .map { it.filter { categoryItem -> !repository.isSpammyCategory(categoryItem.name) - || categoryItem.name==term } } - .map { it.filterNot { categoryItem -> categoryItem.thumbnail == "hidden" } } } - } - override fun onDetachView() { - view = DUMMY - compositeDisposable.clear() - } + override fun onDetachView() { + view = DUMMY + compositeDisposable.clear() + } - /** - * asks the repository to fetch categories for the query - * @param query - */ - override fun searchForCategories(query: String) { - searchTerms.onNext(query) - } + /** + * asks the repository to fetch categories for the query + * @param query + */ + override fun searchForCategories(query: String) { + searchTerms.onNext(query) + } - /** - * Returns image title list from UploadItem - * @return - */ - private fun getImageTitleList(): List { - return repository.uploads - .map { it.uploadMediaDetails[0].captionText } - .filterNot { TextUtils.isEmpty(it) } - } + /** + * Returns image title list from UploadItem + * @return + */ + private fun getImageTitleList(): List = + repository.uploads + .map { it.uploadMediaDetails[0].captionText } + .filterNot { TextUtils.isEmpty(it) } - /** - * Verifies the number of categories selected, prompts the user if none selected - */ - override fun verifyCategories() { - val selectedCategories = repository.selectedCategories - if (selectedCategories.isNotEmpty()) { - repository.setSelectedCategories(selectedCategories.map { it.name }) - view.goToNextScreen() - } else { - view.showNoCategorySelected() + /** + * Verifies the number of categories selected, prompts the user if none selected + */ + override fun verifyCategories() { + val selectedCategories = repository.selectedCategories + if (selectedCategories.isNotEmpty()) { + repository.setSelectedCategories(selectedCategories.map { it.name }) + view.goToNextScreen() + } else { + view.showNoCategorySelected() + } } - } - /** - * ask repository to handle category clicked - * - * @param categoryItem - */ - override fun onCategoryItemClicked(categoryItem: CategoryItem) { - repository.onCategoryClicked(categoryItem, media) - } + /** + * ask repository to handle category clicked + * + * @param categoryItem + */ + override fun onCategoryItemClicked(categoryItem: CategoryItem) { + repository.onCategoryClicked(categoryItem, media) + } - /** - * Attaches view and media - */ - override fun onAttachViewWithMedia(view: CategoriesContract.View, media: Media) { - this.view = view - this.media = media - repository.selectedExistingCategories = view.existingCategories - compositeDisposable.add( - searchTerms - .observeOn(mainThreadScheduler) - .doOnNext { - view.showProgress(true) - } - .switchMap(::searchResults) - .map { repository.selectedCategories + it } - .map { it.distinctBy { categoryItem -> categoryItem.name } } - .observeOn(mainThreadScheduler) - .subscribe( - { - setCategoryListValue(it) - view.showProgress(false) - if (it.isEmpty()) { + /** + * Attaches view and media + */ + override fun onAttachViewWithMedia( + view: CategoriesContract.View, + media: Media, + ) { + this.view = view + this.media = media + repository.selectedExistingCategories = view.existingCategories + compositeDisposable.add( + searchTerms + .observeOn(mainThreadScheduler) + .doOnNext { + view.showProgress(true) + }.switchMap(::searchResults) + .map { repository.selectedCategories + it } + .map { it.distinctBy { categoryItem -> categoryItem.name } } + .observeOn(mainThreadScheduler) + .subscribe( + { + setCategoryListValue(it) + view.showProgress(false) + if (it.isEmpty()) { + view.showError(R.string.no_categories_found) + } + }, + { t: Throwable? -> + view.showProgress(false) view.showError(R.string.no_categories_found) - } - }, - { t: Throwable? -> - view.showProgress(false) - view.showError(R.string.no_categories_found) - Timber.e(t) - } - ) - ) - } + Timber.e(t) + }, + ), + ) + } - /** - * Clears previous selections - */ - override fun clearPreviousSelection() { - repository.cleanup() - } + /** + * Clears previous selections + */ + override fun clearPreviousSelection() { + repository.cleanup() + } - /** - * Gets the selected categories and send them for posting to the server - * - * @param media media - * @param wikiText current WikiText from server - */ - override fun updateCategories(media: Media, wikiText: String) { - //check if view.existingCategories is null - if (repository.selectedCategories.isNotEmpty() - || (view.existingCategories != null && repository.selectedExistingCategories.size != view.existingCategories.size) + /** + * Gets the selected categories and send them for posting to the server + * + * @param media media + * @param wikiText current WikiText from server + */ + override fun updateCategories( + media: Media, + wikiText: String, ) { - val selectedCategories: MutableList = - (repository.selectedCategories.map { it.name }.toMutableList() - + repository.selectedExistingCategories).toMutableList() + // check if view.existingCategories is null + if (repository.selectedCategories.isNotEmpty() || + (view.existingCategories != null && repository.selectedExistingCategories.size != view.existingCategories.size) + ) { + val selectedCategories: MutableList = + ( + repository.selectedCategories.map { it.name }.toMutableList() + + repository.selectedExistingCategories + ).toMutableList() - if (selectedCategories.isNotEmpty()) { - view.showProgressDialog() + if (selectedCategories.isNotEmpty()) { + view.showProgressDialog() - try { - compositeDisposable.add( - categoryEditHelper.makeCategoryEdit( - view.fragmentContext, media, - selectedCategories, wikiText + try { + compositeDisposable.add( + categoryEditHelper + .makeCategoryEdit( + view.fragmentContext, + media, + selectedCategories, + wikiText, + ).subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + Timber.d("Categories are added.") + media.addedCategories = selectedCategories + repository.cleanup() + view.dismissProgressDialog() + view.refreshCategories() + view.goBackToPreviousScreen() + }, { + Timber.e( + "Failed to update categories", + ) + }), ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - Timber.d("Categories are added.") - media.addedCategories = selectedCategories - repository.cleanup() - view.dismissProgressDialog() - view.refreshCategories() - view.goBackToPreviousScreen() - }, { - Timber.e( - "Failed to update categories" - ) - }) - ) - } catch (e: InvalidLoginTokenException) { - view.navigateToLoginScreen(); + } catch (e: InvalidLoginTokenException) { + view.navigateToLoginScreen() + } } - + } else { + repository.cleanup() + view.showNoCategorySelected() } - } else { - repository.cleanup() - view.showNoCategorySelected() } - } - /** - * Selects each [CategoryItem] in a given list as if they were clicked by the user by calling - * [onCategoryItemClicked] for each category and adding the category to [categoryList] - */ - private fun selectNewCategories(toSelect: List) { - toSelect.forEach{ + /** + * Selects each [CategoryItem] in a given list as if they were clicked by the user by calling + * [onCategoryItemClicked] for each category and adding the category to [categoryList] + */ + private fun selectNewCategories(toSelect: List) { + toSelect.forEach { it.isSelected = true repository.onCategoryClicked(it, media) } - // Add the new selections to the list of category items so that the selections appear - // immediately (i.e. without any search term queries) - categoryList.value?.toMutableList() - ?.let { toSelect + it } - ?.distinctBy(CategoryItem::name) - ?.let { setCategoryListValue(it) } - } + // Add the new selections to the list of category items so that the selections appear + // immediately (i.e. without any search term queries) + categoryList.value + ?.toMutableList() + ?.let { toSelect + it } + ?.distinctBy(CategoryItem::name) + ?.let { setCategoryListValue(it) } + } - /** - * Livedata being used to observe category list inside - * @see UploadCategoriesFragment - * Any changes to category list reflect immediately to the adapter list - */ - override fun getCategories(): LiveData> { - return categoryList - } + /** + * Livedata being used to observe category list inside + * @see UploadCategoriesFragment + * Any changes to category list reflect immediately to the adapter list + */ + override fun getCategories(): LiveData> = categoryList - /** - * needed for tests - */ - fun setCategoryList(categoryList: MutableLiveData>) { - this.categoryList = categoryList - } + /** + * needed for tests + */ + fun setCategoryList(categoryList: MutableLiveData>) { + this.categoryList = categoryList + } - /** - * needed for tests - */ - fun setCategoryListValue(categoryItems: List) { - categoryList.postValue(categoryItems) - } + /** + * needed for tests + */ + fun setCategoryListValue(categoryItems: List) { + categoryList.postValue(categoryItems) + } - override fun selectCategories() { - compositeDisposable.add(repository.placeCategories - .subscribeOn(ioScheduler) - .observeOn(mainThreadScheduler) - .subscribe(::selectNewCategories) - ) + override fun selectCategories() { + compositeDisposable.add( + repository.placeCategories + .subscribeOn(ioScheduler) + .observeOn(mainThreadScheduler) + .subscribe(::selectNewCategories), + ) + } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoryAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoryAdapter.kt index 2e301c66f0..145f11d065 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoryAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoryAdapter.kt @@ -4,13 +4,12 @@ import fr.free.nrw.commons.category.CategoryItem import org.jetbrains.annotations.NotNull class UploadCategoryAdapter( - onCategoryClicked: @NotNull() (CategoryItem) -> Unit, nearbyPlaceCategory: String?) : - BaseDelegateAdapter( + onCategoryClicked: @NotNull (CategoryItem) -> Unit, + nearbyPlaceCategory: String?, +) : BaseDelegateAdapter( uploadCategoryDelegate(onCategoryClicked, nearbyPlaceCategory), areItemsTheSame = { oldItem, newItem -> oldItem.name == newItem.name }, areContentsTheSame = { oldItem, newItem -> oldItem.name == newItem.name && oldItem.isSelected == newItem.isSelected - } + }, ) - - diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoryAdapterDelegates.kt b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoryAdapterDelegates.kt index fc5d143a44..619db9ab7a 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoryAdapterDelegates.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoryAdapterDelegates.kt @@ -6,42 +6,47 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.category.CategoryItem import fr.free.nrw.commons.databinding.LayoutUploadCategoriesItemBinding -fun uploadCategoryDelegate(onCategoryClicked: (CategoryItem) -> Unit, nearbyPlaceCategory: String?) = - adapterDelegateViewBinding({ layoutInflater, root -> - LayoutUploadCategoriesItemBinding.inflate(layoutInflater, root, false) - }) { - val onClickListener = { _: View? -> - if (item.name != nearbyPlaceCategory) { - item.isSelected = !item.isSelected - binding.uploadCategoryCheckbox.isChecked = item.isSelected - onCategoryClicked(item) - } +fun uploadCategoryDelegate( + onCategoryClicked: (CategoryItem) -> Unit, + nearbyPlaceCategory: String?, +) = adapterDelegateViewBinding< + CategoryItem, + CategoryItem, + LayoutUploadCategoriesItemBinding, +>({ layoutInflater, root -> + LayoutUploadCategoriesItemBinding.inflate(layoutInflater, root, false) +}) { + val onClickListener = { _: View? -> + if (item.name != nearbyPlaceCategory) { + item.isSelected = !item.isSelected + binding.uploadCategoryCheckbox.isChecked = item.isSelected + onCategoryClicked(item) } + } - binding.root.setOnClickListener(onClickListener) - binding.uploadCategoryCheckbox.setOnClickListener(onClickListener) + binding.root.setOnClickListener(onClickListener) + binding.uploadCategoryCheckbox.setOnClickListener(onClickListener) - bind { - if (item.name == nearbyPlaceCategory) { - item.isSelected = true - binding.uploadCategoryCheckbox.isChecked = true - binding.uploadCategoryCheckbox.isEnabled = false - } else { - binding.uploadCategoryCheckbox.isEnabled = true - binding.uploadCategoryCheckbox.isChecked = item.isSelected - } - binding.categoryLabel.text = item.name - if (item.thumbnail != "null") { - binding.categoryImage.setImageURI(item.thumbnail) - } else { - binding.categoryImage.setActualImageResource(R.drawable.commons) - } + bind { + if (item.name == nearbyPlaceCategory) { + item.isSelected = true + binding.uploadCategoryCheckbox.isChecked = true + binding.uploadCategoryCheckbox.isEnabled = false + } else { + binding.uploadCategoryCheckbox.isEnabled = true + binding.uploadCategoryCheckbox.isChecked = item.isSelected + } + binding.categoryLabel.text = item.name + if (item.thumbnail != "null") { + binding.categoryImage.setImageURI(item.thumbnail) + } else { + binding.categoryImage.setActualImageResource(R.drawable.commons) + } - if (item.description != "null") { - binding.categoryDescription.text = item.description - } else { - binding.categoryDescription.text = "" - } + if (item.description != "null") { + binding.categoryDescription.text = item.description + } else { + binding.categoryDescription.text = "" } } +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/Claims.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/Claims.kt index e0a3a8e8be..6aac02d043 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/Claims.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/Claims.kt @@ -1,9 +1,9 @@ package fr.free.nrw.commons.upload.depicts import com.google.gson.annotations.SerializedName -import fr.free.nrw.commons.wikidata.model.Statement_partial +import fr.free.nrw.commons.wikidata.model.StatementPartial data class Claims( @SerializedName(value = "claims") - val claims: Map> = emptyMap() -) \ No newline at end of file + val claims: Map> = emptyMap(), +) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictEditHelper.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictEditHelper.kt index c681e5b7d1..4d267b7bbb 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictEditHelper.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictEditHelper.kt @@ -13,109 +13,116 @@ import io.reactivex.Observable import timber.log.Timber import javax.inject.Inject -class DepictEditHelper @Inject constructor (notificationHelper: NotificationHelper, - wikidataEditService: WikidataEditService, - viewUtilWrapper: ViewUtilWrapper) { - - /** - * Class for making post operations - */ +class DepictEditHelper @Inject - lateinit var wikidataEditService: WikidataEditService + constructor( + notificationHelper: NotificationHelper, + wikidataEditService: WikidataEditService, + viewUtilWrapper: ViewUtilWrapper, + ) { + /** + * Class for making post operations + */ + @Inject + lateinit var wikidataEditService: WikidataEditService - /** - * Class for creating notification - */ - @Inject - lateinit var notificationHelper: NotificationHelper + /** + * Class for creating notification + */ + @Inject + lateinit var notificationHelper: NotificationHelper - /** - * Class for showing toast - */ - @Inject - lateinit var viewUtilWrapper: ViewUtilWrapper + /** + * Class for showing toast + */ + @Inject + lateinit var viewUtilWrapper: ViewUtilWrapper - /** - * Public interface to edit depictions - * - * @param context context - * @param media media - * @param depictions selected depictions to be added ex: ["Q12", "Q234"] - * @return Single - */ - fun makeDepictionEdit( - context: Context, - media: Media, - depictions: List - ): Observable { - viewUtilWrapper.showShortToast( - context, - context.getString(R.string.depictions_edit_helper_make_edit_toast) - ) - return addDepiction(media, depictions) - .flatMap { result: Boolean -> - Observable.just( - showDepictionEditNotification(context, media, result) - ) - } - } + /** + * Public interface to edit depictions + * + * @param context context + * @param media media + * @param depictions selected depictions to be added ex: ["Q12", "Q234"] + * @return Single + */ + fun makeDepictionEdit( + context: Context, + media: Media, + depictions: List, + ): Observable { + viewUtilWrapper.showShortToast( + context, + context.getString(R.string.depictions_edit_helper_make_edit_toast), + ) + return addDepiction(media, depictions) + .flatMap { result: Boolean -> + Observable.just( + showDepictionEditNotification(context, media, result), + ) + } + } - /** - * Appends new depictions - * - * @param media media - * @param depictions to be added - * @return Observable - */ - private fun addDepiction(media: Media, depictions: List): Observable { - Timber.d("thread is adding depiction %s", Thread.currentThread().name) - return wikidataEditService.updateDepictsProperty(media.pageId, depictions) - } + /** + * Appends new depictions + * + * @param media media + * @param depictions to be added + * @return Observable + */ + private fun addDepiction( + media: Media, + depictions: List, + ): Observable { + Timber.d("thread is adding depiction %s", Thread.currentThread().name) + return wikidataEditService.updateDepictsProperty(media.pageId, depictions) + } - /** - * Helps to create notification about condition of editing depictions - * - * @param context context - * @param media media - * @param result response of result - * @return Single - */ - private fun showDepictionEditNotification( - context: Context, - media: Media, - result: Boolean - ): Boolean { - val message: String - var title = context.getString(R.string.depictions_edit_helper_show_edit_title) - if (result) { - title += ": " + context.getString(R.string.category_edit_helper_show_edit_title_success) - val depictsInMessage = StringBuilder() - val depictIdList = media.depictionIds - for (depiction in depictIdList) { - depictsInMessage.append(depiction) - if (depiction == depictIdList[depictIdList.size - 1]) { - continue + /** + * Helps to create notification about condition of editing depictions + * + * @param context context + * @param media media + * @param result response of result + * @return Single + */ + private fun showDepictionEditNotification( + context: Context, + media: Media, + result: Boolean, + ): Boolean { + val message: String + var title = context.getString(R.string.depictions_edit_helper_show_edit_title) + if (result) { + title += ": " + context.getString(R.string.category_edit_helper_show_edit_title_success) + val depictsInMessage = StringBuilder() + val depictIdList = media.depictionIds + for (depiction in depictIdList) { + depictsInMessage.append(depiction) + if (depiction == depictIdList[depictIdList.size - 1]) { + continue + } + depictsInMessage.append(",") } - depictsInMessage.append(",") + message = + context.resources.getQuantityString( + R.plurals.depictions_edit_helper_show_edit_message_if, + depictIdList.size, + depictsInMessage.toString(), + ) + } else { + title += ": " + context.getString(R.string.depictions_edit_helper_show_edit_title) + message = context.getString(R.string.depictions_edit_helper_edit_message_else) } - message = context.resources.getQuantityString( - R.plurals.depictions_edit_helper_show_edit_message_if, - depictIdList.size, - depictsInMessage.toString() + val urlForFile = BuildConfig.COMMONS_URL + "/wiki/" + media.filename + val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(urlForFile)) + notificationHelper.showNotification( + context, + title, + message, + NotificationHelper.NOTIFICATION_EDIT_DEPICTIONS, + browserIntent, ) - } else { - title += ": " + context.getString(R.string.depictions_edit_helper_show_edit_title) - message = context.getString(R.string.depictions_edit_helper_edit_message_else) + return result } - val urlForFile = BuildConfig.COMMONS_URL + "/wiki/" + media.filename - val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(urlForFile)) - notificationHelper.showNotification( - context, - title, - message, - NotificationHelper.NOTIFICATION_EDIT_DEPICTIONS, - browserIntent - ) - return result } -} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/Depicts.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/Depicts.kt index e1fc36e25b..b14e57c276 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/Depicts.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/Depicts.kt @@ -3,10 +3,13 @@ package fr.free.nrw.commons.upload.depicts import androidx.room.Entity import androidx.room.PrimaryKey import fr.free.nrw.commons.upload.structure.depictions.DepictedItem -import java.util.* +import java.util.Date /** * entity class for DepictsRoomDateBase */ @Entity(tableName = "depicts_table") -data class Depicts (@PrimaryKey val item: DepictedItem, val lastUsed:Date) \ No newline at end of file +data class Depicts( + @PrimaryKey val item: DepictedItem, + val lastUsed: Date, +) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt index d792406016..6844003014 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt @@ -20,10 +20,10 @@ import java.util.Date abstract class DepictsDao { /** The maximum number of depicts allowed in the database. */ private val maxItemsAllowed = 10 - + @Insert(onConflict = OnConflictStrategy.REPLACE) abstract suspend fun insert(depictedItem: Depicts) - + @Query("Select * From depicts_table order by lastUsed DESC") abstract suspend fun getAllDepicts(): List @@ -38,18 +38,20 @@ abstract class DepictsDao { * * @return A list of Depicts objects. */ - fun depictsList(): Deferred> = CoroutineScope(Dispatchers.IO).async { - getAllDepicts() - } + fun depictsList(): Deferred> = + CoroutineScope(Dispatchers.IO).async { + getAllDepicts() + } /** * Inserts a Depicts object into the database. * * @param depictedItem The Depicts object to insert. */ - private fun insertDepict(depictedItem: Depicts) = CoroutineScope(Dispatchers.IO).launch { - insert(depictedItem) - } + private fun insertDepict(depictedItem: Depicts) = + CoroutineScope(Dispatchers.IO).launch { + insert(depictedItem) + } /** * Gets a list of Depicts objects that need to be deleted from the database. @@ -57,18 +59,20 @@ abstract class DepictsDao { * @param n The number of depicts to delete. * @return A list of Depicts objects to delete. */ - private suspend fun depictsForDeletion(n: Int): Deferred> = CoroutineScope(Dispatchers.IO).async { - getDepictsForDeletion(n) - } + private suspend fun depictsForDeletion(n: Int): Deferred> = + CoroutineScope(Dispatchers.IO).async { + getDepictsForDeletion(n) + } /** * Deletes a Depicts object from the database. * * @param depicts The Depicts object to delete. */ - private suspend fun deleteDepicts(depicts: Depicts) = CoroutineScope(Dispatchers.IO).launch { - delete(depicts) - } + private suspend fun deleteDepicts(depicts: Depicts) = + CoroutineScope(Dispatchers.IO).launch { + delete(depicts) + } /** * Saves a list of DepictedItems in the DepictsRoomDataBase. @@ -87,13 +91,12 @@ abstract class DepictsDao { } private suspend fun deleteOldDepictions(depictsListSize: Int) { - if(depictsListSize > maxItemsAllowed) { + if (depictsListSize > maxItemsAllowed) { val depictsForDeletion = depictsForDeletion(depictsListSize).await() - for(depicts in depictsForDeletion) { + for (depicts in depictsForDeletion) { deleteDepicts(depicts) } } } } - diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsInterface.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsInterface.kt index 27c4c6f56e..a71c908e79 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsInterface.kt @@ -24,9 +24,11 @@ interface DepictsInterface { @Query("limit") limit: String?, @Query("language") language: String?, @Query("uselang") uselang: String?, - @Query("continue") offset: String? + @Query("continue") offset: String?, ): Single @GET("/w/api.php?format=json&action=wbgetentities") - fun getEntities(@Query("ids") ids: String?): Single + fun getEntities( + @Query("ids") ids: String?, + ): Single } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsPresenter.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsPresenter.kt index d236292d18..3beedd9d5c 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsPresenter.kt @@ -3,9 +3,9 @@ package fr.free.nrw.commons.upload.depicts import android.annotation.SuppressLint import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import fr.free.nrw.commons.bookmarks.items.BookmarkItemsController import fr.free.nrw.commons.Media import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsController import fr.free.nrw.commons.di.CommonsApplicationModule import fr.free.nrw.commons.repository.UploadRepository import fr.free.nrw.commons.upload.structure.depictions.DepictedItem @@ -27,249 +27,264 @@ import javax.inject.Singleton * presenter for DepictsFragment */ @Singleton -class DepictsPresenter @Inject constructor( - private val repository: UploadRepository, - @param:Named(CommonsApplicationModule.IO_THREAD) private val ioScheduler: Scheduler, - @param:Named(CommonsApplicationModule.MAIN_THREAD) private val mainThreadScheduler: Scheduler -) : DepictsContract.UserActionListener { - - companion object { - private val DUMMY = proxy() - } - - private var view = DUMMY - private val compositeDisposable: CompositeDisposable = CompositeDisposable() - private val searchTerm: PublishProcessor = PublishProcessor.create() - private val depictedItems: MutableLiveData> = MutableLiveData() - private var media: Media? = null +class DepictsPresenter @Inject - lateinit var depictsDao: DepictsDao + constructor( + private val repository: UploadRepository, + @param:Named(CommonsApplicationModule.IO_THREAD) private val ioScheduler: Scheduler, + @param:Named(CommonsApplicationModule.MAIN_THREAD) private val mainThreadScheduler: Scheduler, + ) : DepictsContract.UserActionListener { + companion object { + private val DUMMY = proxy() + } - /** - * Helps to get all bookmarked items - */ - @Inject - lateinit var controller: BookmarkItemsController + private var view = DUMMY + private val compositeDisposable: CompositeDisposable = CompositeDisposable() + private val searchTerm: PublishProcessor = PublishProcessor.create() + private val depictedItems: MutableLiveData> = MutableLiveData() + private var media: Media? = null - @Inject - lateinit var depictsHelper: DepictEditHelper + @Inject + lateinit var depictsDao: DepictsDao - override fun onAttachView(view: DepictsContract.View) { - this.view = view - compositeDisposable.add( - searchTerm - .observeOn(mainThreadScheduler) - .doOnNext { view.showProgress(true) } - .switchMap(::searchResultsWithTerm) - .observeOn(mainThreadScheduler) - .subscribe( - { (results, term) -> - view.showProgress(false) - view.showError(results.isEmpty() && term.isNotEmpty()) - depictedItems.value = results - }, - { t: Throwable? -> - view.showProgress(false) - view.showError(true) - Timber.e(t) - } - ) - ) - } + /** + * Helps to get all bookmarked items + */ + @Inject + lateinit var controller: BookmarkItemsController - private fun searchResultsWithTerm(term: String): Flowable, String>> { - return searchResults(term).map { Pair(it, term) } - } + @Inject + lateinit var depictsHelper: DepictEditHelper - private fun searchResults(querystring: String): Flowable> { - var recentDepictedItemList: MutableList = ArrayList(); - //show recentDepictedItemList when queryString is empty - if (querystring.isEmpty()) { - recentDepictedItemList = getRecentDepictedItems().toMutableList() + override fun onAttachView(view: DepictsContract.View) { + this.view = view + compositeDisposable.add( + searchTerm + .observeOn(mainThreadScheduler) + .doOnNext { view.showProgress(true) } + .switchMap(::searchResultsWithTerm) + .observeOn(mainThreadScheduler) + .subscribe( + { (results, term) -> + view.showProgress(false) + view.showError(results.isEmpty() && term.isNotEmpty()) + depictedItems.value = results + }, + { t: Throwable? -> + view.showProgress(false) + view.showError(true) + Timber.e(t) + }, + ), + ) } - if (media == null) { - return repository.searchAllEntities(querystring) - .subscribeOn(ioScheduler) - .map { repository.selectedDepictions + it + recentDepictedItemList + controller.loadFavoritesItems() } - .map { it.filterNot { item -> WikidataDisambiguationItems.isDisambiguationItem(item.instanceOfs) } } - .map { it.distinctBy(DepictedItem::id) } + private fun searchResultsWithTerm(term: String): Flowable, String>> = + searchResults(term).map { Pair(it, term) } - } else { - return Flowable.zip(repository.getDepictions(repository.selectedExistingDepictions) - .map { list -> list.map { - DepictedItem(it.name, it.description, it.imageUrl, it.instanceOfs, - it.commonsCategories, true, it.id) - } - }, - repository.searchAllEntities(querystring) - ) { it1, it2 -> - it1 + it2 + private fun searchResults(querystring: String): Flowable> { + var recentDepictedItemList: MutableList = ArrayList() + // show recentDepictedItemList when queryString is empty + if (querystring.isEmpty()) { + recentDepictedItemList = getRecentDepictedItems().toMutableList() } - .subscribeOn(ioScheduler) - .map { repository.selectedDepictions + it + recentDepictedItemList + controller.loadFavoritesItems() } - .map { it.filterNot { item -> WikidataDisambiguationItems.isDisambiguationItem(item.instanceOfs) } } - .map { it.distinctBy(DepictedItem::id) } + if (media == null) { + return repository + .searchAllEntities(querystring) + .subscribeOn(ioScheduler) + .map { repository.selectedDepictions + it + recentDepictedItemList + controller.loadFavoritesItems() } + .map { it.filterNot { item -> WikidataDisambiguationItems.isDisambiguationItem(item.instanceOfs) } } + .map { it.distinctBy(DepictedItem::id) } + } else { + return Flowable + .zip( + repository + .getDepictions(repository.selectedExistingDepictions) + .map { list -> + list.map { + DepictedItem( + it.name, + it.description, + it.imageUrl, + it.instanceOfs, + it.commonsCategories, + true, + it.id, + ) + } + }, + repository.searchAllEntities(querystring), + ) { it1, it2 -> + it1 + it2 + }.subscribeOn(ioScheduler) + .map { repository.selectedDepictions + it + recentDepictedItemList + controller.loadFavoritesItems() } + .map { it.filterNot { item -> WikidataDisambiguationItems.isDisambiguationItem(item.instanceOfs) } } + .map { it.distinctBy(DepictedItem::id) } + } } - } - - override fun onDetachView() { - view = DUMMY - media = null - compositeDisposable.clear() - } - /** - * Selects the place depictions retrieved by the repository - */ - override fun selectPlaceDepictions() { - compositeDisposable.add(repository.placeDepictions - .subscribeOn(ioScheduler) - .observeOn(mainThreadScheduler) - .subscribe(::selectNewDepictions) - ) - } + override fun onDetachView() { + view = DUMMY + media = null + compositeDisposable.clear() + } - /** - * Selects each [DepictedItem] in a given list as if they were clicked by the user by calling - * [onDepictItemClicked] for each depiction and adding the depictions to [depictedItems] - */ - private fun selectNewDepictions(toSelect: List) { - toSelect.forEach { - it.isSelected = true - repository.onDepictItemClicked(it, media) + /** + * Selects the place depictions retrieved by the repository + */ + override fun selectPlaceDepictions() { + compositeDisposable.add( + repository.placeDepictions + .subscribeOn(ioScheduler) + .observeOn(mainThreadScheduler) + .subscribe(::selectNewDepictions), + ) } - // Add the new selections to the list of depicted items so that the selections appear - // immediately (i.e. without any search term queries) - depictedItems.value?.toMutableList() - ?.let { toSelect + it } - ?.distinctBy(DepictedItem::id) - ?.let { depictedItems.value = it } - } + /** + * Selects each [DepictedItem] in a given list as if they were clicked by the user by calling + * [onDepictItemClicked] for each depiction and adding the depictions to [depictedItems] + */ + private fun selectNewDepictions(toSelect: List) { + toSelect.forEach { + it.isSelected = true + repository.onDepictItemClicked(it, media) + } - override fun clearPreviousSelection() { - repository.cleanup() - } + // Add the new selections to the list of depicted items so that the selections appear + // immediately (i.e. without any search term queries) + depictedItems.value + ?.toMutableList() + ?.let { toSelect + it } + ?.distinctBy(DepictedItem::id) + ?.let { depictedItems.value = it } + } - override fun onPreviousButtonClicked() { - view.goToPreviousScreen() - } + override fun clearPreviousSelection() { + repository.cleanup() + } - override fun onDepictItemClicked(depictedItem: DepictedItem) { - repository.onDepictItemClicked(depictedItem, media) - } + override fun onPreviousButtonClicked() { + view.goToPreviousScreen() + } - override fun getDepictedItems(): LiveData> { - return depictedItems; - } + override fun onDepictItemClicked(depictedItem: DepictedItem) { + repository.onDepictItemClicked(depictedItem, media) + } - /** - * asks the repository to fetch depictions for the query - * @param query - */ - override fun searchForDepictions(query: String) { - searchTerm.onNext(query) - } + override fun getDepictedItems(): LiveData> = depictedItems - /** - * Check if depictions were selected - * from the depiction list - */ - override fun verifyDepictions() { - if (repository.selectedDepictions.isNotEmpty()) { - if (::depictsDao.isInitialized) { - //save all the selected Depicted item in room Database - depictsDao.savingDepictsInRoomDataBase(repository.selectedDepictions) - } - view.goToNextScreen() - } else { - view.noDepictionSelected() + /** + * asks the repository to fetch depictions for the query + * @param query + */ + override fun searchForDepictions(query: String) { + searchTerm.onNext(query) } - } - - /** - * Gets the selected depicts and send them for posting to the server - * and saves them in local storage - */ - @SuppressLint("CheckResult") - override fun updateDepictions(media: Media) { - if (repository.selectedDepictions.isNotEmpty() - || repository.selectedExistingDepictions.size != view.existingDepictions.size - ) { - view.showProgressDialog() - val selectedDepictions: MutableList = - (repository.selectedDepictions.map { it.id }.toMutableList() - + repository.selectedExistingDepictions).toMutableList() - if (selectedDepictions.isNotEmpty()) { + /** + * Check if depictions were selected + * from the depiction list + */ + override fun verifyDepictions() { + if (repository.selectedDepictions.isNotEmpty()) { if (::depictsDao.isInitialized) { - //save all the selected Depicted item in room Database + // save all the selected Depicted item in room Database depictsDao.savingDepictsInRoomDataBase(repository.selectedDepictions) } + view.goToNextScreen() + } else { + view.noDepictionSelected() + } + } - compositeDisposable.add( - depictsHelper.makeDepictionEdit(view.fragmentContext, media, selectedDepictions) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - media.depictionIds = selectedDepictions - repository.cleanup() - view.dismissProgressDialog() - view.updateDepicts() - view.goBackToPreviousScreen() - }, { error -> - if (error is InvalidLoginTokenException) { - view.navigateToLoginScreen(); - } else { - view.dismissProgressDialog() - Timber.e("Failed to update depictions") - } - }) - ) + /** + * Gets the selected depicts and send them for posting to the server + * and saves them in local storage + */ + @SuppressLint("CheckResult") + override fun updateDepictions(media: Media) { + if (repository.selectedDepictions.isNotEmpty() || + repository.selectedExistingDepictions.size != view.existingDepictions.size + ) { + view.showProgressDialog() + val selectedDepictions: MutableList = + ( + repository.selectedDepictions.map { it.id }.toMutableList() + + repository.selectedExistingDepictions + ).toMutableList() + if (selectedDepictions.isNotEmpty()) { + if (::depictsDao.isInitialized) { + // save all the selected Depicted item in room Database + depictsDao.savingDepictsInRoomDataBase(repository.selectedDepictions) + } + compositeDisposable.add( + depictsHelper + .makeDepictionEdit(view.fragmentContext, media, selectedDepictions) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + media.depictionIds = selectedDepictions + repository.cleanup() + view.dismissProgressDialog() + view.updateDepicts() + view.goBackToPreviousScreen() + }, { error -> + if (error is InvalidLoginTokenException) { + view.navigateToLoginScreen() + } else { + view.dismissProgressDialog() + Timber.e("Failed to update depictions") + } + }), + ) + } + } else { + repository.cleanup() + view.noDepictionSelected() } - } else { - repository.cleanup() - view.noDepictionSelected() } - } - override fun onAttachViewWithMedia(view: DepictsContract.View, media: Media) { - this.view = view - this.media = media - repository.selectedExistingDepictions = view.existingDepictions - compositeDisposable.add( - searchTerm - .observeOn(mainThreadScheduler) - .doOnNext { view.showProgress(true) } - .switchMap(::searchResultsWithTerm) - .observeOn(mainThreadScheduler) - .subscribe( - { (results, term) -> - view.showProgress(false) - view.showError(results.isEmpty() && term.isNotEmpty()) - depictedItems.value = results - }, - { t: Throwable? -> - view.showProgress(false) - view.showError(true) - Timber.e(t) - } - ) - ) - } + override fun onAttachViewWithMedia( + view: DepictsContract.View, + media: Media, + ) { + this.view = view + this.media = media + repository.selectedExistingDepictions = view.existingDepictions + compositeDisposable.add( + searchTerm + .observeOn(mainThreadScheduler) + .doOnNext { view.showProgress(true) } + .switchMap(::searchResultsWithTerm) + .observeOn(mainThreadScheduler) + .subscribe( + { (results, term) -> + view.showProgress(false) + view.showError(results.isEmpty() && term.isNotEmpty()) + depictedItems.value = results + }, + { t: Throwable? -> + view.showProgress(false) + view.showError(true) + Timber.e(t) + }, + ), + ) + } - /** - * Get the depicts from DepictsRoomdataBase - */ - private fun getRecentDepictedItems(): List = runBlocking { - val depictsList = depictsDao.depictsList().await() - return@runBlocking depictsList.map { it.item } + /** + * Get the depicts from DepictsRoomdataBase + */ + private fun getRecentDepictedItems(): List = + runBlocking { + val depictsList = depictsDao.depictsList().await() + return@runBlocking depictsList.map { it.item } + } } -} /** * This creates a dynamic proxy instance of the class, @@ -277,5 +292,6 @@ class DepictsPresenter @Inject constructor( * here our target object is the view. * Thus we when onDettach method of fragment is called we replace the binding of view to our object with the proxy instance */ -inline fun proxy() = Proxy - .newProxyInstance(T::class.java.classLoader, arrayOf(T::class.java)) { _, _, _ -> null } as T +inline fun proxy() = + Proxy + .newProxyInstance(T::class.java.classLoader, arrayOf(T::class.java)) { _, _, _ -> null } as T diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/UploadDepictsAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/UploadDepictsAdapter.kt index c179d99129..306795bde0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/UploadDepictsAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/UploadDepictsAdapter.kt @@ -4,9 +4,11 @@ import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.upload.categories.BaseDelegateAdapter import fr.free.nrw.commons.upload.structure.depictions.DepictedItem -class UploadDepictsAdapter(onDepictsClicked: (DepictedItem) -> Unit, nearbyPlace: Place?) : - BaseDelegateAdapter( +class UploadDepictsAdapter( + onDepictsClicked: (DepictedItem) -> Unit, + nearbyPlace: Place?, +) : BaseDelegateAdapter( uploadDepictsDelegate(onDepictsClicked, nearbyPlace), areItemsTheSame = { oldItem, newItem -> oldItem.id == newItem.id }, - areContentsTheSame = { itemA, itemB -> itemA.isSelected == itemB.isSelected} + areContentsTheSame = { itemA, itemB -> itemA.isSelected == itemB.isSelected }, ) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/UploadDepictsAdapterDelegates.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/UploadDepictsAdapterDelegates.kt index 896def499c..0f02c9672b 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/UploadDepictsAdapterDelegates.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/UploadDepictsAdapterDelegates.kt @@ -9,36 +9,37 @@ import fr.free.nrw.commons.databinding.LayoutUploadDepictsItemBinding import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.upload.structure.depictions.DepictedItem - -fun uploadDepictsDelegate(onDepictClicked: (DepictedItem) -> Unit, nearbyPlace: Place?) = - adapterDelegateViewBinding({ layoutInflater, parent -> - LayoutUploadDepictsItemBinding.inflate(layoutInflater, parent, false) - }) { - val onClickListener = { _: View? -> - if (item.name != nearbyPlace?.name) { - item.isSelected = !item.isSelected - binding.depictCheckbox.isChecked = item.isSelected - onDepictClicked(item) - } +fun uploadDepictsDelegate( + onDepictClicked: (DepictedItem) -> Unit, + nearbyPlace: Place?, +) = adapterDelegateViewBinding({ layoutInflater, parent -> + LayoutUploadDepictsItemBinding.inflate(layoutInflater, parent, false) +}) { + val onClickListener = { _: View? -> + if (item.name != nearbyPlace?.name) { + item.isSelected = !item.isSelected + binding.depictCheckbox.isChecked = item.isSelected + onDepictClicked(item) + } + } + binding.root.setOnClickListener(onClickListener) + binding.depictCheckbox.setOnClickListener(onClickListener) + bind { + if (item.name == nearbyPlace?.name) { + item.isSelected = true + binding.depictCheckbox.isChecked = true + binding.depictCheckbox.isEnabled = false + } else { + binding.depictCheckbox.isEnabled = true + binding.depictCheckbox.isChecked = item.isSelected } - binding.root.setOnClickListener(onClickListener) - binding.depictCheckbox.setOnClickListener(onClickListener) - bind { - if (item.name == nearbyPlace?.name) { - item.isSelected = true - binding.depictCheckbox.isChecked = true - binding.depictCheckbox.isEnabled = false - } else { - binding.depictCheckbox.isEnabled = true - binding.depictCheckbox.isChecked = item.isSelected - } - binding.depictsLabel.text = item.name - binding.description.text = item.description - val imageUrl = item.imageUrl - if (TextUtils.isEmpty(imageUrl)) { - binding.depictedImage.setActualImageResource(R.drawable.ic_wikidata_logo_24dp) - } else { - binding.depictedImage.setImageURI(Uri.parse(imageUrl)) - } + binding.depictsLabel.text = item.name + binding.description.text = item.description + val imageUrl = item.imageUrl + if (TextUtils.isEmpty(imageUrl)) { + binding.depictedImage.setActualImageResource(R.drawable.ic_wikidata_logo_24dp) + } else { + binding.depictedImage.setImageURI(Uri.parse(imageUrl)) } } +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/structure/depictions/DepictModel.kt b/app/src/main/java/fr/free/nrw/commons/upload/structure/depictions/DepictModel.kt index 3caa2934df..7242b8eed3 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/structure/depictions/DepictModel.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/structure/depictions/DepictModel.kt @@ -5,7 +5,6 @@ import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.repository.UploadRepository import io.github.coordinates2country.Coordinates2Country import io.reactivex.Flowable -import io.reactivex.Observable import io.reactivex.Single import io.reactivex.processors.BehaviorProcessor import timber.log.Timber @@ -16,79 +15,93 @@ import javax.inject.Singleton * The model class for depictions in upload */ @Singleton -class DepictModel @Inject constructor(private val depictsClient: DepictsClient) { +class DepictModel + @Inject + constructor( + private val depictsClient: DepictsClient, + ) { + val nearbyPlaces: BehaviorProcessor> = BehaviorProcessor.createDefault(emptyList()) - val nearbyPlaces: BehaviorProcessor> = BehaviorProcessor.createDefault(emptyList()) - - companion object { - private const val SEARCH_DEPICTS_LIMIT = 25 - } + companion object { + private const val SEARCH_DEPICTS_LIMIT = 25 + } - /** - * Search for depictions - */ - fun searchAllEntities(query: String, repository: UploadRepository): Flowable> { - return if (query.isBlank()) { - nearbyPlaces.switchMap { places: List -> - val qids = mutableSetOf() - for(place in places) { - place.wikiDataEntityId?.let { qids.add(it) } - } - repository.uploads.forEach { item -> - if(item.gpsCoords != null && item.gpsCoords.imageCoordsExists) { - Coordinates2Country.countryQID(item.gpsCoords.decLatitude, - item.gpsCoords.decLongitude)?.let { qids.add("Q$it") } + /** + * Search for depictions + */ + fun searchAllEntities( + query: String, + repository: UploadRepository, + ): Flowable> = + if (query.isBlank()) { + nearbyPlaces.switchMap { places: List -> + val qids = mutableSetOf() + for (place in places) { + place.wikiDataEntityId?.let { qids.add(it) } + } + repository.uploads.forEach { item -> + if (item.gpsCoords != null && item.gpsCoords.imageCoordsExists) { + Coordinates2Country + .countryQID( + item.gpsCoords.decLatitude, + item.gpsCoords.decLongitude, + )?.let { qids.add("Q$it") } + } } + getPlaceDepictions(ArrayList(qids)).toFlowable() } - getPlaceDepictions(ArrayList(qids)).toFlowable() + } else { + networkItems(query) } - } else - networkItems(query) - } - - /** - * Provides [DepictedItem] instances via a [Single] for a given list of ids, providing an - * empty list if no places/country are provided or if there is an error - */ - fun getPlaceDepictions(qids: List): Single> = - qids.toIds().let { ids -> - if (ids.isNotEmpty()) - depictsClient.getEntities(ids) - .map{ - it.entities() - .values - .mapIndexed { index, entity -> DepictedItem(entity)} - } - .onErrorResumeWithEmptyList() - else Single.just(emptyList()) - } - fun getDepictions(ids: String): Single> = - if (ids.isNotEmpty()) - depictsClient.getEntities(ids) - .map{ - it.entities() - .values - .mapIndexed { _, entity -> DepictedItem(entity)} + /** + * Provides [DepictedItem] instances via a [Single] for a given list of ids, providing an + * empty list if no places/country are provided or if there is an error + */ + fun getPlaceDepictions(qids: List): Single> = + qids.toIds().let { ids -> + if (ids.isNotEmpty()) { + depictsClient + .getEntities(ids) + .map { + it + .entities() + .values + .mapIndexed { index, entity -> DepictedItem(entity) } + }.onErrorResumeWithEmptyList() + } else { + Single.just(emptyList()) } - .onErrorResumeWithEmptyList() - else Single.just(emptyList()) + } + fun getDepictions(ids: String): Single> = + if (ids.isNotEmpty()) { + depictsClient + .getEntities(ids) + .map { + it + .entities() + .values + .mapIndexed { _, entity -> DepictedItem(entity) } + }.onErrorResumeWithEmptyList() + } else { + Single.just(emptyList()) + } - private fun networkItems(query: String): Flowable> { - return depictsClient.searchForDepictions(query, SEARCH_DEPICTS_LIMIT, 0) - .onErrorResumeWithEmptyList() - .toFlowable() - } + private fun networkItems(query: String): Flowable> = + depictsClient + .searchForDepictions(query, SEARCH_DEPICTS_LIMIT, 0) + .onErrorResumeWithEmptyList() + .toFlowable() - fun cleanUp() { - nearbyPlaces.offer(emptyList()) + fun cleanUp() { + nearbyPlaces.offer(emptyList()) + } } -} - private fun List.toIds() = mapNotNull { it }.joinToString("|") -private fun Single>.onErrorResumeWithEmptyList() = onErrorResumeNext { t: Throwable -> - Single.just(emptyList()).also { Timber.e(t) } -} +private fun Single>.onErrorResumeWithEmptyList() = + onErrorResumeNext { t: Throwable -> + Single.just(emptyList()).also { Timber.e(t) } + } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/structure/depictions/DepictedItem.kt b/app/src/main/java/fr/free/nrw/commons/upload/structure/depictions/DepictedItem.kt index 499011109a..0e4ed482d2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/structure/depictions/DepictedItem.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/structure/depictions/DepictedItem.kt @@ -7,15 +7,17 @@ import fr.free.nrw.commons.category.CategoryItem import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.upload.WikidataItem import fr.free.nrw.commons.wikidata.WikidataProperties -import fr.free.nrw.commons.wikidata.WikidataProperties.* +import fr.free.nrw.commons.wikidata.WikidataProperties.COMMONS_CATEGORY +import fr.free.nrw.commons.wikidata.WikidataProperties.IMAGE +import fr.free.nrw.commons.wikidata.WikidataProperties.INSTANCE_OF import fr.free.nrw.commons.wikidata.model.DataValue import fr.free.nrw.commons.wikidata.model.Entities -import fr.free.nrw.commons.wikidata.model.Statement_partial +import fr.free.nrw.commons.wikidata.model.StatementPartial import kotlinx.parcelize.Parcelize import java.math.BigInteger import java.security.MessageDigest import java.security.NoSuchAlgorithmException -import java.util.* +import java.util.Locale const val THUMB_IMAGE_SIZE = "70px" @@ -31,19 +33,19 @@ data class DepictedItem constructor( val instanceOfs: List, val commonsCategories: List, var isSelected: Boolean, - @PrimaryKey override val id: String -) : WikidataItem, Parcelable { - + @PrimaryKey override val id: String, +) : WikidataItem, + Parcelable { constructor(entity: Entities.Entity) : this( entity, entity.labels().byLanguageOrFirstOrEmpty(), - entity.descriptions().byLanguageOrFirstOrEmpty() + entity.descriptions().byLanguageOrFirstOrEmpty(), ) constructor(entity: Entities.Entity, place: Place) : this( entity, place.name, - place.longDescription + place.longDescription, ) constructor(entity: Entities.Entity, name: String, description: String) : this( @@ -53,56 +55,61 @@ data class DepictedItem constructor( getImageUrl(it.value, THUMB_IMAGE_SIZE) }, entity[INSTANCE_OF].toIds(), - entity[COMMONS_CATEGORY]?.map { CategoryItem((it.mainSnak.dataValue as DataValue.ValueString).value, - "", "", false) } + entity[COMMONS_CATEGORY]?.map { + CategoryItem( + (it.mainSnak.dataValue as DataValue.ValueString).value, + "", + "", + false, + ) + } ?: emptyList(), false, - entity.id() + entity.id(), ) - override fun equals(other: Any?) = when { - this === other -> true - other is DepictedItem -> name == other.name - else -> false - } - - override fun hashCode(): Int { - return name.hashCode() - } + override fun equals(other: Any?) = + when { + this === other -> true + other is DepictedItem -> name == other.name + else -> false + } + override fun hashCode(): Int = name.hashCode() } -private fun List?.toIds(): List { - return this?.map { it.mainSnak.dataValue } +private fun List?.toIds(): List = + this + ?.map { it.mainSnak.dataValue } ?.filterIsInstance() ?.map { it.value.id } ?: emptyList() -} -private val List?.primaryImageValue: DataValue.ValueString? +private val List?.primaryImageValue: DataValue.ValueString? get() = this?.firstOrNull()?.mainSnak?.dataValue as? DataValue.ValueString -operator fun Entities.Entity.get(property: WikidataProperties) = - statements?.get(property.propertyName) +operator fun Entities.Entity.get(property: WikidataProperties) = statements?.get(property.propertyName) private fun Map.byLanguageOrFirstOrEmpty() = let { it[Locale.getDefault().language] ?: it.values.firstOrNull() }?.value() ?: "" -private fun getImageUrl(title: String, size: String): String { - return title.substringAfter(":") +private fun getImageUrl( + title: String, + size: String, +): String = + title + .substringAfter(":") .replace(" ", "_") .let { - val MD5Hash = getMd5(it) - "https://upload.wikimedia.org/wikipedia/commons/thumb/${MD5Hash[0]}/${MD5Hash[0]}${MD5Hash[1]}/$it/$size-$it" + val md5Hash = getMd5(it) + "https://upload.wikimedia.org/wikipedia/commons/thumb/${md5Hash[0]}/${md5Hash[0]}${md5Hash[1]}/$it/$size-$it" } -} /** * Generates MD5 hash for the filename */ -private fun getMd5(input: String): String { - return try { - +private fun getMd5(input: String): String = + try { // Static getInstance method is called with hashing MD5 val md = MessageDigest.getInstance("MD5") @@ -119,8 +126,6 @@ private fun getMd5(input: String): String { hashtext = "0$hashtext" } hashtext - } // For specifying wrong message digest algorithms - catch (e: NoSuchAlgorithmException) { + } catch (e: NoSuchAlgorithmException) { throw RuntimeException(e) } -} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index ebf930915e..2c9022d73c 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -43,14 +43,14 @@ import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber -import java.util.* +import java.util.Date import java.util.regex.Pattern import javax.inject.Inject class UploadWorker( - private var appContext: Context, workerParams: WorkerParameters -): CoroutineWorker(appContext, workerParams) { - + private var appContext: Context, + workerParams: WorkerParameters, +) : CoroutineWorker(appContext, workerParams) { private var notificationManager: NotificationManagerCompat? = null @Inject @@ -74,24 +74,23 @@ class UploadWorker( @Inject lateinit var fileUtilsWrapper: FileUtilsWrapper - private val PROCESSING_UPLOADS_NOTIFICATION_TAG = BuildConfig.APPLICATION_ID + " : upload_tag" - - private val PROCESSING_UPLOADS_NOTIFICATION_ID = 101 - + private val processingUploadsNotificationTag = BuildConfig.APPLICATION_ID + " : upload_tag" + private val processingUploadsNotificationId = 101 - //Attributes of the current-upload notification - private var currentNotificationID: Int = -1// lateinit is not allowed with primitives + // Attributes of the current-upload notification + private var currentNotificationID: Int = -1 // lateinit is not allowed with primitives private lateinit var currentNotificationTag: String private var currentNotification: NotificationCompat.Builder - private val statesToProcess= ArrayList() + private val statesToProcess = ArrayList() - private val STASH_ERROR_CODES = listOf( + private val stashErrorCodes = + listOf( "uploadstash-file-not-found", "stashfailed", "verification-error", - "chunk-too-small" + "chunk-too-small", ) init { @@ -113,55 +112,61 @@ class UploadWorker( open inner class NotificationUpdateProgressListener( private var notificationFinishingTitle: String?, - var contribution: Contribution? + var contribution: Contribution?, ) { - - fun onProgress(transferred: Long, total: Long) { + fun onProgress( + transferred: Long, + total: Long, + ) { if (transferred == total) { // Completed! - currentNotification.setContentTitle(notificationFinishingTitle) + currentNotification + .setContentTitle(notificationFinishingTitle) .setProgress(0, 100, true) } else { currentNotification .setProgress( 100, (transferred.toDouble() / total.toDouble() * 100).toInt(), - false + false, ) } notificationManager?.cancel( - PROCESSING_UPLOADS_NOTIFICATION_TAG, PROCESSING_UPLOADS_NOTIFICATION_ID + processingUploadsNotificationTag, + processingUploadsNotificationId, ) notificationManager?.notify( currentNotificationTag, currentNotificationID, - currentNotification.build() + currentNotification.build(), ) contribution!!.transferred = transferred contributionDao.update(contribution).blockingAwait() } - open fun onChunkUploaded(contribution: Contribution, chunkInfo: ChunkInfo?) { + open fun onChunkUploaded( + contribution: Contribution, + chunkInfo: ChunkInfo?, + ) { contribution.chunkInfo = chunkInfo contributionDao.update(contribution).blockingAwait() } } - private fun getNotificationBuilder(channelId: String): NotificationCompat.Builder? { - return NotificationCompat.Builder(appContext, channelId) + private fun getNotificationBuilder(channelId: String): NotificationCompat.Builder? = + NotificationCompat + .Builder(appContext, channelId) .setAutoCancel(true) .setSmallIcon(R.drawable.ic_launcher) .setLargeIcon( BitmapFactory.decodeResource( appContext.resources, - R.drawable.ic_launcher - ) - ) - .setAutoCancel(true) + R.drawable.ic_launcher, + ), + ).setAutoCancel(true) .setOnlyAlertOnce(true) .setProgress(100, 0, true) .setOngoing(true) - } override suspend fun doWork(): Result { try { @@ -169,16 +174,22 @@ class UploadWorker( // Start a foreground service setForeground(createForegroundInfo()) notificationManager = NotificationManagerCompat.from(appContext) - val processingUploads = getNotificationBuilder( - CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL - )!! + val processingUploads = + getNotificationBuilder( + CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL, + )!! withContext(Dispatchers.IO) { - while (contributionDao.getContribution(statesToProcess) - .blockingGet().size > 0 && contributionDao.getContribution( - arrayListOf( - Contribution.STATE_IN_PROGRESS - ) - ).blockingGet().size == 0 + while (contributionDao + .getContribution(statesToProcess) + .blockingGet() + .size > 0 && + contributionDao + .getContribution( + arrayListOf( + Contribution.STATE_IN_PROGRESS, + ), + ).blockingGet() + .size == 0 ) { /* queuedContributions receives the results from a one-shot query. @@ -189,23 +200,25 @@ class UploadWorker( Related issues (fixed): https://github.com/commons-app/apps-android-commons/issues/5136 https://github.com/commons-app/apps-android-commons/issues/5346 - */ - val queuedContributions = contributionDao.getContribution(statesToProcess) - .blockingGet() - //Showing initial notification for the number of uploads being processed + */ + val queuedContributions = + contributionDao + .getContribution(statesToProcess) + .blockingGet() + // Showing initial notification for the number of uploads being processed processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads)) processingUploads.setContentText( appContext.resources.getQuantityString( R.plurals.starting_multiple_uploads, queuedContributions.size, - queuedContributions.size - ) + queuedContributions.size, + ), ) notificationManager?.notify( - PROCESSING_UPLOADS_NOTIFICATION_TAG, - PROCESSING_UPLOADS_NOTIFICATION_ID, - processingUploads.build() + processingUploadsNotificationTag, + processingUploadsNotificationId, + processingUploads.build(), ) val sortedQueuedContributionsList: List = @@ -222,16 +235,17 @@ class UploadWorker( uploadContribution(contribution = contribution) } } - //Dismiss the global notification + // Dismiss the global notification notificationManager?.cancel( - PROCESSING_UPLOADS_NOTIFICATION_TAG, - PROCESSING_UPLOADS_NOTIFICATION_ID + processingUploadsNotificationTag, + processingUploadsNotificationId, ) } // Trigger WorkManager to process any new contributions that may have been added to the queue - val updatedContributionQueue = withContext(Dispatchers.IO) { - contributionDao.getContribution(statesToProcess).blockingGet() - } + val updatedContributionQueue = + withContext(Dispatchers.IO) { + contributionDao.getContribution(statesToProcess).blockingGet() + } if (updatedContributionQueue.isNotEmpty()) { return Result.retry() } @@ -248,28 +262,27 @@ class UploadWorker( /** * Create new notification for foreground service */ - private fun createForegroundInfo(): ForegroundInfo { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + private fun createForegroundInfo(): ForegroundInfo = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ForegroundInfo( 1, createNotificationForForegroundService(), - ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC, ) } else { ForegroundInfo( 1, - createNotificationForForegroundService() + createNotificationForForegroundService(), ) } - } - override suspend fun getForegroundInfo(): ForegroundInfo { - return createForegroundInfo() - } + override suspend fun getForegroundInfo(): ForegroundInfo = createForegroundInfo() + private fun createNotificationForForegroundService(): Notification { // TODO: Improve notification for foreground service return getNotificationBuilder( - CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL)!! + CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL, + )!! .setContentTitle(appContext.getString(R.string.upload_in_progress)) .build() } @@ -296,85 +309,95 @@ class UploadWorker( currentNotification.setContentTitle( appContext.getString( R.string.upload_progress_notification_title_start, - displayTitle - ) + displayTitle, + ), ) notificationManager?.notify( currentNotificationTag, currentNotificationID, - currentNotification.build() + currentNotification.build(), ) val filename = media.filename - val notificationProgressUpdater = NotificationUpdateProgressListener( - appContext.getString( - R.string.upload_progress_notification_title_finishing, - displayTitle - ), - contribution - ) + val notificationProgressUpdater = + NotificationUpdateProgressListener( + appContext.getString( + R.string.upload_progress_notification_title_finishing, + displayTitle, + ), + contribution, + ) try { - //Upload the file to stash - val stashUploadResult = uploadClient.uploadFileToStash( - filename!!, contribution, notificationProgressUpdater - ).onErrorReturn{ - return@onErrorReturn StashUploadResult( - StashUploadState.FAILED,fileKey = null,errorMessage = it.message - ) - }.blockingSingle() + // Upload the file to stash + val stashUploadResult = + uploadClient + .uploadFileToStash( + filename!!, + contribution, + notificationProgressUpdater, + ).onErrorReturn { + return@onErrorReturn StashUploadResult( + StashUploadState.FAILED, + fileKey = null, + errorMessage = it.message, + ) + }.blockingSingle() when (stashUploadResult.state) { StashUploadState.SUCCESS -> { - //If the stash upload succeeds + // If the stash upload succeeds Timber.d("Upload to stash success for fileName: $filename") Timber.d("Ensure uniqueness of filename") val uniqueFileName = findUniqueFileName(filename) try { - //Upload the file from stash - val uploadResult = uploadClient.uploadFileFromStash( - contribution, uniqueFileName, stashUploadResult.fileKey - ).onErrorReturn { - return@onErrorReturn null - }.blockingSingle() + // Upload the file from stash + val uploadResult = + uploadClient + .uploadFileFromStash( + contribution, + uniqueFileName, + stashUploadResult.fileKey, + ).onErrorReturn { + return@onErrorReturn null + }.blockingSingle() if (null != uploadResult && uploadResult.isSuccessful()) { Timber.d( - "Stash Upload success..proceeding to make wikidata edit" + "Stash Upload success..proceeding to make wikidata edit", ) - wikidataEditService.addDepictionsAndCaptions(uploadResult, contribution) + wikidataEditService + .addDepictionsAndCaptions(uploadResult, contribution) .blockingSubscribe() - if(contribution.wikidataPlace==null){ + if (contribution.wikidataPlace == null) { Timber.d( - "WikiDataEdit not required, upload success" + "WikiDataEdit not required, upload success", ) - saveCompletedContribution(contribution,uploadResult) - }else{ + saveCompletedContribution(contribution, uploadResult) + } else { Timber.d( - "WikiDataEdit not required, making wikidata edit" + "WikiDataEdit not required, making wikidata edit", ) makeWikiDataEdit(uploadResult, contribution) } showSuccessNotification(contribution) - } else { Timber.e("Stash Upload failed") showFailedNotification(contribution) contribution.state = Contribution.STATE_FAILED contribution.chunkInfo = null contributionDao.save(contribution).blockingAwait() - } - }catch (exception : Exception){ + } catch (exception: Exception) { Timber.e(exception) Timber.e("Upload from stash failed for contribution : $filename") showFailedNotification(contribution) - contribution.state=Contribution.STATE_FAILED + contribution.state = Contribution.STATE_FAILED contributionDao.saveSynchronous(contribution) - if (STASH_ERROR_CODES.contains(exception.message)) { + if (stashErrorCodes.contains(exception.message)) { clearChunks(contribution) } } @@ -396,66 +419,75 @@ class UploadWorker( showErrorNotification(contribution) contributionDao.saveSynchronous(contribution) if (stashUploadResult.errorMessage.equals( - CsrfTokenClient.INVALID_TOKEN_ERROR_MESSAGE) - ) { + CsrfTokenClient.INVALID_TOKEN_ERROR_MESSAGE, + ) + ) { Timber.e("Invalid Login, logging out") showInvalidLoginNotification(contribution) val username = sessionManager.userName - var logoutListener = CommonsApplication.BaseLogoutListener( - appContext, - appContext.getString(R.string.invalid_login_message), - username - ) - CommonsApplication.getInstance() + var logoutListener = + CommonsApplication.BaseLogoutListener( + appContext, + appContext.getString(R.string.invalid_login_message), + username, + ) + CommonsApplication + .getInstance() .clearApplicationData(appContext, logoutListener) } } } - }catch (exception: Exception){ + } catch (exception: Exception) { Timber.e(exception) Timber.e("Stash upload failed for contribution: $filename") showFailedNotification(contribution) - contribution.errorInfo=exception.message - contribution.state=Contribution.STATE_FAILED + contribution.errorInfo = exception.message + contribution.state = Contribution.STATE_FAILED clearChunks(contribution) } } private fun clearChunks(contribution: Contribution) { - contribution.chunkInfo=null + contribution.chunkInfo = null contributionDao.saveSynchronous(contribution) } /** * Make the WikiData Edit, if applicable */ - private suspend fun makeWikiDataEdit(uploadResult: UploadResult, contribution: Contribution) { + private suspend fun makeWikiDataEdit( + uploadResult: UploadResult, + contribution: Contribution, + ) { val wikiDataPlace = contribution.wikidataPlace if (wikiDataPlace != null && wikiDataPlace.imageValue == null) { if (!contribution.hasInvalidLocation()) { - var revisionID: Long?=null + var revisionID: Long? = null try { - revisionID = wikidataEditService.createClaim( - wikiDataPlace, uploadResult.filename, - contribution.media.captions - ) + revisionID = + wikidataEditService.createClaim( + wikiDataPlace, + uploadResult.filename, + contribution.media.captions, + ) if (null != revisionID) { showSuccessNotification(contribution) } - }catch (exception: Exception){ + } catch (exception: Exception) { Timber.e(exception) } withContext(Dispatchers.Main) { wikidataEditService.handleImageClaimResult( contribution.wikidataPlace, - revisionID + revisionID, ) } } else { withContext(Dispatchers.Main) { wikidataEditService.handleImageClaimResult( - contribution.wikidataPlace, null + contribution.wikidataPlace, + null, ) } } @@ -463,11 +495,16 @@ class UploadWorker( saveCompletedContribution(contribution, uploadResult) } - private fun saveCompletedContribution(contribution: Contribution, uploadResult: UploadResult) { - val contributionFromUpload = mediaClient.getMedia("File:" + uploadResult.filename) - .map { media: Media? -> contribution.completeWith(media!!) } - .blockingGet() - contributionFromUpload.dateModified=Date() + private fun saveCompletedContribution( + contribution: Contribution, + uploadResult: UploadResult, + ) { + val contributionFromUpload = + mediaClient + .getMedia("File:" + uploadResult.filename) + .map { media: Media? -> contribution.completeWith(media!!) } + .blockingGet() + contributionFromUpload.dateModified = Date() contributionDao.deleteAndSaveContribution(contribution, contributionFromUpload) // Upload success, save to uploaded status. @@ -487,8 +524,8 @@ class UploadWorker( imageSha1, modifiedSha1, imageSha1 == modifiedSha1, - true - ) + true, + ), ) } } @@ -498,25 +535,26 @@ class UploadWorker( var sequenceFileName: String? var sequenceNumber = 1 while (true) { - sequenceFileName = if (sequenceNumber == 1) { - fileName - } else { - if (fileName.indexOf('.') == -1) { - "$fileName $sequenceNumber" + sequenceFileName = + if (sequenceNumber == 1) { + fileName } else { - val regex = - Pattern.compile("^(.*)(\\..+?)$") - val regexMatcher = regex.matcher(fileName) - regexMatcher.replaceAll("$1 $sequenceNumber$2") + if (fileName.indexOf('.') == -1) { + "$fileName $sequenceNumber" + } else { + val regex = + Pattern.compile("^(.*)(\\..+?)$") + val regexMatcher = regex.matcher(fileName) + regexMatcher.replaceAll("$1 $sequenceNumber$2") + } } - } - if (!mediaClient.checkPageExistsUsingTitle( - String.format( - "File:%s", - sequenceFileName - ) - ) - .blockingGet() + if (!mediaClient + .checkPageExistsUsingTitle( + String.format( + "File:%s", + sequenceFileName, + ), + ).blockingGet() ) { break } @@ -532,20 +570,21 @@ class UploadWorker( @SuppressLint("StringFormatInvalid") private fun showSuccessNotification(contribution: Contribution) { val displayTitle = contribution.media.displayTitle - contribution.state=Contribution.STATE_COMPLETED + contribution.state = Contribution.STATE_COMPLETED currentNotification.setContentIntent(getPendingIntent(MainActivity::class.java)) - currentNotification.setContentTitle( - appContext.getString( - R.string.upload_completed_notification_title, - displayTitle - ) - ) - .setContentText(appContext.getString(R.string.upload_completed_notification_text)) + currentNotification + .setContentTitle( + appContext.getString( + R.string.upload_completed_notification_title, + displayTitle, + ), + ).setContentText(appContext.getString(R.string.upload_completed_notification_text)) .setProgress(0, 0, false) .setOngoing(false) notificationManager?.notify( - currentNotificationTag, currentNotificationID, - currentNotification.build() + currentNotificationTag, + currentNotificationID, + currentNotification.build(), ) } @@ -557,35 +596,38 @@ class UploadWorker( private fun showFailedNotification(contribution: Contribution) { val displayTitle = contribution.media.displayTitle currentNotification.setContentIntent(getPendingIntent(UploadProgressActivity::class.java)) - currentNotification.setContentTitle( - appContext.getString( - R.string.upload_failed_notification_title, - displayTitle - ) - ) - .setContentText(appContext.getString(R.string.upload_failed_notification_subtitle)) + currentNotification + .setContentTitle( + appContext.getString( + R.string.upload_failed_notification_title, + displayTitle, + ), + ).setContentText(appContext.getString(R.string.upload_failed_notification_subtitle)) .setProgress(0, 0, false) .setOngoing(false) notificationManager?.notify( - currentNotificationTag, currentNotificationID, - currentNotification.build() + currentNotificationTag, + currentNotificationID, + currentNotification.build(), ) } + @SuppressLint("StringFormatInvalid") private fun showInvalidLoginNotification(contribution: Contribution) { val displayTitle = contribution.media.displayTitle - currentNotification.setContentTitle( - appContext.getString( - R.string.upload_failed_notification_title, - displayTitle - ) - ) - .setContentText(appContext.getString(R.string.invalid_login_message)) + currentNotification + .setContentTitle( + appContext.getString( + R.string.upload_failed_notification_title, + displayTitle, + ), + ).setContentText(appContext.getString(R.string.invalid_login_message)) .setProgress(0, 0, false) .setOngoing(false) notificationManager?.notify( - currentNotificationTag, currentNotificationID, - currentNotification.build() + currentNotificationTag, + currentNotificationID, + currentNotification.build(), ) } @@ -595,18 +637,19 @@ class UploadWorker( @SuppressLint("StringFormatInvalid") private fun showErrorNotification(contribution: Contribution) { val displayTitle = contribution.media.displayTitle - currentNotification.setContentTitle( - appContext.getString( - R.string.upload_failed_notification_title, - displayTitle - ) - ) - .setContentText(contribution.errorInfo) + currentNotification + .setContentTitle( + appContext.getString( + R.string.upload_failed_notification_title, + displayTitle, + ), + ).setContentText(contribution.errorInfo) .setProgress(0, 0, false) .setOngoing(false) notificationManager?.notify( - currentNotificationTag, currentNotificationID, - currentNotification.build() + currentNotificationTag, + currentNotificationID, + currentNotification.build(), ) } @@ -616,20 +659,21 @@ class UploadWorker( */ private fun showPausedNotification(contribution: Contribution) { val displayTitle = contribution.media.displayTitle - + currentNotification.setContentIntent(getPendingIntent(UploadProgressActivity::class.java)) - currentNotification.setContentTitle( - appContext.getString( - R.string.upload_paused_notification_title, - displayTitle - ) - ) - .setContentText(appContext.getString(R.string.upload_paused_notification_subtitle)) + currentNotification + .setContentTitle( + appContext.getString( + R.string.upload_paused_notification_title, + displayTitle, + ), + ).setContentText(appContext.getString(R.string.upload_paused_notification_subtitle)) .setProgress(0, 0, false) .setOngoing(false) notificationManager!!.notify( - currentNotificationTag, currentNotificationID, - currentNotification.build() + currentNotificationTag, + currentNotificationID, + currentNotification.build(), ) } @@ -640,15 +684,16 @@ class UploadWorker( private fun showCancelledNotification(contribution: Contribution) { val displayTitle = contribution.media.displayTitle currentNotification.setContentIntent(getPendingIntent(UploadProgressActivity::class.java)) - currentNotification.setContentTitle( - displayTitle - ) - .setContentText("Upload has been cancelled!") + currentNotification + .setContentTitle( + displayTitle, + ).setContentText("Upload has been cancelled!") .setProgress(0, 0, false) .setOngoing(false) notificationManager!!.notify( - currentNotificationTag, currentNotificationID, - currentNotification.build() + currentNotificationTag, + currentNotificationID, + currentNotification.build(), ) } @@ -656,17 +701,18 @@ class UploadWorker( * Method used to get Pending intent for opening different screen after clicking on notification * @param toClass */ - private fun getPendingIntent(toClass:Class):PendingIntent - { - val intent = Intent(appContext,toClass) + private fun getPendingIntent(toClass: Class): PendingIntent { + val intent = Intent(appContext, toClass) return TaskStackBuilder.create(appContext).run { - addNextIntentWithParentStack(intent) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - getPendingIntent(0, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) - } else { - getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) - } - } + addNextIntentWithParentStack(intent) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + getPendingIntent( + 0, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, + ) + } else { + getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) + } + } } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/WorkRequestHelper.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/WorkRequestHelper.kt index 7d6b383910..39d59a8d54 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/WorkRequestHelper.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/WorkRequestHelper.kt @@ -1,7 +1,12 @@ package fr.free.nrw.commons.upload.worker import android.content.Context -import androidx.work.* +import androidx.work.BackoffPolicy +import androidx.work.Constraints +import androidx.work.ExistingWorkPolicy +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager import androidx.work.WorkRequest.Companion.MIN_BACKOFF_MILLIS import timber.log.Timber import java.util.concurrent.TimeUnit @@ -10,14 +15,14 @@ import java.util.concurrent.TimeUnit * Helper class for all the one time work requests */ class WorkRequestHelper { - companion object { - private var isUploadWorkerRunning = false private val lock = Object() - fun makeOneTimeWorkRequest(context: Context, existingWorkPolicy: ExistingWorkPolicy) { - + fun makeOneTimeWorkRequest( + context: Context, + existingWorkPolicy: ExistingWorkPolicy, + ) { synchronized(lock) { if (isUploadWorkerRunning) { Timber.e("UploadWorker is already running. Cannot start another instance.") @@ -35,23 +40,26 @@ class WorkRequestHelper { More details on when exactly it is retried: https://developer.android.com/guide/background/persistent/getting-started/define-work#retries_backoff - */ - val constraints: Constraints = Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .build() + */ + val constraints: Constraints = + Constraints + .Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() val uploadRequest: OneTimeWorkRequest = - OneTimeWorkRequest.Builder(UploadWorker::class.java) + OneTimeWorkRequest + .Builder(UploadWorker::class.java) .setBackoffCriteria( BackoffPolicy.LINEAR, MIN_BACKOFF_MILLIS, - TimeUnit.MILLISECONDS - ) - .setConstraints(constraints) + TimeUnit.MILLISECONDS, + ).setConstraints(constraints) .build() WorkManager.getInstance(context).enqueueUniqueWork( - UploadWorker::class.java.simpleName, existingWorkPolicy, uploadRequest + UploadWorker::class.java.simpleName, + existingWorkPolicy, + uploadRequest, ) - } /** @@ -64,4 +72,3 @@ class WorkRequestHelper { } } } - diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ConfigUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/ConfigUtils.kt index 539c9246ee..bfd0bbb6b2 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ConfigUtils.kt +++ b/app/src/main/java/fr/free/nrw/commons/utils/ConfigUtils.kt @@ -9,16 +9,13 @@ object ConfigUtils { val isBetaFlavour: Boolean = BuildConfig.FLAVOR == "beta" @JvmStatic - private fun Context.getVersionName(): String { - return try { + private fun Context.getVersionName(): String = + try { packageManager.getPackageInfo(packageName, 0).versionName } catch (e: PackageManager.NameNotFoundException) { BuildConfig.VERSION_NAME } - } @JvmStatic - fun Context.getVersionNameWithSha(): String { - return "${getVersionName()}~${BuildConfig.COMMIT_SHA}" - } -} \ No newline at end of file + fun Context.getVersionNameWithSha(): String = "${getVersionName()}~${BuildConfig.COMMIT_SHA}" +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/CustomSelectorUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/CustomSelectorUtils.kt index daae40f61a..fc80252fc9 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/CustomSelectorUtils.kt +++ b/app/src/main/java/fr/free/nrw/commons/utils/CustomSelectorUtils.kt @@ -5,8 +5,8 @@ import android.content.Context import android.net.Uri import androidx.exifinterface.media.ExifInterface import fr.free.nrw.commons.customselector.model.Image -import fr.free.nrw.commons.filepicker.PickedFiles import fr.free.nrw.commons.customselector.ui.selector.ImageLoader +import fr.free.nrw.commons.filepicker.PickedFiles import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.upload.FileProcessor import fr.free.nrw.commons.upload.FileUtilsWrapper @@ -22,7 +22,6 @@ import java.net.UnknownHostException */ class CustomSelectorUtils { companion object { - /** * Get image sha1 from uri, used to retrieve the original image sha1. */ @@ -30,10 +29,9 @@ class CustomSelectorUtils { uri: Uri, ioDispatcher: CoroutineDispatcher, fileUtilsWrapper: FileUtilsWrapper, - contentResolver: ContentResolver - ): String { - return withContext(ioDispatcher) { - + contentResolver: ContentResolver, + ): String = + withContext(ioDispatcher) { try { val result = fileUtilsWrapper.getSHA1(contentResolver.openInputStream(uri)) result @@ -42,7 +40,6 @@ class CustomSelectorUtils { "" } } - } /** * Generates modified SHA1 of an image @@ -52,37 +49,37 @@ class CustomSelectorUtils { defaultDispatcher: CoroutineDispatcher, context: Context, fileProcessor: FileProcessor, - fileUtilsWrapper: FileUtilsWrapper - ): String { - return withContext(defaultDispatcher) { + fileUtilsWrapper: FileUtilsWrapper, + ): String = + withContext(defaultDispatcher) { val uploadableFile = PickedFiles.pickedExistingPicture(context, image.uri) - val exifInterface: ExifInterface? = try { - ExifInterface(uploadableFile.file!!) - } catch (e: IOException) { - Timber.e(e) - null - } + val exifInterface: ExifInterface? = + try { + ExifInterface(uploadableFile.file!!) + } catch (e: IOException) { + Timber.e(e) + null + } fileProcessor.redactExifTags(exifInterface, fileProcessor.getExifTagsToRedact()) val sha1 = fileUtilsWrapper.getSHA1( - fileUtilsWrapper.getFileInputStream(uploadableFile.filePath) + fileUtilsWrapper.getFileInputStream(uploadableFile.filePath), ) uploadableFile.file.delete() sha1 } - } /** * Query SHA1, return result if previously queried, otherwise start a new query. * * @return true if the image exists on Commons, false otherwise. */ - suspend fun checkWhetherFileExistsOnCommonsUsingSHA1(SHA1: String, - ioDispatcher : CoroutineDispatcher, - mediaClient: MediaClient - ): ImageLoader.Result { - return withContext(ioDispatcher) { - + suspend fun checkWhetherFileExistsOnCommonsUsingSHA1( + SHA1: String, + ioDispatcher: CoroutineDispatcher, + mediaClient: MediaClient, + ): ImageLoader.Result = + withContext(ioDispatcher) { var result: ImageLoader.Result = ImageLoader.Result.FALSE try { if (mediaClient.checkFileExistsUsingSha(SHA1).blockingGet()) { @@ -98,6 +95,5 @@ class CustomSelectorUtils { } result } - } } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.kt b/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.kt index f526677837..ed0487c6aa 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.kt +++ b/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.kt @@ -12,8 +12,10 @@ object DialogUtil { * @param activity the activity * @param dialog the dialog to be shown */ - private fun showSafely(activity: Activity?, dialog: AlertDialog?):AlertDialog? { - + private fun showSafely( + activity: Activity?, + dialog: AlertDialog?, + ): AlertDialog? { if (activity == null || dialog == null) { Timber.d("Show called with null activity / dialog. Ignoring.") return null @@ -37,18 +39,17 @@ object DialogUtil { title: String?, message: String?, onPositiveBtnClick: Runnable?, - onNegativeBtnClick: Runnable? - ): AlertDialog? { - return createAndShowDialogSafely( + onNegativeBtnClick: Runnable?, + ): AlertDialog? = + createAndShowDialogSafely( activity = activity, title = title, message = message, positiveButtonText = activity.getString(R.string.yes), negativeButtonText = activity.getString(R.string.no), onPositiveBtnClick = onPositiveBtnClick, - onNegativeBtnClick = onNegativeBtnClick + onNegativeBtnClick = onNegativeBtnClick, ) - } @JvmStatic fun showAlertDialog( @@ -59,17 +60,16 @@ object DialogUtil { negativeButtonText: String?, onPositiveBtnClick: Runnable?, onNegativeBtnClick: Runnable?, - ): AlertDialog? { - return createAndShowDialogSafely( + ): AlertDialog? = + createAndShowDialogSafely( activity = activity, title = title, message = message, positiveButtonText = positiveButtonText, negativeButtonText = negativeButtonText, onPositiveBtnClick = onPositiveBtnClick, - onNegativeBtnClick = onNegativeBtnClick + onNegativeBtnClick = onNegativeBtnClick, ) - } @JvmStatic fun showAlertDialog( @@ -79,9 +79,9 @@ object DialogUtil { onPositiveBtnClick: Runnable?, onNegativeBtnClick: Runnable?, customView: View?, - cancelable: Boolean - ): AlertDialog? { - return createAndShowDialogSafely( + cancelable: Boolean, + ): AlertDialog? = + createAndShowDialogSafely( activity = activity, title = title, message = message, @@ -90,9 +90,8 @@ object DialogUtil { onPositiveBtnClick = onPositiveBtnClick, onNegativeBtnClick = onNegativeBtnClick, customView = customView, - cancelable = cancelable + cancelable = cancelable, ) - } @JvmStatic fun showAlertDialog( @@ -104,9 +103,9 @@ object DialogUtil { onPositiveBtnClick: Runnable?, onNegativeBtnClick: Runnable?, customView: View?, - cancelable: Boolean - ): AlertDialog? { - return createAndShowDialogSafely( + cancelable: Boolean, + ): AlertDialog? = + createAndShowDialogSafely( activity = activity, title = title, message = message, @@ -115,9 +114,8 @@ object DialogUtil { onPositiveBtnClick = onPositiveBtnClick, onNegativeBtnClick = onNegativeBtnClick, customView = customView, - cancelable = cancelable + cancelable = cancelable, ) - } @JvmStatic fun showAlertDialog( @@ -126,17 +124,16 @@ object DialogUtil { message: String?, positiveButtonText: String?, onPositiveBtnClick: Runnable?, - cancelable: Boolean - ): AlertDialog? { - return createAndShowDialogSafely( + cancelable: Boolean, + ): AlertDialog? = + createAndShowDialogSafely( activity = activity, title = title, message = message, positiveButtonText = positiveButtonText, onPositiveBtnClick = onPositiveBtnClick, - cancelable = cancelable + cancelable = cancelable, ) - } /** * show a dialog @@ -159,9 +156,8 @@ object DialogUtil { onPositiveBtnClick: Runnable? = null, onNegativeBtnClick: Runnable? = null, customView: View? = null, - cancelable: Boolean = true + cancelable: Boolean = true, ): AlertDialog? { - /* If the custom view already has a parent, there is already a dialog showing with the view * This happens for on resume - return to avoid creating a second dialog - the first one * will still show @@ -170,17 +166,22 @@ object DialogUtil { return null } - return showSafely(activity, AlertDialog.Builder(activity).apply { - title?.also{setTitle(title)} - message?.also{setMessage(message)} - setView(customView) - setCancelable(cancelable) - positiveButtonText?.let { - setPositiveButton(it) { _, _ -> onPositiveBtnClick?.run() } - } - negativeButtonText?.let { - setNegativeButton(it) { _, _ -> onNegativeBtnClick?.run() } - } - }.create()) + return showSafely( + activity, + AlertDialog + .Builder(activity) + .apply { + title?.also { setTitle(title) } + message?.also { setMessage(message) } + setView(customView) + setCancelable(cancelable) + positiveButtonText?.let { + setPositiveButton(it) { _, _ -> onPositiveBtnClick?.run() } + } + negativeButtonText?.let { + setNegativeButton(it) { _, _ -> onNegativeBtnClick?.run() } + } + }.create(), + ) } } diff --git a/app/src/main/java/fr/free/nrw/commons/utils/DownloadUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/DownloadUtils.kt index 757dbc6cd6..08c030e337 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/DownloadUtils.kt +++ b/app/src/main/java/fr/free/nrw/commons/utils/DownloadUtils.kt @@ -1,6 +1,5 @@ package fr.free.nrw.commons.utils -import android.Manifest.permission import android.app.Activity import android.app.DownloadManager import android.content.Context @@ -19,7 +18,10 @@ object DownloadUtils { * @param m Media file to download */ @JvmStatic - fun downloadMedia(activity: Activity?, m: Media) { + fun downloadMedia( + activity: Activity?, + m: Media, + ) { val imageUrl = m.imageUrl var fileName = m.filename if (imageUrl == null || fileName == null || activity == null) { @@ -29,32 +31,37 @@ object DownloadUtils { // Strip 'File:' from beginning of filename, we really shouldn't store it fileName = fileName.substringAfter("File:") val imageUri = Uri.parse(imageUrl) - val req = DownloadManager.Request(imageUri).apply { - setTitle(m.displayTitle) - setDescription(activity.getString(R.string.app_name)) - setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName) - allowScanningByMediaScanner() - setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) - } + val req = + DownloadManager.Request(imageUri).apply { + setTitle(m.displayTitle) + setDescription(activity.getString(R.string.app_name)) + setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName) + allowScanningByMediaScanner() + setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) + } PermissionUtils.checkPermissionsAndPerformAction( activity, { enqueueRequest(activity, req) }, { - Toast.makeText( - activity, - R.string.download_failed_we_cannot_download_the_file_without_storage_permission, - Toast.LENGTH_SHORT - ).show() + Toast + .makeText( + activity, + R.string.download_failed_we_cannot_download_the_file_without_storage_permission, + Toast.LENGTH_SHORT, + ).show() }, R.string.storage_permission, R.string.write_storage_permission_rationale, - *PermissionUtils.PERMISSIONS_STORAGE - ) + *PermissionUtils.PERMISSIONS_STORAGE, + ) } - private fun enqueueRequest(activity: Activity, req: DownloadManager.Request) { + private fun enqueueRequest( + activity: Activity, + req: DownloadManager.Request, + ) { val systemService = activity.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager systemService?.enqueue(req) } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/model/ConnectionType.kt b/app/src/main/java/fr/free/nrw/commons/utils/model/ConnectionType.kt index f7ddfa86e4..682fa19181 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/model/ConnectionType.kt +++ b/app/src/main/java/fr/free/nrw/commons/utils/model/ConnectionType.kt @@ -1,9 +1,14 @@ package fr.free.nrw.commons.utils.model -enum class ConnectionType(private val text: String) { - WIFI_NETWORK("wifi"), CELLULAR_4G("cellular-4g"), CELLULAR_3G("cellular-3g"), CELLULAR("cellular"), NO_INTERNET("no-internet"); +enum class ConnectionType( + private val text: String, +) { + WIFI_NETWORK("wifi"), + CELLULAR_4G("cellular-4g"), + CELLULAR_3G("cellular-3g"), + CELLULAR("cellular"), + NO_INTERNET("no-internet"), + ; - override fun toString(): String { - return text - } -} \ No newline at end of file + override fun toString(): String = text +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/model/NetworkConnectionType.kt b/app/src/main/java/fr/free/nrw/commons/utils/model/NetworkConnectionType.kt index d989932705..bf0c92128f 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/model/NetworkConnectionType.kt +++ b/app/src/main/java/fr/free/nrw/commons/utils/model/NetworkConnectionType.kt @@ -1,5 +1,9 @@ package fr.free.nrw.commons.utils.model enum class NetworkConnectionType { - WIFI, TWO_G, THREE_G, FOUR_G, UNKNOWN -} \ No newline at end of file + WIFI, + TWO_G, + THREE_G, + FOUR_G, + UNKNOWN, +} diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/CommonsServiceFactory.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/CommonsServiceFactory.kt index ca8cef4238..39dbf0cadc 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/CommonsServiceFactory.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/CommonsServiceFactory.kt @@ -5,20 +5,27 @@ import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory -class CommonsServiceFactory(private val okHttpClient: OkHttpClient) { - +class CommonsServiceFactory( + private val okHttpClient: OkHttpClient, +) { private val builder: Retrofit.Builder by lazy { // All instances of retrofit share this configuration, but create it lazily - Retrofit.Builder().client(okHttpClient) + Retrofit + .Builder() + .client(okHttpClient) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create(GsonUtil.getDefaultGson())) } private val retrofitCache: MutableMap = mutableMapOf() - fun create(baseUrl: String, service: Class): T = retrofitCache.getOrPut(baseUrl) { - // Cache instances of retrofit based on API backend - builder.baseUrl(baseUrl).build() - }.create(service) - -} \ No newline at end of file + fun create( + baseUrl: String, + service: Class, + ): T = + retrofitCache + .getOrPut(baseUrl) { + // Cache instances of retrofit based on API backend + builder.baseUrl(baseUrl).build() + }.create(service) +} diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikiBaseClient.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/WikiBaseClient.kt index d34a564a24..773c835907 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikiBaseClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikiBaseClient.kt @@ -16,65 +16,84 @@ import javax.inject.Singleton * Wikibase Client for calling WikiBase APIs */ @Singleton -class WikiBaseClient @Inject constructor( - private val wikiBaseInterface: WikiBaseInterface, - @param:Named(NetworkingModule.NAMED_COMMONS_CSRF) private val csrfTokenClient: CsrfTokenClient -) { - fun postEditEntity(fileEntityId: String?, data: String?): Observable { - return csrfToken().switchMap { editToken -> - wikiBaseInterface.postEditEntity(fileEntityId!!, editToken, data!!) - .map { response: MwPostResponse -> response.successVal == 1 } - } - } +class WikiBaseClient + @Inject + constructor( + private val wikiBaseInterface: WikiBaseInterface, + @param:Named(NetworkingModule.NAMED_COMMONS_CSRF) private val csrfTokenClient: CsrfTokenClient, + ) { + fun postEditEntity( + fileEntityId: String?, + data: String?, + ): Observable = + csrfToken().switchMap { editToken -> + wikiBaseInterface + .postEditEntity(fileEntityId!!, editToken, data!!) + .map { response: MwPostResponse -> response.successVal == 1 } + } - /** - * Makes the server call for posting new depicts - * - * @param filename name of the file - * @param data data of the depicts to be uploaded - * @return Observable - */ - fun postEditEntityByFilename(filename: String?, data: String?): Observable { - return csrfToken().switchMap { editToken -> - wikiBaseInterface.postEditEntityByFilename(filename!!, editToken, data!!) - .map { response: MwPostResponse -> response.successVal == 1 } - } - } + /** + * Makes the server call for posting new depicts + * + * @param filename name of the file + * @param data data of the depicts to be uploaded + * @return Observable + */ + fun postEditEntityByFilename( + filename: String?, + data: String?, + ): Observable = + csrfToken().switchMap { editToken -> + wikiBaseInterface + .postEditEntityByFilename(filename!!, editToken, data!!) + .map { response: MwPostResponse -> response.successVal == 1 } + } - fun getClaimIdsByProperty(fileEntityId: String, property: String ): Observable> { - return wikiBaseInterface.getClaimsByProperty(fileEntityId, property).map { claimsResponse -> - claimsResponse.claims[property]?.mapNotNull { claim -> claim.id } ?: emptyList() - } - } + fun getClaimIdsByProperty( + fileEntityId: String, + property: String, + ): Observable> = + wikiBaseInterface.getClaimsByProperty(fileEntityId, property).map { claimsResponse -> + claimsResponse.claims[property]?.mapNotNull { claim -> claim.id } ?: emptyList() + } - fun postDeleteClaims(entityId: String, data: String?): Observable { - return csrfToken().switchMap { editToken -> - wikiBaseInterface.postDeleteClaims(editToken, entityId, data!!) - .map { response: MwPostResponse -> response.successVal == 1 } - } - } + fun postDeleteClaims( + entityId: String, + data: String?, + ): Observable = + csrfToken().switchMap { editToken -> + wikiBaseInterface + .postDeleteClaims(editToken, entityId, data!!) + .map { response: MwPostResponse -> response.successVal == 1 } + } - fun getFileEntityId(uploadResult: UploadResult): Observable { - return wikiBaseInterface.getFileEntityId(uploadResult.createCanonicalFileName()) - .map { response: MwQueryResponse -> response.query()!!.pages()!![0].pageId().toLong() } - } + fun getFileEntityId(uploadResult: UploadResult): Observable = + wikiBaseInterface + .getFileEntityId(uploadResult.createCanonicalFileName()) + .map { response: MwQueryResponse -> + response + .query()!! + .pages()!![0] + .pageId() + .toLong() + } - fun addLabelsToWikidata( - fileEntityId: Long, - languageCode: String?, - captionValue: String? - ): Observable { - return csrfToken().switchMap { editToken -> - wikiBaseInterface.addLabelstoWikidata( - PAGE_ID_PREFIX + fileEntityId, - editToken, - languageCode, - captionValue - ) - } - } + fun addLabelsToWikidata( + fileEntityId: Long, + languageCode: String?, + captionValue: String?, + ): Observable = + csrfToken().switchMap { editToken -> + wikiBaseInterface.addLabelstoWikidata( + PAGE_ID_PREFIX + fileEntityId, + editToken, + languageCode, + captionValue, + ) + } - private fun csrfToken(): Observable = Observable.fromCallable { - csrfTokenClient.getTokenBlocking() + private fun csrfToken(): Observable = + Observable.fromCallable { + csrfTokenClient.getTokenBlocking() + } } -} diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataClient.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataClient.kt index d9d6dc33d8..3b3ea34015 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataClient.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataClient.kt @@ -1,32 +1,35 @@ package fr.free.nrw.commons.wikidata import com.google.gson.Gson -import fr.free.nrw.commons.wikidata.model.Statement_partial +import fr.free.nrw.commons.wikidata.model.StatementPartial import fr.free.nrw.commons.wikidata.model.WbCreateClaimResponse -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse import io.reactivex.Observable import javax.inject.Inject import javax.inject.Singleton @Singleton -class WikidataClient @Inject constructor( - private val wikidataInterface: WikidataInterface, - private val gson: Gson -) { - /** - * Create wikidata claim to add P18 value - * - * @return revisionID of the edit - */ - fun setClaim(claim: Statement_partial?, tags: String?): Observable { - return csrfToken().flatMap { csrfToken: String? -> - wikidataInterface.postSetClaim(gson.toJson(claim), tags!!, csrfToken!!) - }.map { mwPostResponse: WbCreateClaimResponse -> mwPostResponse.pageinfo.lastrevid } - } +class WikidataClient + @Inject + constructor( + private val wikidataInterface: WikidataInterface, + private val gson: Gson, + ) { + /** + * Create wikidata claim to add P18 value + * + * @return revisionID of the edit + */ + fun setClaim( + claim: StatementPartial?, + tags: String?, + ): Observable = + csrfToken() + .flatMap { csrfToken: String? -> + wikidataInterface.postSetClaim(gson.toJson(claim), tags!!, csrfToken!!) + }.map { mwPostResponse: WbCreateClaimResponse -> mwPostResponse.pageinfo.lastrevid } - /** - * Get csrf token for wikidata edit - */ - private fun csrfToken(): Observable = - wikidataInterface.getCsrfToken().map { it.query()?.csrfToken() } -} + /** + * Get csrf token for wikidata edit + */ + private fun csrfToken(): Observable = wikidataInterface.getCsrfToken().map { it.query()?.csrfToken() } + } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataDisambiguationItems.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataDisambiguationItems.kt index fc36de81f6..7160df2ed0 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataDisambiguationItems.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataDisambiguationItems.kt @@ -1,8 +1,12 @@ package fr.free.nrw.commons.wikidata - -enum class WikidataDisambiguationItems(val id: String) { - DISAMBIGUATION_PAGE("Q4167410"), INTERNAL_ITEM("Q17442446"), CATEGORY("Q4167836"); +enum class WikidataDisambiguationItems( + val id: String, +) { + DISAMBIGUATION_PAGE("Q4167410"), + INTERNAL_ITEM("Q17442446"), + CATEGORY("Q4167836"), + ; companion object { fun isDisambiguationItem(ids: List) = diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java index 8e298cc9a6..21567f5e44 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java @@ -19,8 +19,8 @@ import fr.free.nrw.commons.wikidata.model.DataValue.ValueString; import fr.free.nrw.commons.wikidata.model.EditClaim; import fr.free.nrw.commons.wikidata.model.RemoveClaim; -import fr.free.nrw.commons.wikidata.model.Snak_partial; -import fr.free.nrw.commons.wikidata.model.Statement_partial; +import fr.free.nrw.commons.wikidata.model.SnakPartial; +import fr.free.nrw.commons.wikidata.model.StatementPartial; import fr.free.nrw.commons.wikidata.model.WikiBaseMonolingualTextValue; import fr.free.nrw.commons.wikidata.mwapi.MwPostResponse; import io.reactivex.Observable; @@ -198,19 +198,19 @@ public Long createClaim(@Nullable final WikidataPlace wikidataPlace, final Strin public Long addImageAndMediaLegends(final WikidataItem wikidataItem, final String fileName, final Map captions) { - final Snak_partial p18 = new Snak_partial("value", + final SnakPartial p18 = new SnakPartial("value", WikidataProperties.IMAGE.getPropertyName(), new ValueString(fileName.replace("File:", ""))); - final List snaks = new ArrayList<>(); + final List snaks = new ArrayList<>(); for (final Map.Entry entry : captions.entrySet()) { - snaks.add(new Snak_partial("value", + snaks.add(new SnakPartial("value", WikidataProperties.MEDIA_LEGENDS.getPropertyName(), new DataValue.MonoLingualText( new WikiBaseMonolingualTextValue(entry.getValue(), entry.getKey())))); } final String id = wikidataItem.getId() + "$" + UUID.randomUUID().toString(); - final Statement_partial claim = new Statement_partial(p18, "statement", "normal", id, + final StatementPartial claim = new StatementPartial(p18, "statement", "normal", id, Collections.singletonMap(WikidataProperties.MEDIA_LEGENDS.getPropertyName(), snaks), Arrays.asList(WikidataProperties.MEDIA_LEGENDS.getPropertyName())); diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataInterface.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataInterface.kt index 35b3e15935..7a56537ae8 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataInterface.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataInterface.kt @@ -26,6 +26,6 @@ interface WikidataInterface { fun postSetClaim( @Field("claim") request: String, @Field("tags") tags: String, - @Field("token") token: String + @Field("token") token: String, ): Observable } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataProperties.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataProperties.kt index dbeb0dc766..5e82c3c809 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataProperties.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataProperties.kt @@ -2,10 +2,12 @@ package fr.free.nrw.commons.wikidata import fr.free.nrw.commons.BuildConfig -enum class WikidataProperties(val propertyName: String) { +enum class WikidataProperties( + val propertyName: String, +) { IMAGE("P18"), DEPICTS(BuildConfig.DEPICTS_PROPERTY), COMMONS_CATEGORY("P373"), INSTANCE_OF("P31"), - MEDIA_LEGENDS("P2096"); + MEDIA_LEGENDS("P2096"), } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/cookies/CommonsCookieJar.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/cookies/CommonsCookieJar.kt index fbc88f55a3..6e0c0e9016 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/cookies/CommonsCookieJar.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/cookies/CommonsCookieJar.kt @@ -4,7 +4,9 @@ import okhttp3.Cookie import okhttp3.CookieJar import okhttp3.HttpUrl -class CommonsCookieJar(private val cookieStorage: CommonsCookieStorage) : CookieJar { +class CommonsCookieJar( + private val cookieStorage: CommonsCookieStorage, +) : CookieJar { override fun loadForRequest(url: HttpUrl): List { val cookieList = mutableListOf() val domain: String = url.toUri().getAuthority() @@ -21,7 +23,10 @@ class CommonsCookieJar(private val cookieStorage: CommonsCookieStorage) : Cookie return cookieList } - override fun saveFromResponse(url: HttpUrl, cookies: List) { + override fun saveFromResponse( + url: HttpUrl, + cookies: List, + ) { if (cookies.isEmpty()) { return } @@ -66,7 +71,9 @@ class CommonsCookieJar(private val cookieStorage: CommonsCookieStorage) : Cookie } private fun buildCookieList( - outList: MutableList, inList: MutableList, prefix: String? + outList: MutableList, + inList: MutableList, + prefix: String?, ) { var cookieJarModified = false @@ -90,14 +97,11 @@ class CommonsCookieJar(private val cookieStorage: CommonsCookieStorage) : Cookie } } - private fun Cookie.expiredOrDeleted(): Boolean = - expiresAt < System.currentTimeMillis() || "deleted" == value + private fun Cookie.expiredOrDeleted(): Boolean = expiresAt < System.currentTimeMillis() || "deleted" == value - private fun Cookie.domainSpec(url: HttpUrl): String = - domain.ifEmpty { url.toUri().getAuthority() } + private fun Cookie.domainSpec(url: HttpUrl): String = domain.ifEmpty { url.toUri().getAuthority() } fun clear() { cookieStorage.clear() } - } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/cookies/CommonsCookieStorage.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/cookies/CommonsCookieStorage.kt index 211b2a5013..2b0e780704 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/cookies/CommonsCookieStorage.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/cookies/CommonsCookieStorage.kt @@ -5,27 +5,32 @@ import com.google.gson.TypeAdapter import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonWriter import fr.free.nrw.commons.kvstore.JsonKvStore +import fr.free.nrw.commons.wikidata.model.WikiSite import okhttp3.Cookie import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import fr.free.nrw.commons.wikidata.model.WikiSite private const val COOKIE_STORE = "cookie_store" -class CommonsCookieStorage(private val preferences: JsonKvStore? = null) { - private val gson = GsonBuilder().registerTypeAdapter( - CommonsCookieStorage::class.java, - CookieStorageTypeAdapter() - ).create() +class CommonsCookieStorage( + private val preferences: JsonKvStore? = null, +) { + private val gson = + GsonBuilder() + .registerTypeAdapter( + CommonsCookieStorage::class.java, + CookieStorageTypeAdapter(), + ).create() private val cookieMap: MutableMap> = mutableMapOf() - val domains : Set get() = cookieMap.keys.toSet() + val domains: Set get() = cookieMap.keys.toSet() - operator fun set(domainSpec: String, cookies: MutableList) = - cookieMap.put(domainSpec, cookies.toList()) + operator fun set( + domainSpec: String, + cookies: MutableList, + ) = cookieMap.put(domainSpec, cookies.toList()) - operator fun get(domainSpec: String): MutableList = - cookieMap[domainSpec]?.toMutableList() ?: mutableListOf() + operator fun get(domainSpec: String): MutableList = cookieMap[domainSpec]?.toMutableList() ?: mutableListOf() fun clear() { cookieMap.clear() @@ -41,22 +46,24 @@ class CommonsCookieStorage(private val preferences: JsonKvStore? = null) { } } - fun save() = - preferences!!.putString(COOKIE_STORE, gson.toJson(this)) + fun save() = preferences!!.putString(COOKIE_STORE, gson.toJson(this)) - fun contains(domainSpec: String): Boolean = - cookieMap.containsKey(domainSpec) + fun contains(domainSpec: String): Boolean = cookieMap.containsKey(domainSpec) companion object { - fun from(map: Map>) = CommonsCookieStorage().apply { - cookieMap.clear() - cookieMap.putAll(map) - } + fun from(map: Map>) = + CommonsCookieStorage().apply { + cookieMap.clear() + cookieMap.putAll(map) + } } } private class CookieStorageTypeAdapter : TypeAdapter() { - override fun write(out: JsonWriter, value: CommonsCookieStorage) { + override fun write( + out: JsonWriter, + value: CommonsCookieStorage, + ) { out.beginObject() value.domains.forEach { domain -> out.name(domain).beginArray() @@ -90,4 +97,4 @@ private class CookieStorageTypeAdapter : TypeAdapter() { endArray() return list } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/AddEditTagResponse.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/AddEditTagResponse.kt index 735f9a344f..7d7ecdab70 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/AddEditTagResponse.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/AddEditTagResponse.kt @@ -10,4 +10,4 @@ class AddEditTagResponse { @SerializedName("tag") @Expose var tag: List? = null -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/BaseModel.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/BaseModel.kt index 88c5aa5e8e..951dd860c8 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/BaseModel.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/BaseModel.kt @@ -5,15 +5,9 @@ import org.apache.commons.lang3.builder.HashCodeBuilder import org.apache.commons.lang3.builder.ToStringBuilder abstract class BaseModel { - override fun toString(): String { - return ToStringBuilder.reflectionToString(this) - } + override fun toString(): String = ToStringBuilder.reflectionToString(this) - override fun hashCode(): Int { - return HashCodeBuilder.reflectionHashCode(this) - } + override fun hashCode(): Int = HashCodeBuilder.reflectionHashCode(this) - override fun equals(other: Any?): Boolean { - return EqualsBuilder.reflectionEquals(this, other) - } + override fun equals(other: Any?): Boolean = EqualsBuilder.reflectionEquals(this, other) } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/DataValue.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/DataValue.kt index 4790822be0..88453a4ac8 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/DataValue.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/DataValue.kt @@ -2,16 +2,19 @@ package fr.free.nrw.commons.wikidata.model import fr.free.nrw.commons.wikidata.json.RuntimeTypeAdapterFactory -sealed class DataValue(val type: String) { +sealed class DataValue( + val type: String, +) { companion object { @JvmStatic val polymorphicTypeAdapter = - RuntimeTypeAdapterFactory.of(DataValue::class.java, DataValue::type.name) + RuntimeTypeAdapterFactory + .of(DataValue::class.java, DataValue::type.name) .registerSubtype(EntityId::class.java, EntityId.TYPE) .registerSubtype(ValueString::class.java, ValueString.TYPE) - .registerSubtype(GlobeCoordinate_partial::class.java, GlobeCoordinate_partial.TYPE) - .registerSubtype(Time_partial::class.java, Time_partial.TYPE) - .registerSubtype(Quantity_partial::class.java, Quantity_partial.TYPE) + .registerSubtype(GlobeCoordinatePartial::class.java, GlobeCoordinatePartial.TYPE) + .registerSubtype(TimePartial::class.java, TimePartial.TYPE) + .registerSubtype(QuantityPartial::class.java, QuantityPartial.TYPE) .registerSubtype(MonoLingualText::class.java, MonoLingualText.TYPE) } @@ -22,7 +25,9 @@ sealed class DataValue(val type: String) { // }, // "type": "wikibase-entityid" // } - data class EntityId(val value: WikiBaseEntityValue) : DataValue(TYPE) { + data class EntityId( + val value: WikiBaseEntityValue, + ) : DataValue(TYPE) { companion object { const val TYPE = "wikibase-entityid" } @@ -32,7 +37,9 @@ sealed class DataValue(val type: String) { // "value": "SomePicture.jpg", // "type": "string" // } - data class ValueString(val value: String) : DataValue(TYPE) { + data class ValueString( + val value: String, + ) : DataValue(TYPE) { companion object { const val TYPE = "string" } @@ -47,7 +54,7 @@ sealed class DataValue(val type: String) { // }, // "type": "globecoordinate" // } - class GlobeCoordinate_partial() : DataValue(TYPE) { + class GlobeCoordinatePartial : DataValue(TYPE) { companion object { const val TYPE = "globecoordinate" } @@ -63,7 +70,7 @@ sealed class DataValue(val type: String) { // }, // "type": "time" // } - class Time_partial() : DataValue(TYPE) { + class TimePartial : DataValue(TYPE) { companion object { const val TYPE = "time" } @@ -75,7 +82,7 @@ sealed class DataValue(val type: String) { // "unit": "http://www.wikidata.org/entity/Q828224" // } // } - class Quantity_partial() : DataValue(TYPE) { + class QuantityPartial : DataValue(TYPE) { companion object { const val TYPE = "quantity" } @@ -87,7 +94,9 @@ sealed class DataValue(val type: String) { // "language": "ko" // } // } - class MonoLingualText(val value: WikiBaseMonolingualTextValue) : DataValue(TYPE) { + class MonoLingualText( + val value: WikiBaseMonolingualTextValue, + ) : DataValue(TYPE) { companion object { const val TYPE = "monolingualtext" } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/DepictSearchItem.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/DepictSearchItem.kt index 596426f507..b1d9d45ad4 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/DepictSearchItem.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/DepictSearchItem.kt @@ -23,5 +23,5 @@ class DepictSearchItem( val pageid: String, val url: String, val label: String, - val description: String? + val description: String?, ) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/EditClaim.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/EditClaim.kt index 5d2ab7e389..5919fff103 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/EditClaim.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/EditClaim.kt @@ -1,30 +1,34 @@ package fr.free.nrw.commons.wikidata.model - -data class EditClaim(val claims: List) { - +data class EditClaim( + val claims: List, +) { companion object { @JvmStatic - fun from(entityIds: List, propertyName: String): EditClaim { - - val list = mutableListOf() + fun from( + entityIds: List, + propertyName: String, + ): EditClaim { + val list = mutableListOf() entityIds.forEach { list.add( - Statement_partial( - mainSnak = Snak_partial( - snakType = "value", - property = propertyName, - dataValue = DataValue.EntityId( - WikiBaseEntityValue( - entityType = "item", - id = it, - numericId = it.removePrefix("Q").toLong() - ) - ) - ), + StatementPartial( + mainSnak = + SnakPartial( + snakType = "value", + property = propertyName, + dataValue = + DataValue.EntityId( + WikiBaseEntityValue( + entityType = "item", + id = it, + numericId = it.removePrefix("Q").toLong(), + ), + ), + ), type = "statement", - rank = "preferred" - ) + rank = "preferred", + ), ) } return EditClaim(list) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/EditTag.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/EditTag.kt index 3ff8b7e28a..4d63f4f525 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/EditTag.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/EditTag.kt @@ -6,4 +6,10 @@ import com.google.gson.annotations.SerializedName /** * Tag class used when adding wikidata edit tag */ -class EditTag(@field:Expose @field:SerializedName("revid") val revid: Int, @field:Expose @field:SerializedName("status") val status: String, @field:Expose @field:SerializedName("actionlogid") val actionlogid: Int, @field:Expose @field:SerializedName("added") val added: List, @field:Expose @field:SerializedName("removed") val removed: List) \ No newline at end of file +class EditTag( + @field:Expose @field:SerializedName("revid") val revid: Int, + @field:Expose @field:SerializedName("status") val status: String, + @field:Expose @field:SerializedName("actionlogid") val actionlogid: Int, + @field:Expose @field:SerializedName("added") val added: List, + @field:Expose @field:SerializedName("removed") val removed: List, +) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/Entities.java b/app/src/main/java/fr/free/nrw/commons/wikidata/model/Entities.java index ea9ec6c2a5..9dab836cf8 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/Entities.java +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/Entities.java @@ -44,7 +44,7 @@ public static class Entity { @Nullable private Map labels; @Nullable private Map descriptions; @Nullable private Map sitelinks; - @Nullable @SerializedName(value = "statements", alternate = "claims") private Map> statements; + @Nullable @SerializedName(value = "statements", alternate = "claims") private Map> statements; @Nullable private String missing; @NonNull public String id() { @@ -64,7 +64,7 @@ public static class Entity { } @Nullable - public Map> getStatements() { + public Map> getStatements() { return statements; } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/EnumCodeMap.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/EnumCodeMap.kt index 3f804f333e..4b358cc498 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/EnumCodeMap.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/EnumCodeMap.kt @@ -2,16 +2,16 @@ package fr.free.nrw.commons.wikidata.model import android.util.SparseArray -class EnumCodeMap(enumeration: Class) where T : Enum, T : EnumCode { +class EnumCodeMap( + enumeration: Class, +) where T : Enum, T : EnumCode { private val map: SparseArray init { map = codeToEnumMap(enumeration) } - operator fun get(code: Int): T { - return map.get(code) ?: throw IllegalArgumentException("code=$code") - } + operator fun get(code: Int): T = map.get(code) ?: throw IllegalArgumentException("code=$code") private fun codeToEnumMap(enumeration: Class): SparseArray { val ret = SparseArray() @@ -21,7 +21,5 @@ class EnumCodeMap(enumeration: Class) where T : Enum, T : EnumCode { return ret } - fun size(): Int { - return map.size() - } + fun size(): Int = map.size() } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/GetWikidataEditCountResponse.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/GetWikidataEditCountResponse.kt index 973ac25272..44e715151e 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/GetWikidataEditCountResponse.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/GetWikidataEditCountResponse.kt @@ -2,4 +2,6 @@ package fr.free.nrw.commons.wikidata.model import com.google.gson.annotations.SerializedName -class GetWikidataEditCountResponse(@field:SerializedName("edits") val wikidataEditCount: Int) \ No newline at end of file +class GetWikidataEditCountResponse( + @field:SerializedName("edits") val wikidataEditCount: Int, +) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/PageInfo.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/PageInfo.kt index 97e6cb4c21..1b580d3430 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/PageInfo.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/PageInfo.kt @@ -6,4 +6,6 @@ import com.google.gson.annotations.SerializedName /** * PageInfo model class with last revision id of the edited Wikidata entity */ -class PageInfo(@field:Expose @field:SerializedName("lastrevid") val lastrevid: Long) \ No newline at end of file +class PageInfo( + @field:Expose @field:SerializedName("lastrevid") val lastrevid: Long, +) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/RemoveClaim.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/RemoveClaim.kt index 1e553ae3ee..02a88c4e22 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/RemoveClaim.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/RemoveClaim.kt @@ -1,6 +1,8 @@ package fr.free.nrw.commons.wikidata.model -data class RemoveClaim(val claims: List) { +data class RemoveClaim( + val claims: List, +) { companion object { @JvmStatic fun from(claimIds: List): RemoveClaim { @@ -8,7 +10,7 @@ data class RemoveClaim(val claims: List) { claimIds.forEach { claimsToRemove.add( - ClaimRemoveRequest(id = it, remove = "") + ClaimRemoveRequest(id = it, remove = ""), ) } @@ -17,4 +19,7 @@ data class RemoveClaim(val claims: List) { } } -data class ClaimRemoveRequest(val id: String, val remove: String) \ No newline at end of file +data class ClaimRemoveRequest( + val id: String, + val remove: String, +) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/Snak_partial.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/SnakPartial.kt similarity index 84% rename from app/src/main/java/fr/free/nrw/commons/wikidata/model/Snak_partial.kt rename to app/src/main/java/fr/free/nrw/commons/wikidata/model/SnakPartial.kt index 9ffe054346..576fb2fa94 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/Snak_partial.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/SnakPartial.kt @@ -15,8 +15,8 @@ import com.google.gson.annotations.SerializedName }, "datatype": "wikibase-item", }*/ -data class Snak_partial( +data class SnakPartial( @SerializedName("snaktype") val snakType: String, val property: String, - @SerializedName("datavalue") val dataValue: DataValue + @SerializedName("datavalue") val dataValue: DataValue, ) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/Statement_partial.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/StatementPartial.kt similarity index 75% rename from app/src/main/java/fr/free/nrw/commons/wikidata/model/Statement_partial.kt rename to app/src/main/java/fr/free/nrw/commons/wikidata/model/StatementPartial.kt index 5bfb33e192..4d357f85ca 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/Statement_partial.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/StatementPartial.kt @@ -18,11 +18,11 @@ import com.google.gson.annotations.SerializedName } ] }*/ -data class Statement_partial( - @SerializedName("mainsnak") val mainSnak: Snak_partial, +data class StatementPartial( + @SerializedName("mainsnak") val mainSnak: SnakPartial, val type: String, val rank: String, val id: String? = null, - val qualifiers: Map> = mapOf(), - @SerializedName("qualifiers-order") val qualifiersOrder: List = listOf() + val qualifiers: Map> = mapOf(), + @SerializedName("qualifiers-order") val qualifiersOrder: List = listOf(), ) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/WbCreateClaimResponse.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/WbCreateClaimResponse.kt index 425931b474..2188939464 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/WbCreateClaimResponse.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/WbCreateClaimResponse.kt @@ -6,4 +6,7 @@ import com.google.gson.annotations.SerializedName /** * Wikidata create claim response model class */ -class WbCreateClaimResponse(@field:Expose @field:SerializedName("pageinfo") val pageinfo: PageInfo, @field:Expose @field:SerializedName("success") val success: Int) \ No newline at end of file +class WbCreateClaimResponse( + @field:Expose @field:SerializedName("pageinfo") val pageinfo: PageInfo, + @field:Expose @field:SerializedName("success") val success: Int, +) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/WikiBaseEntityValue.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/WikiBaseEntityValue.kt index f35b525614..d672b0840f 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/WikiBaseEntityValue.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/WikiBaseEntityValue.kt @@ -10,5 +10,5 @@ import com.google.gson.annotations.SerializedName data class WikiBaseEntityValue( @SerializedName("entity-type") val entityType: String, val id: String, - val numericId: Long + val numericId: Long, ) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/model/WikiBaseMonolingualTextValue.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/model/WikiBaseMonolingualTextValue.kt index 6b58c17805..84a78e646b 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/model/WikiBaseMonolingualTextValue.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/model/WikiBaseMonolingualTextValue.kt @@ -1,7 +1,5 @@ package fr.free.nrw.commons.wikidata.model -import com.google.gson.annotations.SerializedName - /*"value": { "type": "monolingualtext", "value": { @@ -10,4 +8,7 @@ import com.google.gson.annotations.SerializedName } }*/ -data class WikiBaseMonolingualTextValue(val text: String, val language: String) \ No newline at end of file +data class WikiBaseMonolingualTextValue( + val text: String, + val language: String, +) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/AboutActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/AboutActivityUnitTests.kt index 6c033d8c32..56549fa1c7 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/AboutActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/AboutActivityUnitTests.kt @@ -18,11 +18,9 @@ import org.robolectric.fakes.RoboMenu import org.robolectric.fakes.RoboMenuItem import org.robolectric.shadows.ShadowActivity - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class AboutActivityUnitTests { - private lateinit var activity: AboutActivity private lateinit var context: Context @@ -116,5 +114,4 @@ class AboutActivityUnitTests { fun testOnSupportNavigateUp() { activity.onSupportNavigateUp() } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/FakeContextWrapper.kt b/app/src/test/kotlin/fr/free/nrw/commons/FakeContextWrapper.kt index 429df1dcc2..bf7d6f6460 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/FakeContextWrapper.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/FakeContextWrapper.kt @@ -8,16 +8,18 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations -class FakeContextWrapper(base: Context?) : ContextWrapper(base) { - +class FakeContextWrapper( + base: Context?, +) : ContextWrapper(base) { @Mock private lateinit var mMockAccountManager: AccountManager - override fun getSystemService(name: String): Any { - return if (ACCOUNT_SERVICE == name) { + override fun getSystemService(name: String): Any = + if (ACCOUNT_SERVICE == name) { mMockAccountManager - } else super.getSystemService(name) - } + } else { + super.getSystemService(name) + } companion object { private val ACCOUNT = Account("test@example.com", BuildConfig.ACCOUNT_TYPE) @@ -27,7 +29,8 @@ class FakeContextWrapper(base: Context?) : ContextWrapper(base) { init { MockitoAnnotations.openMocks(this) Mockito.`when`(mMockAccountManager.accounts).thenReturn(ACCOUNTS) - Mockito.`when`(mMockAccountManager.getAccountsByType(BuildConfig.ACCOUNT_TYPE)) + Mockito + .`when`(mMockAccountManager.getAccountsByType(BuildConfig.ACCOUNT_TYPE)) .thenReturn(ACCOUNTS) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/FakeContextWrapperWithException.kt b/app/src/test/kotlin/fr/free/nrw/commons/FakeContextWrapperWithException.kt index c9f08ad043..035da1f12e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/FakeContextWrapperWithException.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/FakeContextWrapperWithException.kt @@ -7,20 +7,23 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations -class FakeContextWrapperWithException(base: Context?) : ContextWrapper(base) { - +class FakeContextWrapperWithException( + base: Context?, +) : ContextWrapper(base) { @Mock private lateinit var mMockAccountManager: AccountManager - override fun getSystemService(name: String): Any { - return if (ACCOUNT_SERVICE == name) { + override fun getSystemService(name: String): Any = + if (ACCOUNT_SERVICE == name) { mMockAccountManager - } else super.getSystemService(name) - } + } else { + super.getSystemService(name) + } init { MockitoAnnotations.openMocks(this) - Mockito.`when`(mMockAccountManager.getAccountsByType(BuildConfig.ACCOUNT_TYPE)) + Mockito + .`when`(mMockAccountManager.getAccountsByType(BuildConfig.ACCOUNT_TYPE)) .thenThrow(SecurityException("Permission Denied")) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/LatLngTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/LatLngTests.kt index efafa4e3ab..d9ef4d6e8d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/LatLngTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/LatLngTests.kt @@ -59,6 +59,8 @@ class LatLngTests { assertPrettyCoordinateString("0.0 S, 1.0 W", place) } - private fun assertPrettyCoordinateString(expected: String, place: LatLng) = - assertEquals(expected, place.prettyCoordinateString) + private fun assertPrettyCoordinateString( + expected: String, + place: LatLng, + ) = assertEquals(expected, place.prettyCoordinateString) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt index 82f3b3ada9..802e8c206e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt @@ -1,25 +1,23 @@ package fr.free.nrw.commons -import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.media.MediaClient import io.reactivex.Single -import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.mockito.ArgumentMatchers import org.mockito.InjectMocks import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations /** * Test methods in media data extractor */ class MediaDataExtractorTest { - @Mock internal var mediaClient: MediaClient? = null + @InjectMocks var mediaDataExtractor: MediaDataExtractor? = null @@ -38,17 +36,17 @@ class MediaDataExtractorTest { @Test fun fetchMediaDetails() { `when`(mediaClient?.getMedia(ArgumentMatchers.anyString())) - .thenReturn(Single.just(mock(Media::class.java))) + .thenReturn(Single.just(mock(Media::class.java))) `when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString())) - .thenReturn(Single.just(true)) + .thenReturn(Single.just(true)) `when`(mediaClient?.getPageHtml(ArgumentMatchers.anyString())) - .thenReturn(Single.just("Test")) + .thenReturn(Single.just("Test")) - //val fetchMediaDetails = mediaDataExtractor?.fetchMediaDetails("File:Test.jpg", null) + // val fetchMediaDetails = mediaDataExtractor?.fetchMediaDetails("File:Test.jpg", null) - //assertTrue(fetchMediaDetails is Media) + // assertTrue(fetchMediaDetails is Media) } @Test @@ -56,4 +54,4 @@ class MediaDataExtractorTest { `when`(mediaDataExtractor?.getCurrentWikiText(ArgumentMatchers.anyString())) .thenReturn(Single.just("Test")) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/MediaTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/MediaTest.kt index f55857edbf..9f3f05223e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/MediaTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/MediaTest.kt @@ -22,5 +22,3 @@ class MediaTest { assertEquals("Example 1 2", m.displayTitle) } } - - diff --git a/app/src/test/kotlin/fr/free/nrw/commons/ModelFunctions.kt b/app/src/test/kotlin/fr/free/nrw/commons/ModelFunctions.kt index 7d63863db3..76f9c21d0c 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/ModelFunctions.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/ModelFunctions.kt @@ -7,9 +7,13 @@ import fr.free.nrw.commons.nearby.Label import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.Sitelinks import fr.free.nrw.commons.upload.structure.depictions.DepictedItem +import fr.free.nrw.commons.wikidata.model.DataValue import fr.free.nrw.commons.wikidata.model.DepictSearchItem -import fr.free.nrw.commons.wikidata.model.* -import java.util.* +import fr.free.nrw.commons.wikidata.model.Entities +import fr.free.nrw.commons.wikidata.model.SnakPartial +import fr.free.nrw.commons.wikidata.model.StatementPartial +import fr.free.nrw.commons.wikidata.model.WikiBaseEntityValue +import java.util.Date fun depictedItem( name: String = "label", @@ -18,7 +22,7 @@ fun depictedItem( instanceOfs: List = listOf(), commonsCategories: List = listOf(), isSelected: Boolean = false, - id: String = "entityId" + id: String = "entityId", ) = DepictedItem( name = name, description = description, @@ -26,12 +30,15 @@ fun depictedItem( instanceOfs = instanceOfs, commonsCategories = commonsCategories, isSelected = isSelected, - id = id + id = id, ) -fun categoryItem(name: String = "name", description: String = "desc", - thumbUrl: String = "thumbUrl", selected: Boolean = false) = - CategoryItem(name, description, thumbUrl, selected) +fun categoryItem( + name: String = "name", + description: String = "desc", + thumbUrl: String = "thumbUrl", + selected: Boolean = false, +) = CategoryItem(name, description, thumbUrl, selected) fun media( thumbUrl: String? = "thumbUrl", @@ -42,13 +49,13 @@ fun media( license: String? = "license", licenseUrl: String? = "licenseUrl", author: String? = "creator", - user:String?="user", + user: String? = "user", pageId: String = "pageId", categories: List? = listOf("categories"), coordinates: LatLng? = LatLng(0.0, 0.0, 0.0f), captions: Map = mapOf("en" to "caption"), descriptions: Map = mapOf("en" to "description"), - depictionIds: List = listOf("depictionId") + depictionIds: List = listOf("depictionId"), ) = Media( pageId, thumbUrl, @@ -64,7 +71,7 @@ fun media( coordinates, captions, descriptions, - depictionIds + depictionIds, ) fun depictSearchItem( @@ -72,7 +79,7 @@ fun depictSearchItem( pageId: String = "pageid", url: String = "url", label: String = "label", - description: String = "description" + description: String = "description", ) = DepictSearchItem(id, pageId, url, label, description) fun place( @@ -85,39 +92,36 @@ fun place( siteLinks: Sitelinks? = null, pic: String = "pic", exists: Boolean = false, - entityID: String = "entityID" -): Place { - return Place(lang, name, label, longDescription, latLng, category, siteLinks, pic, exists, entityID) -} + entityID: String = "entityID", +): Place = Place(lang, name, label, longDescription, latLng, category, siteLinks, pic, exists, entityID) -fun entityId(wikiBaseEntityValue: WikiBaseEntityValue = wikiBaseEntityValue()) = - DataValue.EntityId(wikiBaseEntityValue) +fun entityId(wikiBaseEntityValue: WikiBaseEntityValue = wikiBaseEntityValue()) = DataValue.EntityId(wikiBaseEntityValue) fun wikiBaseEntityValue( entityType: String = "type", id: String = "id", - numericId: Long = 0 + numericId: Long = 0, ) = WikiBaseEntityValue(entityType, id, numericId) fun statement( - mainSnak: Snak_partial = snak(), + mainSnak: SnakPartial = snak(), rank: String = "rank", - type: String = "type" -) = Statement_partial(mainSnak, type, rank) + type: String = "type", +) = StatementPartial(mainSnak, type, rank) fun snak( snakType: String = "type", property: String = "property", - dataValue: DataValue = valueString("") -) = Snak_partial(snakType, property, dataValue) + dataValue: DataValue = valueString(""), +) = SnakPartial(snakType, property, dataValue) fun valueString(value: String) = DataValue.ValueString(value) fun entity( labels: Map = emptyMap(), descriptions: Map = emptyMap(), - statements: Map>? = emptyMap(), - id: String = "id" + statements: Map>? = emptyMap(), + id: String = "id", ) = mock().apply { val mockedLabels = labels.mockLabels() whenever(labels()).thenReturn(mockedLabels) @@ -127,8 +131,7 @@ fun entity( whenever(id()).thenReturn(id) } -private fun Map.mockLabels(): Map { - return mapValues { entry -> +private fun Map.mockLabels(): Map = + mapValues { entry -> mock().also { whenever(it.value()).thenReturn(entry.value) } } -} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/NearbyControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/NearbyControllerTest.kt index 28b12fe0fe..eae9180115 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/NearbyControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/NearbyControllerTest.kt @@ -2,7 +2,6 @@ package fr.free.nrw.commons import fr.free.nrw.commons.location.LatLng import fr.free.nrw.commons.nearby.NearbyController.loadAttractionsFromLocationToBaseMarkerOptions -import androidx.test.core.app.ApplicationProvider import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -12,13 +11,15 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class NearbyControllerTest { - @Test fun testNullAttractions() { val location = LatLng(0.0, 0.0, 0f) - val options = loadAttractionsFromLocationToBaseMarkerOptions( - location, null) + val options = + loadAttractionsFromLocationToBaseMarkerOptions( + location, + null, + ) assertEquals(0, options.size.toLong()) } @@ -27,8 +28,11 @@ class NearbyControllerTest { fun testEmptyList() { val location = LatLng(0.0, 0.0, 0f) - val options = loadAttractionsFromLocationToBaseMarkerOptions( - location, emptyList()) + val options = + loadAttractionsFromLocationToBaseMarkerOptions( + location, + emptyList(), + ) assertEquals(0, options.size.toLong()) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/OkHttpJsonApiClientTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/OkHttpJsonApiClientTests.kt index 98db40fd04..3431efd6b8 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/OkHttpJsonApiClientTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/OkHttpJsonApiClientTests.kt @@ -46,14 +46,15 @@ class OkHttpJsonApiClientTests { @Before fun setUp() { MockitoAnnotations.openMocks(this) - okHttpJsonApiClient = OkHttpJsonApiClient( - okhttpClient, - depictsClient, - wikiMediaToolforgeUrl, - sparqlQueryUrl, - campaignsUrl, - gson - ) + okHttpJsonApiClient = + OkHttpJsonApiClient( + okhttpClient, + depictsClient, + wikiMediaToolforgeUrl, + sparqlQueryUrl, + campaignsUrl, + gson, + ) Mockito.`when`(okhttpClient.newCall(any())).thenReturn(call) Mockito.`when`(call.execute()).thenReturn(response) } @@ -69,7 +70,6 @@ class OkHttpJsonApiClientTests { } verify(okhttpClient).newCall(any()) verify(call).execute() - } @Test @@ -83,6 +83,5 @@ class OkHttpJsonApiClientTests { } verify(okhttpClient).newCall(any()) verify(call).execute() - } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt index 9c6e081717..84ec5a2cba 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt @@ -17,28 +17,30 @@ import fr.free.nrw.commons.location.LocationServiceManager class TestCommonsApplication : Application() { private var mockApplicationComponent: CommonsApplicationComponent? = null - override fun onCreate() { if (mockApplicationComponent == null) { - mockApplicationComponent = DaggerCommonsApplicationComponent.builder() + mockApplicationComponent = + DaggerCommonsApplicationComponent + .builder() .appModule(MockCommonsApplicationModule(this)) .build() } super.onCreate() setTheme(R.style.Theme_AppCompat) - context=applicationContext + context = applicationContext } - companion object{ - private var context: Context?=null - fun getContext(): Context? { - return context - } + companion object { + private var context: Context? = null + + fun getContext(): Context? = context } } @Suppress("MemberVisibilityCanBePrivate") -class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModule(appContext) { +class MockCommonsApplicationModule( + appContext: Context, +) : CommonsApplicationModule(appContext) { val accountUtil: AccountUtil = mock() val defaultSharedPreferences: JsonKvStore = mock() val locationServiceManager: LocationServiceManager = mock() @@ -58,7 +60,10 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu override fun providesAccountUtil(context: Context): AccountUtil = accountUtil - override fun providesDefaultKvStore(context: Context, gson: Gson): JsonKvStore = defaultSharedPreferences + override fun providesDefaultKvStore( + context: Context, + gson: Gson, + ): JsonKvStore = defaultSharedPreferences override fun provideLocationServiceManager(context: Context): LocationServiceManager = locationServiceManager diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestConnectionFactory.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestConnectionFactory.kt index bac63cda48..3bea1dc262 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestConnectionFactory.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestConnectionFactory.kt @@ -1,15 +1,17 @@ package fr.free.nrw.commons +import fr.free.nrw.commons.OkHttpConnectionFactory.HttpStatusException import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Response -import fr.free.nrw.commons.OkHttpConnectionFactory.HttpStatusException import java.io.IOException -fun createTestClient(): OkHttpClient = OkHttpClient.Builder() - .addInterceptor(UnsuccessfulResponseInterceptor()) - .addInterceptor(TestStubInterceptor()) - .build() +fun createTestClient(): OkHttpClient = + OkHttpClient + .Builder() + .addInterceptor(UnsuccessfulResponseInterceptor()) + .addInterceptor(TestStubInterceptor()) + .build() private class TestStubInterceptor : Interceptor { interface Callback { @@ -18,14 +20,15 @@ private class TestStubInterceptor : Interceptor { } @Throws(IOException::class) - override fun intercept(chain: Interceptor.Chain): Response { - return if (CALLBACK != null) { - CALLBACK!!.getResponse(chain) - } else chain.proceed(chain.request()) - } + override fun intercept(chain: Interceptor.Chain): Response = + if (callback != null) { + callback!!.getResponse(chain) + } else { + chain.proceed(chain.request()) + } companion object { - var CALLBACK: Callback? = null + var callback: Callback? = null } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestUtility.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestUtility.kt index 45de962685..31cc07b37f 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestUtility.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestUtility.kt @@ -5,7 +5,10 @@ import java.lang.reflect.Modifier object TestUtility { @Throws(java.lang.Exception::class) - fun setFinalStatic(field: Field, newValue: Any?) { + fun setFinalStatic( + field: Field, + newValue: Any?, + ) { try { field.isAccessible = true // remove final modifier from field @@ -21,4 +24,4 @@ object TestUtility { e.stackTrace } } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt index d074e4a8f1..e9e68a3ada 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt @@ -2,7 +2,7 @@ package fr.free.nrw.commons import org.junit.Test import org.junit.jupiter.api.Assertions -import java.util.* +import java.util.Calendar class UtilsTest { @Test @@ -25,4 +25,4 @@ class UtilsTest { cal.set(2022, Calendar.DECEMBER, 1) Assertions.assertEquals(2022, Utils.getWikiLovesMonumentsYear(cal)) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/WelcomeActivityUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/WelcomeActivityUnitTest.kt index 7d6ecd23ae..18627796b7 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/WelcomeActivityUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/WelcomeActivityUnitTest.kt @@ -21,7 +21,6 @@ import org.robolectric.shadows.ShadowIntent @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class WelcomeActivityUnitTest { - private lateinit var activity: WelcomeActivity private lateinit var finishTutorialButton: TextView @@ -32,8 +31,10 @@ class WelcomeActivityUnitTest { @Before fun setUp() { val intent = Intent().putExtra("isQuiz", true) - activity = Robolectric.buildActivity(WelcomeActivity::class.java, intent) - .get() + activity = + Robolectric + .buildActivity(WelcomeActivity::class.java, intent) + .get() activity.onCreate(null) finishTutorialButton = activity.findViewById(R.id.finishTutorialButton) } @@ -77,5 +78,4 @@ class WelcomeActivityUnitTest { fun testOnBackPressed() { activity.onBackPressed() } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/actions/PageEditClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/actions/PageEditClientTest.kt index be0937e9d0..d98515c96a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/actions/PageEditClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/actions/PageEditClientTest.kt @@ -2,6 +2,8 @@ package fr.free.nrw.commons.actions import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.verify +import fr.free.nrw.commons.auth.csrf.CsrfTokenClient +import fr.free.nrw.commons.wikidata.model.edit.Edit import io.reactivex.Observable import org.junit.Before import org.junit.Test @@ -9,12 +11,11 @@ import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations -import fr.free.nrw.commons.auth.csrf.CsrfTokenClient -import fr.free.nrw.commons.wikidata.model.edit.Edit class PageEditClientTest { @Mock private lateinit var csrfTokenClient: CsrfTokenClient + @Mock private lateinit var pageEditInterface: PageEditInterface @@ -52,16 +53,17 @@ class PageEditClientTest { @Test fun testAppendEdit() { Mockito.`when`(csrfTokenClient.getTokenBlocking()).thenReturn("test") - Mockito.`when`( - pageEditInterface.postAppendEdit( - ArgumentMatchers.anyString(), - ArgumentMatchers.anyString(), - ArgumentMatchers.anyString(), - ArgumentMatchers.anyString() + Mockito + .`when`( + pageEditInterface.postAppendEdit( + ArgumentMatchers.anyString(), + ArgumentMatchers.anyString(), + ArgumentMatchers.anyString(), + ArgumentMatchers.anyString(), + ), + ).thenReturn( + Observable.just(edit), ) - ).thenReturn( - Observable.just(edit) - ) Mockito.`when`(edit.edit()).thenReturn(editResult) Mockito.`when`(editResult.editSucceeded()).thenReturn(true) pageEditClient.appendEdit("test", "test", "test").test() @@ -88,7 +90,12 @@ class PageEditClientTest { fun testSetCaptions() { Mockito.`when`(csrfTokenClient.getTokenBlocking()).thenReturn("test") pageEditClient.setCaptions("test", "test", "en", "test") - verify(pageEditInterface).postCaptions(eq("test"), eq("test"), eq("en"), - eq("test"), eq("test")) + verify(pageEditInterface).postCaptions( + eq("test"), + eq("test"), + eq("en"), + eq("test"), + eq("test"), + ) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/actions/ThanksClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/actions/ThanksClientTest.kt index c3257ddab7..d409016ae2 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/actions/ThanksClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/actions/ThanksClientTest.kt @@ -3,6 +3,7 @@ package fr.free.nrw.commons.actions import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.verify import fr.free.nrw.commons.CommonsApplication +import fr.free.nrw.commons.auth.csrf.CsrfTokenClient import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -14,13 +15,13 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.powermock.core.classloader.annotations.PrepareForTest import org.robolectric.RobolectricTestRunner -import fr.free.nrw.commons.auth.csrf.CsrfTokenClient @RunWith(RobolectricTestRunner::class) @PrepareForTest(CommonsApplication::class) class ThanksClientTest { @Mock private lateinit var csrfTokenClient: CsrfTokenClient + @Mock private lateinit var service: ThanksInterface @@ -52,4 +53,4 @@ class ThanksClientTest { thanksClient.thank(1L) verify(service).thank(ArgumentMatchers.anyString(), ArgumentMatchers.any(), eq("test"), eq("test")) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/auth/AccountUtilUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/auth/AccountUtilUnitTest.kt index 72aab70365..b45b844c06 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/auth/AccountUtilUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/auth/AccountUtilUnitTest.kt @@ -14,7 +14,6 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class AccountUtilUnitTest { - private lateinit var context: FakeContextWrapper private lateinit var accountUtil: AccountUtil @@ -58,4 +57,4 @@ class AccountUtilUnitTest { FakeContextWrapperWithException(ApplicationProvider.getApplicationContext()) Assert.assertEquals(AccountUtil.account(context), null) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/auth/LoginActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/auth/LoginActivityUnitTests.kt index 2d80c65c94..b50c820a0d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/auth/LoginActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/auth/LoginActivityUnitTests.kt @@ -13,8 +13,8 @@ import androidx.test.core.app.ApplicationProvider import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.auth.login.LoginResult +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.kvstore.JsonKvStore import org.junit.Assert import org.junit.Before @@ -30,11 +30,9 @@ import org.robolectric.annotation.Config import org.robolectric.fakes.RoboMenuItem import java.lang.reflect.Method - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class LoginActivityUnitTests { - private lateinit var menuItem: MenuItem private lateinit var context: Context @@ -92,12 +90,13 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testOnEditorActionCaseDefault() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onEditorAction", - TextView::class.java, - Int::class.java, - KeyEvent::class.java - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "onEditorAction", + TextView::class.java, + Int::class.java, + KeyEvent::class.java, + ) method.isAccessible = true method.invoke(activity, textView, 0, keyEvent) } @@ -105,9 +104,10 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testSkipLogin() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "skipLogin" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "skipLogin", + ) method.isAccessible = true method.invoke(activity) } @@ -115,9 +115,10 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testForgotPassword() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "forgotPassword" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "forgotPassword", + ) method.isAccessible = true method.invoke(activity) } @@ -125,9 +126,10 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testOnPrivacyPolicyClicked() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onPrivacyPolicyClicked" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "onPrivacyPolicyClicked", + ) method.isAccessible = true method.invoke(activity) } @@ -135,9 +137,10 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testSignUp() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "signUp" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "signUp", + ) method.isAccessible = true method.invoke(activity) } @@ -145,10 +148,11 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testOnPostCreate() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onPostCreate", - Bundle::class.java - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "onPostCreate", + Bundle::class.java, + ) method.isAccessible = true method.invoke(activity, bundle) } @@ -157,9 +161,10 @@ class LoginActivityUnitTests { @Throws(Exception::class) fun testOnDestroy() { `when`(progressDialog.isShowing).thenReturn(true) - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onDestroy" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "onDestroy", + ) method.isAccessible = true method.invoke(activity) } @@ -168,9 +173,10 @@ class LoginActivityUnitTests { @Throws(Exception::class) fun testOnDestroyWithException() { `when`(progressDialog.isShowing).thenThrow(NullPointerException()) - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onDestroy" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "onDestroy", + ) method.isAccessible = true method.invoke(activity) } @@ -178,10 +184,11 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testOnLoginSuccessCaseDefault() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onLoginSuccess", - LoginResult::class.java - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "onLoginSuccess", + LoginResult::class.java, + ) method.isAccessible = true method.invoke(activity, loginResult) } @@ -190,10 +197,11 @@ class LoginActivityUnitTests { @Throws(Exception::class) fun testOnLoginSuccess() { `when`(progressDialog.isShowing).thenReturn(true) - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onLoginSuccess", - LoginResult::class.java - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "onLoginSuccess", + LoginResult::class.java, + ) method.isAccessible = true method.invoke(activity, loginResult) } @@ -201,9 +209,10 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testShowPasswordResetPrompt() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "showPasswordResetPrompt" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "showPasswordResetPrompt", + ) method.isAccessible = true method.invoke(activity) } @@ -211,9 +220,10 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testHideProgress() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "hideProgress" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "hideProgress", + ) method.isAccessible = true method.invoke(activity) } @@ -225,9 +235,10 @@ class LoginActivityUnitTests { `when`(applicationKvStore.getBoolean("login_skipped", false)).thenReturn(true) `when`(sessionManager.currentAccount).thenReturn(account) `when`(sessionManager.isUserLoggedIn).thenReturn(true) - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onResume" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "onResume", + ) method.isAccessible = true method.invoke(activity) } @@ -235,9 +246,10 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testOnStart() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onStart" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "onStart", + ) method.isAccessible = true method.invoke(activity) } @@ -245,9 +257,10 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testOnStop() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onStop" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "onStop", + ) method.isAccessible = true method.invoke(activity) } @@ -255,9 +268,10 @@ class LoginActivityUnitTests { @Test @Throws(Exception::class) fun testOnPostResume() { - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onPostResume" - ) + val method: Method = + LoginActivity::class.java.getDeclaredMethod( + "onPostResume", + ) method.isAccessible = true method.invoke(activity) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/auth/SessionManagerUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/auth/SessionManagerUnitTests.kt index 1103978aa9..7b7c260e86 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/auth/SessionManagerUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/auth/SessionManagerUnitTests.kt @@ -20,12 +20,10 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import java.lang.reflect.Method - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class SessionManagerUnitTests { - private lateinit var sessionManager: SessionManager private lateinit var accountManager: AccountManager @@ -59,9 +57,10 @@ class SessionManagerUnitTests { @Test @Throws(Exception::class) fun testRemoveAccountCaseNull() { - val method: Method = SessionManager::class.java.getDeclaredMethod( - "removeAccount" - ) + val method: Method = + SessionManager::class.java.getDeclaredMethod( + "removeAccount", + ) method.isAccessible = true method.invoke(sessionManager) } @@ -71,10 +70,11 @@ class SessionManagerUnitTests { fun testUpdateAccount() { `when`(loginResult.userName).thenReturn("username") `when`(loginResult.password).thenReturn("password") - val method: Method = SessionManager::class.java.getDeclaredMethod( - "updateAccount", - LoginResult::class.java - ) + val method: Method = + SessionManager::class.java.getDeclaredMethod( + "updateAccount", + LoginResult::class.java, + ) method.isAccessible = true method.invoke(sessionManager, loginResult) } @@ -118,11 +118,12 @@ class SessionManagerUnitTests { @Test @Throws(Exception::class) fun testCreateAccount() { - val method: Method = SessionManager::class.java.getDeclaredMethod( - "createAccount", - String::class.java, - String::class.java - ) + val method: Method = + SessionManager::class.java.getDeclaredMethod( + "createAccount", + String::class.java, + String::class.java, + ) method.isAccessible = true Assert.assertEquals(method.invoke(sessionManager, "username", "password"), true) } @@ -130,10 +131,11 @@ class SessionManagerUnitTests { @Test @Throws(Exception::class) fun testSetUserLoggedIn() { - val method: Method = SessionManager::class.java.getDeclaredMethod( - "setUserLoggedIn", - Boolean::class.java - ) + val method: Method = + SessionManager::class.java.getDeclaredMethod( + "setUserLoggedIn", + Boolean::class.java, + ) method.isAccessible = true method.invoke(sessionManager, true) } @@ -141,9 +143,10 @@ class SessionManagerUnitTests { @Test @Throws(Exception::class) fun testGetUserName() { - val method: Method = SessionManager::class.java.getDeclaredMethod( - "getUserName" - ) + val method: Method = + SessionManager::class.java.getDeclaredMethod( + "getUserName", + ) method.isAccessible = true Assert.assertEquals(method.invoke(sessionManager), null) } @@ -151,11 +154,11 @@ class SessionManagerUnitTests { @Test @Throws(Exception::class) fun testGetPassword() { - val method: Method = SessionManager::class.java.getDeclaredMethod( - "getPassword" - ) + val method: Method = + SessionManager::class.java.getDeclaredMethod( + "getPassword", + ) method.isAccessible = true Assert.assertEquals(method.invoke(sessionManager), null) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/auth/SignupActivityTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/auth/SignupActivityTest.kt index bb9c677809..054bf851e7 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/auth/SignupActivityTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/auth/SignupActivityTest.kt @@ -14,11 +14,9 @@ import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class SignupActivityTest { - private lateinit var activity: SignupActivity @Mock @@ -49,4 +47,4 @@ class SignupActivityTest { `when`(webView.canGoBack()).thenReturn(true) activity.onBackPressed() } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/auth/WikiAccountAuthenticatorServiceUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/auth/WikiAccountAuthenticatorServiceUnitTest.kt index 3fc00c6696..c1c2094623 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/auth/WikiAccountAuthenticatorServiceUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/auth/WikiAccountAuthenticatorServiceUnitTest.kt @@ -7,7 +7,6 @@ import org.mockito.MockitoAnnotations import java.lang.reflect.Field class WikiAccountAuthenticatorServiceUnitTest { - private lateinit var service: WikiAccountAuthenticatorService @Before @@ -30,5 +29,4 @@ class WikiAccountAuthenticatorServiceUnitTest { field.set(service, null) Assert.assertEquals(service.onBind(null), null) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/auth/WikiAccountAuthenticatorUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/auth/WikiAccountAuthenticatorUnitTest.kt index f1c11d56e8..856e5015d0 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/auth/WikiAccountAuthenticatorUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/auth/WikiAccountAuthenticatorUnitTest.kt @@ -23,7 +23,6 @@ import org.robolectric.annotation.LooperMode @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class WikiAccountAuthenticatorUnitTest { - private lateinit var context: Context private lateinit var authenticator: WikiAccountAuthenticator @@ -64,7 +63,7 @@ class WikiAccountAuthenticatorUnitTest { val intent: Intent? = bundle.getParcelable(AccountManager.KEY_INTENT) Assert.assertEquals( intent?.extras!![AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE], - response + response, ) } @@ -89,7 +88,7 @@ class WikiAccountAuthenticatorUnitTest { fun testGetAuthTokenLabelCaseNonNull() { Assert.assertEquals( authenticator.getAuthTokenLabel(BuildConfig.ACCOUNT_TYPE), - AccountUtil.AUTH_TOKEN_TYPE + AccountUtil.AUTH_TOKEN_TYPE, ) } @@ -110,5 +109,4 @@ class WikiAccountAuthenticatorUnitTest { val bundle: Bundle? = authenticator.getAccountRemovalAllowed(response, account) Assert.assertEquals(bundle?.getBoolean(AccountManager.KEY_BOOLEAN_RESULT), true) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/auth/csrf/CsrfTokenClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/auth/csrf/CsrfTokenClientTest.kt index 238c9f30da..2e502d21f9 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/auth/csrf/CsrfTokenClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/auth/csrf/CsrfTokenClientTest.kt @@ -2,8 +2,10 @@ package fr.free.nrw.commons.auth.csrf import com.google.gson.stream.MalformedJsonException import fr.free.nrw.commons.MockWebServerTest +import fr.free.nrw.commons.OkHttpConnectionFactory.HttpStatusException import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.auth.login.LoginClient +import fr.free.nrw.commons.wikidata.mwapi.MwException import org.junit.Test import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq @@ -11,8 +13,6 @@ import org.mockito.ArgumentMatchers.isA import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify -import fr.free.nrw.commons.wikidata.mwapi.MwException -import fr.free.nrw.commons.OkHttpConnectionFactory.HttpStatusException class CsrfTokenClientTest : MockWebServerTest() { private val cb = mock(CsrfTokenClient.Callback::class.java) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/auth/login/UserExtendedInfoClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/auth/login/UserExtendedInfoClientTest.kt index 755f8ea8b9..dbf7e20957 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/auth/login/UserExtendedInfoClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/auth/login/UserExtendedInfoClientTest.kt @@ -4,16 +4,16 @@ import android.net.Uri import com.google.gson.GsonBuilder import com.google.gson.stream.MalformedJsonException import fr.free.nrw.commons.MockWebServerTest -import io.reactivex.observers.TestObserver -import org.junit.Before -import org.junit.Test -import fr.free.nrw.commons.wikidata.model.WikiSite -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse import fr.free.nrw.commons.wikidata.json.NamespaceTypeAdapter import fr.free.nrw.commons.wikidata.json.PostProcessingTypeAdapter import fr.free.nrw.commons.wikidata.json.UriTypeAdapter import fr.free.nrw.commons.wikidata.json.WikiSiteTypeAdapter +import fr.free.nrw.commons.wikidata.model.WikiSite import fr.free.nrw.commons.wikidata.model.page.Namespace +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse +import io.reactivex.observers.TestObserver +import org.junit.Before +import org.junit.Test import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory @@ -21,29 +21,36 @@ import retrofit2.converter.gson.GsonConverterFactory class UserExtendedInfoClientTest : MockWebServerTest() { private var apiService: LoginInterface? = null private val observer = TestObserver() - private val gson = GsonBuilder() - .registerTypeHierarchyAdapter(Uri::class.java, UriTypeAdapter() - .nullSafe()) - .registerTypeHierarchyAdapter( - Namespace::class.java, NamespaceTypeAdapter() - .nullSafe()) - .registerTypeAdapter( - WikiSite::class.java, WikiSiteTypeAdapter() - .nullSafe()) - .registerTypeAdapterFactory(PostProcessingTypeAdapter()) - .create() + private val gson = + GsonBuilder() + .registerTypeHierarchyAdapter( + Uri::class.java, + UriTypeAdapter() + .nullSafe(), + ).registerTypeHierarchyAdapter( + Namespace::class.java, + NamespaceTypeAdapter() + .nullSafe(), + ).registerTypeAdapter( + WikiSite::class.java, + WikiSiteTypeAdapter() + .nullSafe(), + ).registerTypeAdapterFactory(PostProcessingTypeAdapter()) + .create() @Before @Throws(Throwable::class) override fun setUp() { super.setUp() - apiService = Retrofit.Builder() - .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) - .addConverterFactory(GsonConverterFactory.create(gson)) - .baseUrl(server().url) - .build() - .create(LoginInterface::class.java) + apiService = + Retrofit + .Builder() + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .addConverterFactory(GsonConverterFactory.create(gson)) + .baseUrl(server().url) + .build() + .create(LoginInterface::class.java) } @Test @@ -57,9 +64,14 @@ class UserExtendedInfoClientTest : MockWebServerTest() { .assertComplete() .assertNoErrors() .assertValue { result: MwQueryResponse -> - result.query()!! - .userInfo()!!.id() == 24531888 && result.query()!!.getUserResponse("USER")!! - .name() == "USER" + result + .query()!! + .userInfo()!! + .id() == 24531888 && + result + .query()!! + .getUserResponse("USER")!! + .name() == "USER" } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/BookmarkListRootFragmentUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/BookmarkListRootFragmentUnitTest.kt index 2029c1cbc4..5098bd0c1a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/BookmarkListRootFragmentUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/BookmarkListRootFragmentUnitTest.kt @@ -18,9 +18,9 @@ import fr.free.nrw.commons.Media import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment import fr.free.nrw.commons.contributions.MainActivity +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.databinding.FragmentBookmarksBinding import fr.free.nrw.commons.databinding.FragmentFeaturedRootBinding import fr.free.nrw.commons.explore.ParentViewPager @@ -44,7 +44,6 @@ import java.lang.reflect.Field @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class BookmarkListRootFragmentUnitTest { - private lateinit var fragment: BookmarkListRootFragment private lateinit var fragmentManager: FragmentManager private lateinit var layoutInflater: LayoutInflater @@ -328,5 +327,4 @@ class BookmarkListRootFragmentUnitTest { field.set(fragment, null) Assert.assertEquals(fragment.backPressed(), false) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/BookmarksPagerAdapterTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/BookmarksPagerAdapterTests.kt index 2301faccfb..9423c90958 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/BookmarksPagerAdapterTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/BookmarksPagerAdapterTests.kt @@ -44,4 +44,4 @@ class BookmarksPagerAdapterTests { fun testGetPageTitle() { bookmarksPagerAdapter.getPageTitle(0) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/LoggedOutBookmarksPagerAdapterTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/LoggedOutBookmarksPagerAdapterTests.kt index d5401ad7d0..4c20bdda93 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/LoggedOutBookmarksPagerAdapterTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/LoggedOutBookmarksPagerAdapterTests.kt @@ -63,4 +63,4 @@ class LoggedOutBookmarksPagerAdapterTests { fun testGetPageTitle() { bookmarksPagerAdapter.getPageTitle(0) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsControllerTest.kt index 513f13162a..98279520d0 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsControllerTest.kt @@ -14,6 +14,7 @@ import java.util.ArrayList class BookmarkItemsControllerTest { @Mock var bookmarkDao: BookmarkItemsDao? = null + @InjectMocks lateinit var bookmarkItemsController: BookmarkItemsController @@ -33,9 +34,21 @@ class BookmarkItemsControllerTest { val list = ArrayList() list.add( DepictedItem( - "name", "description", "image url", listOf("instance"), - listOf(CategoryItem("category name", "category description", - "category thumbnail", false)), true, "id") + "name", + "description", + "image url", + listOf("instance"), + listOf( + CategoryItem( + "category name", + "category description", + "category thumbnail", + false, + ), + ), + true, + "id", + ), ) return list } @@ -49,4 +62,4 @@ class BookmarkItemsControllerTest { bookmarkItemsController.loadFavoritesItems() Assert.assertEquals(1, bookmarkedItems.size.toLong()) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDaoTest.kt index 15d04283c3..60ae7869de 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDaoTest.kt @@ -7,9 +7,31 @@ import android.database.MatrixCursor import android.database.sqlite.SQLiteDatabase import android.net.Uri import android.os.RemoteException -import com.nhaarman.mockitokotlin2.* +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.anyOrNull +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.eq +import com.nhaarman.mockitokotlin2.inOrder +import com.nhaarman.mockitokotlin2.isA +import com.nhaarman.mockitokotlin2.isNull +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.* +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_CATEGORIES_DESCRIPTION_LIST +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_CATEGORIES_NAME_LIST +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_CATEGORIES_THUMBNAIL_LIST +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_DESCRIPTION +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_ID +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_IMAGE +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_INSTANCE_LIST +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_IS_SELECTED +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_NAME +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.CREATE_TABLE_STATEMENT +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.DROP_TABLE_STATEMENT +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onCreate +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onDelete +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onUpdate import fr.free.nrw.commons.category.CategoryItem import fr.free.nrw.commons.upload.structure.depictions.DepictedItem import org.junit.Assert @@ -23,17 +45,18 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class BookmarkItemsDaoTest { - private val columns = arrayOf( - COLUMN_NAME, - COLUMN_DESCRIPTION, - COLUMN_IMAGE, - COLUMN_INSTANCE_LIST, - COLUMN_CATEGORIES_NAME_LIST, - COLUMN_CATEGORIES_DESCRIPTION_LIST, - COLUMN_CATEGORIES_THUMBNAIL_LIST, - COLUMN_IS_SELECTED, - COLUMN_ID, - ) + private val columns = + arrayOf( + COLUMN_NAME, + COLUMN_DESCRIPTION, + COLUMN_IMAGE, + COLUMN_INSTANCE_LIST, + COLUMN_CATEGORIES_NAME_LIST, + COLUMN_CATEGORIES_DESCRIPTION_LIST, + COLUMN_CATEGORIES_THUMBNAIL_LIST, + COLUMN_IS_SELECTED, + COLUMN_ID, + ) private val client: ContentProviderClient = mock() private val database: SQLiteDatabase = mock() private val captor = argumentCaptor() @@ -46,12 +69,23 @@ class BookmarkItemsDaoTest { */ @Before fun setUp() { - exampleItemBookmark = DepictedItem("itemName", "itemDescription", - "itemImageUrl", listOf("instance"), listOf( - CategoryItem("category name", "category description", - "category thumbnail", false) - ), false, - "itemID") + exampleItemBookmark = + DepictedItem( + "itemName", + "itemDescription", + "itemImageUrl", + listOf("instance"), + listOf( + CategoryItem( + "category name", + "category description", + "category thumbnail", + false, + ), + ), + false, + "itemID", + ) testObject = BookmarkItemsDao { client } } @@ -79,9 +113,17 @@ class BookmarkItemsDaoTest { Assert.assertEquals("itemDescription", it.description) Assert.assertEquals("itemImageUrl", it.imageUrl) Assert.assertEquals(listOf("instance"), it.instanceOfs) - Assert.assertEquals(listOf(CategoryItem("category name", - "category description", - "category thumbnail", false)), it.commonsCategories) + Assert.assertEquals( + listOf( + CategoryItem( + "category name", + "category description", + "category thumbnail", + false, + ), + ), + it.commonsCategories, + ) Assert.assertEquals(false, it.isSelected) Assert.assertEquals("itemID", it.id) } @@ -96,13 +138,12 @@ class BookmarkItemsDaoTest { val result = testObject.allBookmarksItems Assert.assertEquals(14, (result.size)) - } @Test(expected = RuntimeException::class) fun getAllItemsBookmarksTranslatesExceptions() { whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow( - RemoteException("") + RemoteException(""), ) testObject.allBookmarksItems } @@ -131,7 +172,6 @@ class BookmarkItemsDaoTest { verify(mockCursor).close() } - @Test fun updateNewItemBookmark() { whenever(client.insert(any(), any())).thenReturn(Uri.EMPTY) @@ -143,39 +183,39 @@ class BookmarkItemsDaoTest { Assert.assertEquals(9, cv.size()) Assert.assertEquals( exampleItemBookmark.name, - cv.getAsString(COLUMN_NAME) + cv.getAsString(COLUMN_NAME), ) Assert.assertEquals( exampleItemBookmark.description, - cv.getAsString(COLUMN_DESCRIPTION) + cv.getAsString(COLUMN_DESCRIPTION), ) Assert.assertEquals( exampleItemBookmark.imageUrl, - cv.getAsString(COLUMN_IMAGE) + cv.getAsString(COLUMN_IMAGE), ) Assert.assertEquals( exampleItemBookmark.instanceOfs[0], - cv.getAsString(COLUMN_INSTANCE_LIST) + cv.getAsString(COLUMN_INSTANCE_LIST), ) Assert.assertEquals( exampleItemBookmark.commonsCategories[0].name, - cv.getAsString(COLUMN_CATEGORIES_NAME_LIST) + cv.getAsString(COLUMN_CATEGORIES_NAME_LIST), ) Assert.assertEquals( exampleItemBookmark.commonsCategories[0].description, - cv.getAsString(COLUMN_CATEGORIES_DESCRIPTION_LIST) + cv.getAsString(COLUMN_CATEGORIES_DESCRIPTION_LIST), ) Assert.assertEquals( exampleItemBookmark.commonsCategories[0].thumbnail, - cv.getAsString(COLUMN_CATEGORIES_THUMBNAIL_LIST) + cv.getAsString(COLUMN_CATEGORIES_THUMBNAIL_LIST), ) Assert.assertEquals( exampleItemBookmark.isSelected, - cv.getAsBoolean(COLUMN_IS_SELECTED) + cv.getAsBoolean(COLUMN_IS_SELECTED), ) Assert.assertEquals( exampleItemBookmark.id, - cv.getAsString(COLUMN_ID) + cv.getAsString(COLUMN_ID), ) } } @@ -186,8 +226,11 @@ class BookmarkItemsDaoTest { whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(1)) Assert.assertFalse(testObject.updateBookmarkItem(exampleItemBookmark)) - verify(client).delete(eq(BookmarkItemsContentProvider.uriForName(exampleItemBookmark.id)), - isNull(), isNull()) + verify(client).delete( + eq(BookmarkItemsContentProvider.uriForName(exampleItemBookmark.id)), + isNull(), + isNull(), + ) } @Test @@ -199,7 +242,7 @@ class BookmarkItemsDaoTest { @Test(expected = RuntimeException::class) fun findItemBookmarkTranslatesExceptions() { whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow( - RemoteException("") + RemoteException(""), ) testObject.findBookmarkItem(exampleItemBookmark.id) } @@ -351,12 +394,22 @@ class BookmarkItemsDaoTest { verifyNoInteractions(database) } - private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply { - - for (i in 0 until rowCount) { - addRow(listOf("itemName", "itemDescription", - "itemImageUrl", "instance", "category name", "category description", - "category thumbnail", false, "itemID")) + private fun createCursor(rowCount: Int) = + MatrixCursor(columns, rowCount).apply { + for (i in 0 until rowCount) { + addRow( + listOf( + "itemName", + "itemDescription", + "itemImageUrl", + "instance", + "category name", + "category description", + "category thumbnail", + false, + "itemID", + ), + ) + } } - } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsFragmentUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsFragmentUnitTest.kt index c68ca34b9a..12292af917 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsFragmentUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/items/BookmarkItemsFragmentUnitTest.kt @@ -5,7 +5,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.widget.ProgressBar -import android.widget.RelativeLayout import android.widget.TextView import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction @@ -15,8 +14,8 @@ import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.category.CategoryItem +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.databinding.FragmentBookmarksItemsBinding import fr.free.nrw.commons.profile.ProfileActivity import fr.free.nrw.commons.upload.structure.depictions.DepictedItem @@ -32,13 +31,11 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import java.lang.reflect.Method -import java.util.* @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class BookmarkItemsFragmentUnitTest { - private lateinit var fragment: BookmarkItemsFragment private lateinit var context: Context private lateinit var view: View @@ -63,11 +60,21 @@ class BookmarkItemsFragmentUnitTest { val list = ArrayList() list.add( DepictedItem( - "name", "description", "image url", listOf("instance"), + "name", + "description", + "image url", + listOf("instance"), listOf( - CategoryItem("category name", "category description", - "category thumbnail", false) - ), true, "id") + CategoryItem( + "category name", + "category description", + "category thumbnail", + false, + ), + ), + true, + "id", + ), ) return list } @@ -88,8 +95,9 @@ class BookmarkItemsFragmentUnitTest { fragmentTransaction.commit() layoutInflater = LayoutInflater.from(activity) - view = layoutInflater - .inflate(R.layout.fragment_bookmarks_items, null) as View + view = + layoutInflater + .inflate(R.layout.fragment_bookmarks_items, null) as View binding = FragmentBookmarksItemsBinding.inflate(layoutInflater) statusTextView = view.findViewById(R.id.status_message) @@ -99,14 +107,13 @@ class BookmarkItemsFragmentUnitTest { fragment.controller = controller Whitebox.setInternalState(fragment, "binding", binding) - } /** * test init items when non empty */ @Test - fun testInitNonEmpty(){ + fun testInitNonEmpty() { whenever(controller.loadFavoritesItems()).thenReturn(mockBookmarkList) val method: Method = BookmarkItemsFragment::class.java.getDeclaredMethod("initList", Context::class.java) @@ -120,8 +127,7 @@ class BookmarkItemsFragmentUnitTest { @Test @Throws(Exception::class) fun testOnCreateView() { - - fragment.onCreateView(layoutInflater,null,savedInstanceState) + fragment.onCreateView(layoutInflater, null, savedInstanceState) } /** diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt index 3edda40169..391dd8d17a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt @@ -7,15 +7,44 @@ import android.database.MatrixCursor import android.database.sqlite.SQLiteDatabase import android.net.Uri import android.os.RemoteException -import com.nhaarman.mockitokotlin2.* +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.anyOrNull +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.eq +import com.nhaarman.mockitokotlin2.inOrder +import com.nhaarman.mockitokotlin2.isA +import com.nhaarman.mockitokotlin2.isNull +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsContentProvider.BASE_URI -import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.* +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_CATEGORY +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_COMMONS_LINK +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_DESCRIPTION +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_EXISTS +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_IMAGE_URL +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_LABEL_ICON +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_LABEL_TEXT +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_LANGUAGE +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_LAT +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_LONG +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_NAME +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_PIC +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_WIKIDATA_LINK +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.COLUMN_WIKIPEDIA_LINK +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.CREATE_TABLE_STATEMENT +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.DROP_TABLE_STATEMENT +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.onCreate +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.onDelete +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.onUpdate import fr.free.nrw.commons.location.LatLng import fr.free.nrw.commons.nearby.Label import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.Sitelinks -import org.junit.Assert.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -26,20 +55,23 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class BookMarkLocationDaoTest { - private val columns = arrayOf(COLUMN_NAME, - COLUMN_LANGUAGE, - COLUMN_DESCRIPTION, - COLUMN_CATEGORY, - COLUMN_LABEL_TEXT, - COLUMN_LABEL_ICON, - COLUMN_IMAGE_URL, - COLUMN_WIKIPEDIA_LINK, - COLUMN_WIKIDATA_LINK, - COLUMN_COMMONS_LINK, - COLUMN_LAT, - COLUMN_LONG, - COLUMN_PIC, - COLUMN_EXISTS) + private val columns = + arrayOf( + COLUMN_NAME, + COLUMN_LANGUAGE, + COLUMN_DESCRIPTION, + COLUMN_CATEGORY, + COLUMN_LABEL_TEXT, + COLUMN_LABEL_ICON, + COLUMN_IMAGE_URL, + COLUMN_WIKIPEDIA_LINK, + COLUMN_WIKIDATA_LINK, + COLUMN_COMMONS_LINK, + COLUMN_LAT, + COLUMN_LONG, + COLUMN_PIC, + COLUMN_EXISTS, + ) private val client: ContentProviderClient = mock() private val database: SQLiteDatabase = mock() private val captor = argumentCaptor() @@ -55,16 +87,25 @@ class BookMarkLocationDaoTest { fun setUp() { exampleLabel = Label.FOREST exampleUri = Uri.parse("wikimedia/uri") - exampleLocation = LatLng(40.0,51.4, 1f) + exampleLocation = LatLng(40.0, 51.4, 1f) builder = Sitelinks.Builder() builder.setWikipediaLink("wikipediaLink") builder.setWikidataLink("wikidataLink") builder.setCommonsLink("commonsLink") - - examplePlaceBookmark = Place("en", "placeName", exampleLabel, "placeDescription" - , exampleLocation, "placeCategory", builder.build(),"picName",false) + examplePlaceBookmark = + Place( + "en", + "placeName", + exampleLabel, + "placeDescription", + exampleLocation, + "placeCategory", + builder.build(), + "picName", + false, + ) testObject = BookmarkLocationsDao { client } } @@ -276,8 +317,8 @@ class BookMarkLocationDaoTest { verify(database).execSQL("ALTER TABLE bookmarksLocations ADD COLUMN location_exists STRING;") } - private fun createCursor(rows: Int): Cursor { - return MatrixCursor(columns, rows).apply { + private fun createCursor(rows: Int): Cursor = + MatrixCursor(columns, rows).apply { repeat(rows) { newRow().apply { add("placeName") @@ -297,5 +338,4 @@ class BookMarkLocationDaoTest { } } } - } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationControllerTest.kt index 232ded8da0..d0db8e0371 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationControllerTest.kt @@ -13,15 +13,16 @@ import java.util.ArrayList class BookmarkLocationControllerTest { @Mock var bookmarkDao: BookmarkLocationsDao? = null + @InjectMocks lateinit var bookmarkLocationsController: BookmarkLocationsController - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - whenever(bookmarkDao!!.allBookmarksLocations) - .thenReturn(mockBookmarkList) - } + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + whenever(bookmarkDao!!.allBookmarksLocations) + .thenReturn(mockBookmarkList) + } /** * Get mock bookmark list @@ -32,7 +33,17 @@ class BookmarkLocationControllerTest { val list = ArrayList() list.add( Place( - "en", "a place", null, "a description", null, "a cat", null, null, true, "entityID") + "en", + "a place", + null, + "a description", + null, + "a cat", + null, + null, + true, + "entityID", + ), ) list.add( Place( @@ -45,8 +56,8 @@ class BookmarkLocationControllerTest { null, null, true, - "entityID" - ) + "entityID", + ), ) return list } @@ -60,4 +71,4 @@ class BookmarkLocationControllerTest { bookmarkLocationsController!!.loadFavoritesLocations() Assert.assertEquals(2, bookmarkedLocations.size.toLong()) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationFragmentUnitTests.kt index 10d4756fda..24693dc856 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationFragmentUnitTests.kt @@ -5,7 +5,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.widget.ProgressBar -import android.widget.RelativeLayout import android.widget.TextView import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction @@ -15,8 +14,8 @@ import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.contributions.ContributionController +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.databinding.FragmentBookmarksLocationsBinding import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.nearby.Place @@ -40,7 +39,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class BookmarkLocationFragmentUnitTests { - private lateinit var fragment: BookmarkLocationsFragment private lateinit var context: Context private lateinit var view: View @@ -87,7 +85,8 @@ class BookmarkLocationFragmentUnitTests { null, null, true, - "entityID") + "entityID", + ), ) return list } @@ -108,28 +107,28 @@ class BookmarkLocationFragmentUnitTests { fragmentTransaction.commit() layoutInflater = LayoutInflater.from(activity) - view = layoutInflater - .inflate(R.layout.fragment_bookmarks_locations, null) as View + view = + layoutInflater + .inflate(R.layout.fragment_bookmarks_locations, null) as View binding = FragmentBookmarksLocationsBinding.bind(view) statusTextView = view.findViewById(R.id.statusMessage) progressBar = view.findViewById(R.id.loadingImagesProgressBar) recyclerView = view.findViewById(R.id.listView) - commonPlaceClickActions = CommonPlaceClickActions(store,activity,contributionController) + commonPlaceClickActions = CommonPlaceClickActions(store, activity, contributionController) fragment.bookmarkLocationDao = bookmarkLocationDao fragment.controller = controller fragment.commonPlaceClickActions = commonPlaceClickActions Whitebox.setInternalState(fragment, "adapter", adapter) Whitebox.setInternalState(fragment, "binding", binding) - } /** * test init places when non empty */ @Test - fun testInitNonEmpty(){ + fun testInitNonEmpty() { whenever(controller.loadFavoritesLocations()).thenReturn(mockBookmarkList) val method: Method = BookmarkLocationsFragment::class.java.getDeclaredMethod("initList") @@ -143,7 +142,7 @@ class BookmarkLocationFragmentUnitTests { @Test @Throws(Exception::class) fun testOnCreateView() { - fragment.onCreateView(layoutInflater,null,savedInstanceState) + fragment.onCreateView(layoutInflater, null, savedInstanceState) } /** diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPictureDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPictureDaoTest.kt index 51d53e3dbc..02668ff1c8 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPictureDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPictureDaoTest.kt @@ -7,23 +7,39 @@ import android.database.MatrixCursor import android.database.sqlite.SQLiteDatabase import android.net.Uri import android.os.RemoteException -import com.nhaarman.mockitokotlin2.* +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.anyOrNull +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.eq +import com.nhaarman.mockitokotlin2.inOrder +import com.nhaarman.mockitokotlin2.isA +import com.nhaarman.mockitokotlin2.isNull +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.bookmarks.models.Bookmark import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.BASE_URI -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config -import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.* -import org.junit.Assert.* +import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.COLUMN_CREATOR +import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.COLUMN_MEDIA_NAME +import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.CREATE_TABLE_STATEMENT +import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.DROP_TABLE_STATEMENT +import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onCreate +import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onDelete +import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onUpdate +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import org.mockito.Mockito.verifyNoInteractions +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class BookmarkPictureDaoTest { - private val columns = arrayOf(COLUMN_MEDIA_NAME, COLUMN_CREATOR) private val client: ContentProviderClient = mock() private val database: SQLiteDatabase = mock() @@ -70,8 +86,7 @@ class BookmarkPictureDaoTest { var result = testObject.allBookmarks - assertEquals(14,(result.size)) - + assertEquals(14, (result.size)) } @Test(expected = RuntimeException::class) @@ -103,7 +118,6 @@ class BookmarkPictureDaoTest { verify(mockCursor).close() } - @Test fun updateNewBookmark() { whenever(client.insert(any(), any())).thenReturn(exampleBookmark.contentUri) @@ -210,9 +224,10 @@ class BookmarkPictureDaoTest { verify(database).execSQL(CREATE_TABLE_STATEMENT) } - private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply { - for (i in 0 until rowCount) { - addRow(listOf("mediaName", "creatorName")) + private fun createCursor(rowCount: Int) = + MatrixCursor(columns, rowCount).apply { + for (i in 0 until rowCount) { + addRow(listOf("mediaName", "creatorName")) + } } - } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesControllerTest.kt index 94cec4054f..bb0718c278 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesControllerTest.kt @@ -14,7 +14,6 @@ import org.mockito.ArgumentMatchers import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.MockitoAnnotations -import java.util.* /** * Tests for bookmark pictures controller @@ -40,10 +39,9 @@ class BookmarkPicturesControllerTest { .thenReturn(mockBookmarkList) whenever( mediaClient!!.getMedia( - ArgumentMatchers.anyString() - ) - ) - .thenReturn(Single.just(mockMedia)) + ArgumentMatchers.anyString(), + ), + ).thenReturn(Single.just(mockMedia)) } /** @@ -83,7 +81,7 @@ class BookmarkPicturesControllerTest { } private val mockMedia: Media - private get() = media(filename="File:Test.jpg") + private get() = media(filename = "File:Test.jpg") /** * Test case where current bookmarks don't match the bookmarks in DB diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesFragmentUnitTests.kt index 0267b543f1..301e31d6da 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPicturesFragmentUnitTests.kt @@ -5,7 +5,6 @@ import android.content.Context import android.os.Bundle import android.os.Looper.getMainLooper import android.view.LayoutInflater -import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.widget.GridView @@ -18,13 +17,11 @@ import androidx.test.core.app.ApplicationProvider import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.times import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.Media import fr.free.nrw.commons.OkHttpConnectionFactory -import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.category.GridViewAdapter +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.databinding.FragmentBookmarksPicturesBinding import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.profile.ProfileActivity @@ -43,12 +40,10 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import java.lang.reflect.Method - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class BookmarkPicturesFragmentUnitTests { - private lateinit var fragment: BookmarkPicturesFragment private lateinit var binding: FragmentBookmarksPicturesBinding @@ -107,11 +102,15 @@ class BookmarkPicturesFragmentUnitTests { fragment.controller = controller - Whitebox.setInternalState(fragment, "gridAdapter", GridViewAdapter( - context, - 0, - listOf(media()) - )) + Whitebox.setInternalState( + fragment, + "gridAdapter", + GridViewAdapter( + context, + 0, + listOf(media()), + ), + ) Whitebox.setInternalState(fragment, "binding", binding) Whitebox.setInternalState(binding, "statusMessage", statusTextView) @@ -178,10 +177,11 @@ class BookmarkPicturesFragmentUnitTests { @Test @Throws(Exception::class) fun testHandleError() { - val method: Method = BookmarkPicturesFragment::class.java.getDeclaredMethod( - "handleError", - Throwable::class.java - ) + val method: Method = + BookmarkPicturesFragment::class.java.getDeclaredMethod( + "handleError", + Throwable::class.java, + ) method.isAccessible = true method.invoke(fragment, throwable) } @@ -190,10 +190,11 @@ class BookmarkPicturesFragmentUnitTests { @Throws(Exception::class) fun testHandleSuccess() { gridAdapter.addItems(listOf(media())) - val method: Method = BookmarkPicturesFragment::class.java.getDeclaredMethod( - "handleSuccess", - List::class.java - ) + val method: Method = + BookmarkPicturesFragment::class.java.getDeclaredMethod( + "handleSuccess", + List::class.java, + ) method.isAccessible = true method.invoke(fragment, listOf(media())) verify(progressBar, times(1)).setVisibility(GONE) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/campaigns/CampaignViewUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/campaigns/CampaignViewUnitTests.kt index efb5b65643..ea8dec1929 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/campaigns/CampaignViewUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/campaigns/CampaignViewUnitTests.kt @@ -4,9 +4,9 @@ import android.app.Activity import android.view.View import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.campaigns.models.Campaign import fr.free.nrw.commons.contributions.MainActivity +import fr.free.nrw.commons.createTestClient import org.junit.Assert import org.junit.Before import org.junit.Test @@ -22,7 +22,6 @@ import java.lang.reflect.Method @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class CampaignViewUnitTests { - private lateinit var activityController: ActivityController private lateinit var activity: MainActivity private lateinit var campaignView: CampaignView @@ -80,11 +79,11 @@ class CampaignViewUnitTests { @Test @Throws(Exception::class) fun testInit() { - val method: Method = CampaignView::class.java.getDeclaredMethod( - "init" - ) + val method: Method = + CampaignView::class.java.getDeclaredMethod( + "init", + ) method.isAccessible = true method.invoke(campaignView) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/campaigns/CampaignsPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/campaigns/CampaignsPresenterTest.kt index 4b0c8a3a04..7efdfd1ad4 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/campaigns/CampaignsPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/campaigns/CampaignsPresenterTest.kt @@ -13,7 +13,9 @@ import org.mockito.Mockito import org.mockito.MockitoAnnotations import java.lang.reflect.Field import java.text.SimpleDateFormat -import java.util.* +import java.util.Calendar +import java.util.Locale +import java.util.TimeZone import kotlin.collections.ArrayList class CampaignsPresenterTest { @@ -44,9 +46,9 @@ class CampaignsPresenterTest { @Throws(Exception::class) fun setUp() { MockitoAnnotations.initMocks(this) - testScheduler=TestScheduler() - campaignsSingle= Single.just(campaignResponseDTO) - campaignsPresenter= CampaignsPresenter(okHttpJsonApiClient,testScheduler,testScheduler) + testScheduler = TestScheduler() + campaignsSingle = Single.just(campaignResponseDTO) + campaignsPresenter = CampaignsPresenter(okHttpJsonApiClient, testScheduler, testScheduler) campaignsPresenter.onAttachView(view) Mockito.`when`(okHttpJsonApiClient.campaigns).thenReturn(campaignsSingle) } @@ -62,17 +64,17 @@ class CampaignsPresenterTest { @Test fun getCampaignsTestNonEmptyCampaigns() { campaignsPresenter.getCampaigns() - var campaigns= ArrayList() + var campaigns = ArrayList() campaigns.add(campaign) val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT) simpleDateFormat.timeZone = TimeZone.getTimeZone("UTC") Mockito.`when`(campaignResponseDTO.campaigns).thenReturn(campaigns) var calendar = Calendar.getInstance() - calendar.add(Calendar.DATE,-1) + calendar.add(Calendar.DATE, -1) val startDateString = simpleDateFormat.format(calendar.time).toString() - calendar= Calendar.getInstance() - calendar.add(Calendar.DATE,3) - val endDateString= simpleDateFormat.format(calendar.time).toString() + calendar = Calendar.getInstance() + calendar.add(Calendar.DATE, 3) + val endDateString = simpleDateFormat.format(calendar.time).toString() Mockito.`when`(campaign.endDate).thenReturn(endDateString) Mockito.`when`(campaign.startDate).thenReturn(startDateString) Mockito.`when`(campaignResponseDTO.campaigns).thenReturn(campaigns) @@ -105,5 +107,4 @@ class CampaignsPresenterTest { disposableField.set(campaignsPresenter, disposable) campaignsPresenter.onDetachView() } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoriesModelTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoriesModelTest.kt index 16f7ec2c2e..cc5340fbb7 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoriesModelTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoriesModelTest.kt @@ -15,9 +15,8 @@ import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.MockitoAnnotations -//class for testing CategoriesModel class +// class for testing CategoriesModel class class CategoriesModelTest { - @Mock internal lateinit var categoryDao: CategoryDao @@ -41,59 +40,125 @@ class CategoriesModelTest { fun searchAllFoundCaseTest() { val categoriesModel = CategoriesModel(categoryClient, mock(), mock()) - val expectedList = listOf(CategoryItem( - "Test", "", "", false)) + val expectedList = + listOf( + CategoryItem( + "Test", + "", + "", + false, + ), + ) whenever( categoryClient.searchCategoriesForPrefix( ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt() - ) - ) - .thenReturn(Single.just(expectedList)) + ArgumentMatchers.anyInt(), + ), + ).thenReturn(Single.just(expectedList)) // Checking if both return "Test" - val expectedItems = expectedList.map { CategoryItem( - it.name, it.description, it.thumbnail, false) } + val expectedItems = + expectedList.map { + CategoryItem( + it.name, + it.description, + it.thumbnail, + false, + ) + } var categoryTerm = "Test" - categoriesModel.searchAll(categoryTerm, emptyList(), emptyList()) + categoriesModel + .searchAll(categoryTerm, emptyList(), emptyList()) .test() .assertValues(expectedItems) verify(categoryClient).searchCategoriesForPrefix( categoryTerm, - CategoriesModel.SEARCH_CATS_LIMIT + CategoriesModel.SEARCH_CATS_LIMIT, ) - categoriesModel.searchAll(categoryTerm, emptyList(), emptyList()) + categoriesModel + .searchAll(categoryTerm, emptyList(), emptyList()) .test() .assertValues(expectedItems) } - @Test fun `searchAll with empty search terms creates results from gps, title search & recents`() { val gpsCategoryModel: GpsCategoryModel = mock() - val depictedItem = depictedItem(commonsCategories = listOf(CategoryItem( - "depictionCategory", "", "", false))) + val depictedItem = + depictedItem( + commonsCategories = + listOf( + CategoryItem( + "depictionCategory", + "", + "", + false, + ), + ), + ) whenever(gpsCategoryModel.categoriesFromLocation) - .thenReturn(BehaviorSubject.createDefault(listOf(CategoryItem( - "gpsCategory", "", "", false)))) + .thenReturn( + BehaviorSubject.createDefault( + listOf( + CategoryItem( + "gpsCategory", + "", + "", + false, + ), + ), + ), + ) whenever( categoryClient.searchCategories( ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt() - ) + ArgumentMatchers.anyInt(), + ), + ).thenReturn( + Single.just( + listOf( + CategoryItem( + "titleSearch", + "", + "", + false, + ), + ), + ), + ) + whenever(categoryDao.recentCategories(25)).thenReturn( + listOf( + CategoryItem( + "recentCategories", + "", + "", + false, + ), + ), + ) + whenever( + categoryClient.getCategoriesByName( + "depictionCategory", + "depictionCategory", + 25, + ), + ).thenReturn( + Single.just( + listOf( + CategoryItem( + "commonsCategories", + "", + "", + false, + ), + ), + ), ) - .thenReturn(Single.just(listOf(CategoryItem( - "titleSearch", "", "", false)))) - whenever(categoryDao.recentCategories(25)).thenReturn(listOf(CategoryItem( - "recentCategories","","", false))) - whenever(categoryClient.getCategoriesByName("depictionCategory", - "depictionCategory", 25)).thenReturn(Single.just(listOf(CategoryItem( - "commonsCategories","","", false)))) val imageTitleList = listOf("Test") CategoriesModel(categoryClient, categoryDao, gpsCategoryModel) .searchAll("", imageTitleList, listOf(depictedItem)) @@ -103,8 +168,8 @@ class CategoriesModelTest { categoryItem("commonsCategories"), categoryItem("gpsCategory"), categoryItem("titleSearch"), - categoryItem("recentCategories") - ) + categoryItem("recentCategories"), + ), ) imageTitleList.forEach { verify(categoryClient).searchCategories(it, CategoriesModel.SEARCH_CATS_LIMIT) @@ -113,103 +178,121 @@ class CategoriesModelTest { @Test @Throws(Exception::class) - fun testGetCategoriesByName(){ + fun testGetCategoriesByName() { categoriesModel.getCategoriesByName(listOf("Test")) } @Test @Throws(Exception::class) - fun `Test buildCategories when it returns non empty list`(){ - whenever(categoryClient.getCategoriesByName("Test", - "Test", CategoriesModel.SEARCH_CATS_LIMIT - )).thenReturn(Single.just(listOf(categoryItem()))) + fun `Test buildCategories when it returns non empty list`() { + whenever( + categoryClient.getCategoriesByName( + "Test", + "Test", + CategoriesModel.SEARCH_CATS_LIMIT, + ), + ).thenReturn(Single.just(listOf(categoryItem()))) categoriesModel.buildCategories("Test") } @Test @Throws(Exception::class) - fun `Test buildCategories when it returns empty list`(){ - whenever(categoryClient.getCategoriesByName("Test", - "Test", CategoriesModel.SEARCH_CATS_LIMIT - )).thenReturn(Single.just(emptyList())) + fun `Test buildCategories when it returns empty list`() { + whenever( + categoryClient.getCategoriesByName( + "Test", + "Test", + CategoriesModel.SEARCH_CATS_LIMIT, + ), + ).thenReturn(Single.just(emptyList())) categoriesModel.buildCategories("Test") } @Test @Throws(Exception::class) - fun testGetSelectedExistingCategories(){ + fun testGetSelectedExistingCategories() { categoriesModel.getSelectedExistingCategories() } @Test @Throws(Exception::class) - fun testSetSelectedExistingCategories(){ + fun testSetSelectedExistingCategories() { categoriesModel.setSelectedExistingCategories(mutableListOf("Test")) } @Test @Throws(Exception::class) - fun `Test onCategoryItemClicked when media is null and item is selected`(){ + fun `Test onCategoryItemClicked when media is null and item is selected`() { categoriesModel.onCategoryItemClicked( CategoryItem( "name", "des", "image", - true - ), null) + true, + ), + null, + ) } @Test @Throws(Exception::class) - fun `Test onCategoryItemClicked when media is null and item is not selected`(){ + fun `Test onCategoryItemClicked when media is null and item is not selected`() { categoriesModel.onCategoryItemClicked(categoryItem(), null) } @Test @Throws(Exception::class) - fun `Test onCategoryItemClicked when media is not null and item is selected and media contains category`(){ + fun `Test onCategoryItemClicked when media is not null and item is selected and media contains category`() { categoriesModel.onCategoryItemClicked( CategoryItem( "categories", "des", "image", - true - ), media()) + true, + ), + media(), + ) } @Test @Throws(Exception::class) - fun `Test onCategoryItemClicked when media is not null and item is selected and media does not contains category`(){ + fun `Test onCategoryItemClicked when media is not null and item is selected and media does not contains category`() { categoriesModel.onCategoryItemClicked( CategoryItem( "name", "des", "image", - true - ), media()) + true, + ), + media(), + ) } @Test @Throws(Exception::class) - fun `Test onCategoryItemClicked when media is not null and item is not selected and media contains category`(){ + fun `Test onCategoryItemClicked when media is not null and item is not selected and media contains category`() { categoriesModel.onCategoryItemClicked( CategoryItem( "categories", "des", "image", - false - ), media()) + false, + ), + media(), + ) } @Test @Throws(Exception::class) - fun `Test onCategoryItemClicked when media is not null and item is not selected and media does not contains category`(){ + fun `Test onCategoryItemClicked when media is not null and item is not selected and media does not contains category`() { categoriesModel.onCategoryItemClicked( CategoryItem( "name", "des", "image", - false - ), media()) + false, + ), + media(), + ) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryClientTest.kt index 1081dc0be8..5c95215a87 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryClientTest.kt @@ -2,17 +2,19 @@ package fr.free.nrw.commons.category import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.whenever +import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResult import io.reactivex.Single import org.junit.Before import org.junit.Test -import org.mockito.ArgumentMatchers.* +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyMap +import org.mockito.ArgumentMatchers.anyString import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations -import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResult class CategoryClientTest { @Mock @@ -32,10 +34,12 @@ class CategoryClientTest { val mockResponse = withMockResponse("Category:Test") whenever(categoryInterface.searchCategories(anyString(), anyInt(), anyInt())) .thenReturn(Single.just(mockResponse)) - categoryClient.searchCategories("tes", 10) + categoryClient + .searchCategories("tes", 10) .test() .assertValues(listOf(CategoryItem("Test", "", "", false))) - categoryClient.searchCategories("tes", 10, 10) + categoryClient + .searchCategories("tes", 10, 10) .test() .assertValues(listOf(CategoryItem("Test", "", "", false))) } @@ -45,22 +49,27 @@ class CategoryClientTest { val mockResponse = withNullPages() whenever(categoryInterface.searchCategories(anyString(), anyInt(), anyInt())) .thenReturn(Single.just(mockResponse)) - categoryClient.searchCategories("tes", 10) + categoryClient + .searchCategories("tes", 10) .test() .assertValues(emptyList()) - categoryClient.searchCategories("tes", 10, 10) + categoryClient + .searchCategories("tes", 10, 10) .test() .assertValues(emptyList()) } + @Test fun searchCategoriesForPrefixFound() { val mockResponse = withMockResponse("Category:Test") whenever(categoryInterface.searchCategoriesForPrefix(anyString(), anyInt(), anyInt())) .thenReturn(Single.just(mockResponse)) - categoryClient.searchCategoriesForPrefix("tes", 10) + categoryClient + .searchCategoriesForPrefix("tes", 10) .test() .assertValues(listOf(CategoryItem("Test", "", "", false))) - categoryClient.searchCategoriesForPrefix("tes", 10, 10) + categoryClient + .searchCategoriesForPrefix("tes", 10, 10) .test() .assertValues(listOf(CategoryItem("Test", "", "", false))) } @@ -70,10 +79,12 @@ class CategoryClientTest { val mockResponse = withNullPages() whenever(categoryInterface.searchCategoriesForPrefix(anyString(), anyInt(), anyInt())) .thenReturn(Single.just(mockResponse)) - categoryClient.searchCategoriesForPrefix("tes", 10) + categoryClient + .searchCategoriesForPrefix("tes", 10) .test() .assertValues(emptyList()) - categoryClient.searchCategoriesForPrefix("tes", 10, 10) + categoryClient + .searchCategoriesForPrefix("tes", 10, 10) .test() .assertValues(emptyList()) } @@ -81,33 +92,71 @@ class CategoryClientTest { @Test fun getCategoriesByNameFound() { val mockResponse = withMockResponse("Category:Test") - whenever(categoryInterface.getCategoriesByName(anyString(), anyString(), - anyInt(), anyInt())) - .thenReturn(Single.just(mockResponse)) - categoryClient.getCategoriesByName("tes", "tes", 10) - .test() - .assertValues(listOf(CategoryItem("Test", "", - "", false))) - categoryClient.getCategoriesByName("tes" , "tes", - 10, 10) - .test() - .assertValues(listOf(CategoryItem("Test", "", - "", false))) + whenever( + categoryInterface.getCategoriesByName( + anyString(), + anyString(), + anyInt(), + anyInt(), + ), + ).thenReturn(Single.just(mockResponse)) + categoryClient + .getCategoriesByName("tes", "tes", 10) + .test() + .assertValues( + listOf( + CategoryItem( + "Test", + "", + "", + false, + ), + ), + ) + categoryClient + .getCategoriesByName( + "tes", + "tes", + 10, + 10, + ).test() + .assertValues( + listOf( + CategoryItem( + "Test", + "", + "", + false, + ), + ), + ) } @Test fun getCategoriesByNameNull() { val mockResponse = withNullPages() - whenever(categoryInterface.getCategoriesByName(anyString(), anyString(), - anyInt(), anyInt())) - .thenReturn(Single.just(mockResponse)) - categoryClient.getCategoriesByName("tes", "tes", - 10) - .test() + whenever( + categoryInterface.getCategoriesByName( + anyString(), + anyString(), + anyInt(), + anyInt(), + ), + ).thenReturn(Single.just(mockResponse)) + categoryClient + .getCategoriesByName( + "tes", + "tes", + 10, + ).test() .assertValues(emptyList()) - categoryClient.getCategoriesByName("tes", "tes", - 10, 10) - .test() + categoryClient + .getCategoriesByName( + "tes", + "tes", + 10, + 10, + ).test() .assertValues(emptyList()) } @@ -116,7 +165,8 @@ class CategoryClientTest { val mockResponse = withMockResponse("Category:Test") whenever(categoryInterface.getParentCategoryList(anyString(), anyMap())) .thenReturn(Single.just(mockResponse)) - categoryClient.getParentCategoryList("tes") + categoryClient + .getParentCategoryList("tes") .test() .assertValues(listOf(CategoryItem("Test", "", "", false))) } @@ -126,7 +176,8 @@ class CategoryClientTest { val mockResponse = withNullPages() whenever(categoryInterface.getParentCategoryList(anyString(), anyMap())) .thenReturn(Single.just(mockResponse)) - categoryClient.getParentCategoryList("tes") + categoryClient + .getParentCategoryList("tes") .test() .assertValues(emptyList()) } @@ -136,7 +187,8 @@ class CategoryClientTest { val mockResponse = withMockResponse("Category:Test") whenever(categoryInterface.getSubCategoryList("tes", emptyMap())) .thenReturn(Single.just(mockResponse)) - categoryClient.getSubCategoryList("tes") + categoryClient + .getSubCategoryList("tes") .test() .assertValues(listOf(CategoryItem("Test", "", "", false))) } @@ -144,12 +196,14 @@ class CategoryClientTest { @Test fun getSubCategoryListNull() { val mockResponse = withNullPages() - whenever(categoryInterface.getSubCategoryList( - anyString(), - anyMap() - )) - .thenReturn(Single.just(mockResponse)) - categoryClient.getSubCategoryList("tes") + whenever( + categoryInterface.getSubCategoryList( + anyString(), + anyMap(), + ), + ).thenReturn(Single.just(mockResponse)) + categoryClient + .getSubCategoryList("tes") .test() .assertValues(emptyList()) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDaoTest.kt index f663a6cc8c..ab222b4ebe 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDaoTest.kt @@ -6,27 +6,55 @@ import android.database.Cursor import android.database.MatrixCursor import android.database.sqlite.SQLiteDatabase import android.os.RemoteException -import com.nhaarman.mockitokotlin2.* -import fr.free.nrw.commons.BuildConfig +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.anyOrNull +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.eq +import com.nhaarman.mockitokotlin2.inOrder +import com.nhaarman.mockitokotlin2.isA +import com.nhaarman.mockitokotlin2.isNull +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.category.CategoryContentProvider.BASE_URI import fr.free.nrw.commons.category.CategoryContentProvider.uriForId -import fr.free.nrw.commons.category.CategoryDao.Table.* -import org.junit.Assert.* +import fr.free.nrw.commons.category.CategoryDao.Table.ALL_FIELDS +import fr.free.nrw.commons.category.CategoryDao.Table.COLUMN_DESCRIPTION +import fr.free.nrw.commons.category.CategoryDao.Table.COLUMN_ID +import fr.free.nrw.commons.category.CategoryDao.Table.COLUMN_LAST_USED +import fr.free.nrw.commons.category.CategoryDao.Table.COLUMN_NAME +import fr.free.nrw.commons.category.CategoryDao.Table.COLUMN_THUMBNAIL +import fr.free.nrw.commons.category.CategoryDao.Table.COLUMN_TIMES_USED +import fr.free.nrw.commons.category.CategoryDao.Table.CREATE_TABLE_STATEMENT +import fr.free.nrw.commons.category.CategoryDao.Table.DROP_TABLE_STATEMENT +import fr.free.nrw.commons.category.CategoryDao.Table.onCreate +import fr.free.nrw.commons.category.CategoryDao.Table.onDelete +import fr.free.nrw.commons.category.CategoryDao.Table.onUpdate +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.verifyNoInteractions import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import java.util.* +import java.util.Date @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class CategoryDaoTest { - - private val columns = arrayOf(COLUMN_ID, COLUMN_NAME, COLUMN_DESCRIPTION, - COLUMN_THUMBNAIL, COLUMN_LAST_USED, COLUMN_TIMES_USED) + private val columns = + arrayOf( + COLUMN_ID, + COLUMN_NAME, + COLUMN_DESCRIPTION, + COLUMN_THUMBNAIL, + COLUMN_LAST_USED, + COLUMN_TIMES_USED, + ) private val client: ContentProviderClient = mock() private val database: SQLiteDatabase = mock() private val captor = argumentCaptor() @@ -138,8 +166,15 @@ class CategoryDaoTest { fun saveNewCategory() { val contentUri = CategoryContentProvider.uriForId(111) whenever(client.insert(isA(), isA())).thenReturn(contentUri) - val category = Category(null, "showImageWithItem", "description", - "image", Date(234L), 1) + val category = + Category( + null, + "showImageWithItem", + "description", + "image", + Date(234L), + 1, + ) testObject.save(category) @@ -199,11 +234,11 @@ class CategoryDaoTest { assertEquals(2, category?.timesUsed) verify(client).query( - eq(BASE_URI), - eq(ALL_FIELDS), - eq("$COLUMN_NAME=?"), - queryCaptor.capture(), - isNull() + eq(BASE_URI), + eq(ALL_FIELDS), + eq("$COLUMN_NAME=?"), + queryCaptor.capture(), + isNull(), ) assertEquals("showImageWithItem", queryCaptor.firstValue[0]) } @@ -253,11 +288,11 @@ class CategoryDaoTest { assertEquals("showImageWithItem", result[0].name) verify(client).query( - eq(BASE_URI), - eq(ALL_FIELDS), - isNull(), - queryCaptor.capture(), - eq("$COLUMN_LAST_USED DESC") + eq(BASE_URI), + eq(ALL_FIELDS), + isNull(), + queryCaptor.capture(), + eq("$COLUMN_LAST_USED DESC"), ) assertEquals(0, queryCaptor.firstValue.size) } @@ -271,10 +306,10 @@ class CategoryDaoTest { assertEquals(5, result.size) } - private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply { - for (i in 0 until rowCount) { - addRow(listOf("1", "showImageWithItem", "description", "image", "123", "2")) + private fun createCursor(rowCount: Int) = + MatrixCursor(columns, rowCount).apply { + for (i in 0 until rowCount) { + addRow(listOf("1", "showImageWithItem", "description", "image", "123", "2")) + } } - } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDetailsActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDetailsActivityUnitTests.kt index e5e17704da..fd6bc7976a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDetailsActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDetailsActivityUnitTests.kt @@ -24,7 +24,6 @@ import java.lang.reflect.Field @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class CategoryDetailsActivityUnitTests { - private lateinit var activity: CategoryDetailsActivity private lateinit var context: Context @@ -38,7 +37,6 @@ class CategoryDetailsActivityUnitTests { @Before fun setUp() { - MockitoAnnotations.openMocks(this) OkHttpConnectionFactory.CLIENT = createTestClient() @@ -110,5 +108,4 @@ class CategoryDetailsActivityUnitTests { fun testViewPagerNotifyDataSetChanged() { activity.viewPagerNotifyDataSetChanged() } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryEditHelperUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryEditHelperUnitTests.kt index 2f25c815ce..ec7b5111b1 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryEditHelperUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryEditHelperUnitTests.kt @@ -26,7 +26,6 @@ import org.robolectric.annotation.LooperMode @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class CategoryEditHelperUnitTests { - private lateinit var context: Context private lateinit var helper: CategoryEditHelper @@ -46,18 +45,25 @@ class CategoryEditHelperUnitTests { fun setUp() { MockitoAnnotations.openMocks(this) context = ApplicationProvider.getApplicationContext() - helper = CategoryEditHelper(notificationHelper, pageEditClient, viewUtilWrapper, - "") + helper = + CategoryEditHelper( + notificationHelper, + pageEditClient, + viewUtilWrapper, + "", + ) Mockito.`when`(media.filename).thenReturn("File:Example.jpg") - Mockito.`when`(pageEditClient.getCurrentWikiText(ArgumentMatchers.anyString())) + Mockito + .`when`(pageEditClient.getCurrentWikiText(ArgumentMatchers.anyString())) .thenReturn(Single.just("")) - Mockito.`when`( - pageEditClient.edit( - ArgumentMatchers.anyString(), - ArgumentMatchers.anyString(), - ArgumentMatchers.anyString() - ) - ).thenReturn(Observable.just(true)) + Mockito + .`when`( + pageEditClient.edit( + ArgumentMatchers.anyString(), + ArgumentMatchers.anyString(), + ArgumentMatchers.anyString(), + ), + ).thenReturn(Observable.just(true)) } @Test @@ -72,19 +78,23 @@ class CategoryEditHelperUnitTests { helper.makeCategoryEdit(context, media, listOf("Test"), "[[Category:Test]]") Mockito.verify(viewUtilWrapper, Mockito.times(1)).showShortToast( context, - context.getString(R.string.category_edit_helper_make_edit_toast) + context.getString(R.string.category_edit_helper_make_edit_toast), ) Mockito.verify(pageEditClient, Mockito.times(1)).edit( ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), - ArgumentMatchers.anyString() + ArgumentMatchers.anyString(), ) } @Test @Throws(Exception::class) fun testMakeUnCategoryEdit() { - helper.makeCategoryEdit(context, media, listOf("Test"), "== {{int:filedesc}} ==\n" + + helper.makeCategoryEdit( + context, + media, + listOf("Test"), + "== {{int:filedesc}} ==\n" + "{{Information\n" + "|description=\n" + "|source={{own}}\n" + @@ -97,15 +107,16 @@ class CategoryEditHelperUnitTests { "{{self|cc-zero}}\n" + "\n" + "{{Uploaded from Mobile|platform=Android|version=3.1.1-debug-master~e7a9ba9ad}}\n" + - "{{Uncategorized|year=2022|month=April|day=23}}") + "{{Uncategorized|year=2022|month=April|day=23}}", + ) Mockito.verify(viewUtilWrapper, Mockito.times(1)).showShortToast( context, - context.getString(R.string.category_edit_helper_make_edit_toast) + context.getString(R.string.category_edit_helper_make_edit_toast), ) Mockito.verify(pageEditClient, Mockito.times(1)).edit( ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), - ArgumentMatchers.anyString() + ArgumentMatchers.anyString(), ) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/category/GridViewAdapterUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/category/GridViewAdapterUnitTest.kt index 3921b31b35..d3901113a0 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/category/GridViewAdapterUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/category/GridViewAdapterUnitTest.kt @@ -26,7 +26,6 @@ import java.lang.reflect.Method @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class GridViewAdapterUnitTest { - private lateinit var gridViewAdapter: GridViewAdapter private lateinit var activity: CategoryDetailsActivity private lateinit var context: Context @@ -47,10 +46,8 @@ class GridViewAdapterUnitTest { @Before @Throws(Exception::class) fun setUp() { - MockitoAnnotations.openMocks(this) - context = ApplicationProvider.getApplicationContext() SoLoader.setInTestMode() @@ -59,8 +56,10 @@ class GridViewAdapterUnitTest { activity = Robolectric.buildActivity(CategoryDetailsActivity::class.java).get() - convertView = LayoutInflater.from(activity) - .inflate(R.layout.layout_category_images, null) as View + convertView = + LayoutInflater + .from(activity) + .inflate(R.layout.layout_category_images, null) as View gridViewAdapter = GridViewAdapter(context, 0, images) } @@ -116,11 +115,13 @@ class GridViewAdapterUnitTest { @Test fun testSetUploaderView() { `when`(media1.author).thenReturn("author") - val method: Method = GridViewAdapter::class.java.getDeclaredMethod( - "setUploaderView", Media::class.java, TextView::class.java - ) + val method: Method = + GridViewAdapter::class.java.getDeclaredMethod( + "setUploaderView", + Media::class.java, + TextView::class.java, + ) method.isAccessible = true method.invoke(gridViewAdapter, media1, textView) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionBoundaryCallbackTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionBoundaryCallbackTest.kt index 88657e101a..19b36521df 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionBoundaryCallbackTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionBoundaryCallbackTest.kt @@ -62,8 +62,8 @@ class ContributionBoundaryCallbackTest { whenever(mediaClient.getMediaListForUser(anyString())) .thenReturn(Single.just(listOf(media()))) contributionBoundaryCallback.onZeroItemsLoaded() - verify(repository).save(anyList()); - verify(mediaClient).getMediaListForUser(anyString()); + verify(repository).save(anyList()) + verify(mediaClient).getMediaListForUser(anyString()) } @Test @@ -74,8 +74,8 @@ class ContributionBoundaryCallbackTest { whenever(mediaClient.getMediaListForUser(anyString())) .thenReturn(Single.just(listOf(media()))) contributionBoundaryCallback.onItemAtEndLoaded(mock(Contribution::class.java)) - verify(repository).save(anyList()); - verify(mediaClient).getMediaListForUser(anyString()); + verify(repository).save(anyList()) + verify(mediaClient).getMediaListForUser(anyString()) } @Test @@ -94,27 +94,29 @@ class ContributionBoundaryCallbackTest { .thenReturn(Single.just(listOf(1L, 2L))) whenever(sessionManager.userName).thenReturn("Test") whenever(mediaClient.getMediaListForUser(anyString())).thenReturn( - Single.just(listOf(media())) - ) - val method: Method = ContributionBoundaryCallback::class.java.getDeclaredMethod( - "fetchContributions" + Single.just(listOf(media())), ) + val method: Method = + ContributionBoundaryCallback::class.java.getDeclaredMethod( + "fetchContributions", + ) method.isAccessible = true method.invoke(contributionBoundaryCallback) - verify(repository).save(anyList()); - verify(mediaClient).getMediaListForUser(anyString()); + verify(repository).save(anyList()) + verify(mediaClient).getMediaListForUser(anyString()) } @Test fun testFetchContributionsFailed() { whenever(sessionManager.userName).thenReturn("Test") whenever(mediaClient.getMediaListForUser(anyString())).thenReturn(Single.error(Exception("Error"))) - val method: Method = ContributionBoundaryCallback::class.java.getDeclaredMethod( - "fetchContributions" - ) + val method: Method = + ContributionBoundaryCallback::class.java.getDeclaredMethod( + "fetchContributions", + ) method.isAccessible = true method.invoke(contributionBoundaryCallback) verifyNoInteractions(repository) - verify(mediaClient).getMediaListForUser(anyString()); + verify(mediaClient).getMediaListForUser(anyString()) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionViewHolderUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionViewHolderUnitTests.kt index 3f56c109d3..397e030704 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionViewHolderUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionViewHolderUnitTests.kt @@ -4,13 +4,8 @@ import android.net.Uri import android.os.Looper import android.view.LayoutInflater import android.view.View -import android.widget.ImageButton -import android.widget.ProgressBar -import android.widget.RelativeLayout -import android.widget.TextView import androidx.test.core.app.ApplicationProvider import com.facebook.drawee.backends.pipeline.Fresco -import com.facebook.drawee.view.SimpleDraweeView import com.facebook.soloader.SoLoader import fr.free.nrw.commons.Media import fr.free.nrw.commons.R @@ -25,7 +20,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.* +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.powermock.core.classloader.annotations.PrepareForTest import org.powermock.reflect.Whitebox @@ -34,16 +29,13 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.Shadows import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode -import java.lang.reflect.Field import java.lang.reflect.Method -import java.lang.reflect.Modifier @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) @PrepareForTest(ContributionViewHolder::class) class ContributionViewHolderUnitTests { - private lateinit var contributionViewHolder: ContributionViewHolder private lateinit var activity: ProfileActivity private lateinit var parent: View @@ -66,7 +58,7 @@ class ContributionViewHolderUnitTests { @Mock private lateinit var media: Media - private lateinit var bindind : LayoutContributionBinding + private lateinit var bindind: LayoutContributionBinding @Before fun setUp() { @@ -82,8 +74,9 @@ class ContributionViewHolderUnitTests { Whitebox.setInternalState(contributionViewHolder, "binding", bindind) setFinalStatic( - ContributionViewHolder::class.java.getDeclaredField("compositeDisposable"), - compositeDisposable) + ContributionViewHolder::class.java.getDeclaredField("compositeDisposable"), + compositeDisposable, + ) } @Test @@ -108,11 +101,12 @@ class ContributionViewHolderUnitTests { @Throws(Exception::class) fun testChooseImageSource() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = ContributionViewHolder::class.java.getDeclaredMethod( - "chooseImageSource", - String::class.java, - Uri::class.java - ) + val method: Method = + ContributionViewHolder::class.java.getDeclaredMethod( + "chooseImageSource", + String::class.java, + Uri::class.java, + ) method.isAccessible = true method.invoke(contributionViewHolder, "", uri) } @@ -121,10 +115,11 @@ class ContributionViewHolderUnitTests { @Throws(Exception::class) fun testDisplayWikipediaButton() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = ContributionViewHolder::class.java.getDeclaredMethod( - "displayWikipediaButton", - Boolean::class.javaObjectType - ) + val method: Method = + ContributionViewHolder::class.java.getDeclaredMethod( + "displayWikipediaButton", + Boolean::class.javaObjectType, + ) method.isAccessible = true method.invoke(contributionViewHolder, false) } @@ -134,10 +129,11 @@ class ContributionViewHolderUnitTests { fun testCheckIfMediaExistsOnWikipediaPageCaseNull() { Shadows.shadowOf(Looper.getMainLooper()).idle() `when`(contribution.wikidataPlace).thenReturn(null) - val method: Method = ContributionViewHolder::class.java.getDeclaredMethod( - "checkIfMediaExistsOnWikipediaPage", - Contribution::class.java - ) + val method: Method = + ContributionViewHolder::class.java.getDeclaredMethod( + "checkIfMediaExistsOnWikipediaPage", + Contribution::class.java, + ) method.isAccessible = true method.invoke(contributionViewHolder, contribution) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt index 098bab8523..4fd5689da4 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt @@ -3,7 +3,11 @@ package fr.free.nrw.commons.contributions import android.content.Context import android.os.Bundle import android.os.Looper -import android.view.* +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View import android.widget.LinearLayout import android.widget.TextView import androidx.fragment.app.FragmentManager @@ -12,10 +16,9 @@ import androidx.test.core.app.ApplicationProvider import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.campaigns.CampaignView import fr.free.nrw.commons.campaigns.models.Campaign -import fr.free.nrw.commons.databinding.FragmentContributionsBinding +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.media.MediaDetailPagerFragment import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient @@ -29,7 +32,10 @@ import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.* +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` @@ -46,7 +52,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ContributionsFragmentUnitTests { - @Mock private lateinit var mediaDetailPagerFragment: MediaDetailPagerFragment @@ -105,7 +110,6 @@ class ContributionsFragmentUnitTests { context = ApplicationProvider.getApplicationContext() activity = Robolectric.buildActivity(MainActivity::class.java).create().get() - fragment = ContributionsFragment.newInstance() val fragmentManager: FragmentManager = activity.supportFragmentManager val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction() @@ -113,8 +117,10 @@ class ContributionsFragmentUnitTests { fragmentTransaction.commit() layoutInflater = LayoutInflater.from(activity) - view = LayoutInflater.from(activity) - .inflate(R.layout.fragment_contributions, null) as View + view = + LayoutInflater + .from(activity) + .inflate(R.layout.fragment_contributions, null) as View nearbyNotificationCardView = view.findViewById(R.id.card_view_nearby) campaignView = view.findViewById(R.id.campaigns_view) @@ -157,17 +163,20 @@ class ContributionsFragmentUnitTests { Shadows.shadowOf(Looper.getMainLooper()).idle() `when`(notificationController.getNotifications(anyBoolean())).thenReturn(singleNotification) `when`(notificationController.getNotifications(anyBoolean()).subscribeOn(any())).thenReturn( - singleNotification + singleNotification, ) `when`( notificationController.getNotifications(anyBoolean()).subscribeOn(any()).observeOn( - any() - ) + any(), + ), ).thenReturn(singleNotification) `when`( - notificationController.getNotifications(anyBoolean()).subscribeOn(any()).observeOn( - any() - ).subscribe() + notificationController + .getNotifications(anyBoolean()) + .subscribeOn(any()) + .observeOn( + any(), + ).subscribe(), ).thenReturn(compositeDisposable) fragment.setNotificationCount() } @@ -176,10 +185,11 @@ class ContributionsFragmentUnitTests { @Throws(Exception::class) fun testInitNotificationViewsCaseEmptyList() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = ContributionsFragment::class.java.getDeclaredMethod( - "initNotificationViews", - List::class.java - ) + val method: Method = + ContributionsFragment::class.java.getDeclaredMethod( + "initNotificationViews", + List::class.java, + ) method.isAccessible = true method.invoke(fragment, listOf()) } @@ -190,10 +200,11 @@ class ContributionsFragmentUnitTests { Shadows.shadowOf(Looper.getMainLooper()).idle() val list: List = listOf(Notification(NotificationType.UNKNOWN, "", "", "", "", "")) - val method: Method = ContributionsFragment::class.java.getDeclaredMethod( - "initNotificationViews", - List::class.java - ) + val method: Method = + ContributionsFragment::class.java.getDeclaredMethod( + "initNotificationViews", + List::class.java, + ) method.isAccessible = true method.invoke(fragment, list) } @@ -209,7 +220,7 @@ class ContributionsFragmentUnitTests { @Test @Throws(Exception::class) - fun testScrollToTop(){ + fun testScrollToTop() { Shadows.shadowOf(Looper.getMainLooper()).idle() fragment.scrollToTop() verify(contributionsListFragment).scrollToTop() @@ -348,5 +359,4 @@ class ContributionsFragmentUnitTests { `when`(mediaDetailPagerFragment.isVisible).thenReturn(false) fragment.showDetail(0, false) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsListFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsListFragmentUnitTests.kt index 00c0b12dce..db2475f63c 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsListFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsListFragmentUnitTests.kt @@ -30,7 +30,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ContributionsListFragmentUnitTests { - private lateinit var scenario: FragmentScenario private lateinit var fragment: ContributionsListFragment @@ -43,17 +42,19 @@ class ContributionsListFragmentUnitTests { fun setUp() { OkHttpConnectionFactory.CLIENT = createTestClient() - scenario = launchFragmentInContainer( - initialState = Lifecycle.State.RESUMED, - themeResId = R.style.LightAppTheme - ) { - ContributionsListFragment().apply { - contributionsListPresenter = mock() - callback = mock() - }.also { - fragment = it + scenario = + launchFragmentInContainer( + initialState = Lifecycle.State.RESUMED, + themeResId = R.style.LightAppTheme, + ) { + ContributionsListFragment() + .apply { + contributionsListPresenter = mock() + callback = mock() + }.also { + fragment = it + } } - } scenario.onFragment { it.adapter = adapter @@ -129,10 +130,11 @@ class ContributionsListFragmentUnitTests { @Throws(Exception::class) fun testShowAddImageToWikipediaInstructions() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = ContributionsListFragment::class.java.getDeclaredMethod( - "showAddImageToWikipediaInstructions", - Contribution::class.java - ) + val method: Method = + ContributionsListFragment::class.java.getDeclaredMethod( + "showAddImageToWikipediaInstructions", + Contribution::class.java, + ) method.isAccessible = true method.invoke(fragment, contribution) } @@ -193,10 +195,11 @@ class ContributionsListFragmentUnitTests { scenario.onFragment { it.requireView().findViewById(R.id.fab_plus).hide() } - val method: Method = ContributionsListFragment::class.java.getDeclaredMethod( - "animateFAB", - Boolean::class.java - ) + val method: Method = + ContributionsListFragment::class.java.getDeclaredMethod( + "animateFAB", + Boolean::class.java, + ) method.isAccessible = true method.invoke(fragment, true) } @@ -208,10 +211,11 @@ class ContributionsListFragmentUnitTests { scenario.onFragment { it.requireView().findViewById(R.id.fab_plus).show() } - val method: Method = ContributionsListFragment::class.java.getDeclaredMethod( - "animateFAB", - Boolean::class.java - ) + val method: Method = + ContributionsListFragment::class.java.getDeclaredMethod( + "animateFAB", + Boolean::class.java, + ) method.isAccessible = true method.invoke(fragment, true) } @@ -223,10 +227,11 @@ class ContributionsListFragmentUnitTests { scenario.onFragment { it.requireView().findViewById(R.id.fab_plus).show() } - val method: Method = ContributionsListFragment::class.java.getDeclaredMethod( - "animateFAB", - Boolean::class.java - ) + val method: Method = + ContributionsListFragment::class.java.getDeclaredMethod( + "animateFAB", + Boolean::class.java, + ) method.isAccessible = true method.invoke(fragment, false) } @@ -235,9 +240,10 @@ class ContributionsListFragmentUnitTests { @Throws(Exception::class) fun testSetListeners() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = ContributionsListFragment::class.java.getDeclaredMethod( - "setListeners" - ) + val method: Method = + ContributionsListFragment::class.java.getDeclaredMethod( + "setListeners", + ) method.isAccessible = true method.invoke(fragment) } @@ -246,9 +252,10 @@ class ContributionsListFragmentUnitTests { @Throws(Exception::class) fun testInitializeAnimations() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = ContributionsListFragment::class.java.getDeclaredMethod( - "initializeAnimations" - ) + val method: Method = + ContributionsListFragment::class.java.getDeclaredMethod( + "initializeAnimations", + ) method.isAccessible = true method.invoke(fragment) } @@ -261,4 +268,4 @@ class ContributionsListFragmentUnitTests { newConfig.orientation = Configuration.ORIENTATION_LANDSCAPE fragment.onConfigurationChanged(newConfig) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsListPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsListPresenterTest.kt index ff47f66f03..5c35f71eda 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsListPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsListPresenterTest.kt @@ -2,18 +2,11 @@ package fr.free.nrw.commons.contributions import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.nhaarman.mockitokotlin2.times -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever -import io.reactivex.Completable import io.reactivex.Scheduler import io.reactivex.schedulers.Schedulers import org.junit.Before import org.junit.Rule -import org.junit.Test -import org.mockito.ArgumentMatchers -import org.mockito.ArgumentMatchers.any import org.mockito.Mock -import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations /** @@ -50,8 +43,7 @@ class ContributionsListPresenterTest { contributionBoundaryCallback, remoteDataSource, repository, - scheduler - ); + scheduler, + ) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt index d75fc22854..de226c97c8 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsPresenterTest.kt @@ -7,14 +7,11 @@ import androidx.lifecycle.MutableLiveData import androidx.loader.content.CursorLoader import androidx.loader.content.Loader import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.repository.UploadRepository -import io.reactivex.Completable import io.reactivex.schedulers.TestScheduler import org.junit.Before import org.junit.Rule import org.junit.Test -import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations @@ -72,6 +69,4 @@ class ContributionsPresenterTest { contributionsPresenter.getContributionsWithTitle("ashish") verify(repository).getContributionWithFileName("ashish") } - - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsRepositoryTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsRepositoryTest.kt index b90b4420cb..b98603b494 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsRepositoryTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsRepositoryTest.kt @@ -8,8 +8,11 @@ import io.reactivex.Scheduler import io.reactivex.Single import org.junit.Before import org.junit.Test -import org.mockito.* +import org.mockito.ArgumentMatchers +import org.mockito.InjectMocks +import org.mockito.Mock import org.mockito.Mockito.mock +import org.mockito.MockitoAnnotations /** * The unit test class for ContributionsRepositoryTest @@ -38,7 +41,7 @@ class ContributionsRepositoryTest { whenever(localDataSource.getContributions()) .thenReturn(createMockDataSourceFactory(listOf(contribution))) val contributionsFactory = contributionsRepository.fetchContributions() - verify(localDataSource, times(1)).getContributions(); + verify(localDataSource, times(1)).getContributions() } @Test @@ -46,8 +49,9 @@ class ContributionsRepositoryTest { val contributions = listOf(mock(Contribution::class.java)) whenever(localDataSource.saveContributions(ArgumentMatchers.anyList())) .thenReturn(Single.just(listOf(1L))) - val save = contributionsRepository.save(contributions).test().assertValueAt(0) { - it.size == 1 && it.get(0) == 1L - } + val save = + contributionsRepository.save(contributions).test().assertValueAt(0) { + it.size == 1 && it.get(0) == 1L + } } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/MainActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/MainActivityUnitTests.kt index 44330fabb6..a2eb41615a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/MainActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/MainActivityUnitTests.kt @@ -9,13 +9,12 @@ import androidx.fragment.app.Fragment import androidx.test.core.app.ApplicationProvider import androidx.work.Configuration import androidx.work.testing.WorkManagerTestInitHelper -import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.bookmarks.BookmarkFragment import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.explore.ExploreFragment import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.navtab.NavTabLayout @@ -40,12 +39,10 @@ import org.robolectric.fakes.RoboMenuItem import java.lang.reflect.Field import java.lang.reflect.Method - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class MainActivityUnitTests { - private lateinit var activity: MainActivity private lateinit var context: Context private lateinit var menuItem: RoboMenuItem @@ -198,9 +195,10 @@ class MainActivityUnitTests { @Test @Throws(Exception::class) fun testSetUpPager() { - val method: Method = MainActivity::class.java.getDeclaredMethod( - "setUpPager" - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "setUpPager", + ) method.isAccessible = true method.invoke(activity) } @@ -208,9 +206,10 @@ class MainActivityUnitTests { @Test @Throws(Exception::class) fun testSetUpLoggedOutPager() { - val method: Method = MainActivity::class.java.getDeclaredMethod( - "setUpLoggedOutPager" - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "setUpLoggedOutPager", + ) method.isAccessible = true method.invoke(activity) } @@ -218,11 +217,12 @@ class MainActivityUnitTests { @Test @Throws(Exception::class) fun testLoadFragmentCaseContributionsFragment() { - val method: Method = MainActivity::class.java.getDeclaredMethod( - "loadFragment", - Fragment::class.java, - Boolean::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "loadFragment", + Fragment::class.java, + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, contributionsFragment, false) } @@ -232,14 +232,15 @@ class MainActivityUnitTests { fun testLoadFragmentCaseContributionsFragmentCaseTrue() { activeFragment = ActiveFragment.CONTRIBUTIONS activity.activeFragment = activeFragment - val method: Method = MainActivity::class.java.getDeclaredMethod( - "loadFragment", - Fragment::class.java, - Boolean::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "loadFragment", + Fragment::class.java, + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, contributionsFragment, false) - verify(contributionsFragment).scrollToTop(); + verify(contributionsFragment).scrollToTop() } @Test @@ -247,11 +248,12 @@ class MainActivityUnitTests { fun testLoadFragmentCaseNearbyParentFragmentCaseTrue() { activeFragment = ActiveFragment.NEARBY activity.activeFragment = activeFragment - val method: Method = MainActivity::class.java.getDeclaredMethod( - "loadFragment", - Fragment::class.java, - Boolean::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "loadFragment", + Fragment::class.java, + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, nearbyParentFragment, false) } @@ -259,11 +261,12 @@ class MainActivityUnitTests { @Test @Throws(Exception::class) fun testLoadFragmentCaseNearbyParentFragment() { - val method: Method = MainActivity::class.java.getDeclaredMethod( - "loadFragment", - Fragment::class.java, - Boolean::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "loadFragment", + Fragment::class.java, + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, nearbyParentFragment, false) } @@ -273,11 +276,12 @@ class MainActivityUnitTests { fun testLoadFragmentCaseExploreFragmentCaseTrue() { activeFragment = ActiveFragment.EXPLORE activity.activeFragment = activeFragment - val method: Method = MainActivity::class.java.getDeclaredMethod( - "loadFragment", - Fragment::class.java, - Boolean::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "loadFragment", + Fragment::class.java, + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, exploreFragment, false) } @@ -285,11 +289,12 @@ class MainActivityUnitTests { @Test @Throws(Exception::class) fun testLoadFragmentCaseExploreFragment() { - val method: Method = MainActivity::class.java.getDeclaredMethod( - "loadFragment", - Fragment::class.java, - Boolean::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "loadFragment", + Fragment::class.java, + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, exploreFragment, false) } @@ -299,11 +304,12 @@ class MainActivityUnitTests { fun testLoadFragmentCaseBookmarkFragmentCaseTrue() { activeFragment = ActiveFragment.BOOKMARK activity.activeFragment = activeFragment - val method: Method = MainActivity::class.java.getDeclaredMethod( - "loadFragment", - Fragment::class.java, - Boolean::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "loadFragment", + Fragment::class.java, + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, bookmarkFrament, false) } @@ -311,11 +317,12 @@ class MainActivityUnitTests { @Test @Throws(Exception::class) fun testLoadFragmentCaseBookmarkFragment() { - val method: Method = MainActivity::class.java.getDeclaredMethod( - "loadFragment", - Fragment::class.java, - Boolean::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "loadFragment", + Fragment::class.java, + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, bookmarkFrament, false) } @@ -323,11 +330,12 @@ class MainActivityUnitTests { @Test @Throws(Exception::class) fun testLoadFragmentCaseNull() { - val method: Method = MainActivity::class.java.getDeclaredMethod( - "loadFragment", - Fragment::class.java, - Boolean::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "loadFragment", + Fragment::class.java, + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, null, true) } @@ -335,12 +343,13 @@ class MainActivityUnitTests { @Test @Throws(Exception::class) fun testOnActivityResult() { - val method: Method = MainActivity::class.java.getDeclaredMethod( - "onActivityResult", - Int::class.java, - Int::class.java, - Intent::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "onActivityResult", + Int::class.java, + Int::class.java, + Intent::class.java, + ) method.isAccessible = true method.invoke(activity, 0, 0, null) } @@ -348,9 +357,10 @@ class MainActivityUnitTests { @Test @Throws(Exception::class) fun testOnResume() { - val method: Method = MainActivity::class.java.getDeclaredMethod( - "onResume" - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "onResume", + ) method.isAccessible = true method.invoke(activity) } @@ -358,9 +368,10 @@ class MainActivityUnitTests { @Test @Throws(Exception::class) fun testOnDestroy() { - val method: Method = MainActivity::class.java.getDeclaredMethod( - "onDestroy" - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "onDestroy", + ) method.isAccessible = true method.invoke(activity) } @@ -368,10 +379,11 @@ class MainActivityUnitTests { @Test @Throws(Exception::class) fun testOnPostCreate() { - val method: Method = MainActivity::class.java.getDeclaredMethod( - "onPostCreate", - Bundle::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "onPostCreate", + Bundle::class.java, + ) method.isAccessible = true method.invoke(activity, null) } @@ -380,10 +392,11 @@ class MainActivityUnitTests { @Throws(Exception::class) fun testOnSaveInstanceState() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = MainActivity::class.java.getDeclaredMethod( - "onSaveInstanceState", - Bundle::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "onSaveInstanceState", + Bundle::class.java, + ) method.isAccessible = true method.invoke(activity, bundle) } @@ -393,10 +406,11 @@ class MainActivityUnitTests { fun testOnRestoreInstanceStateCaseContributions() { Shadows.shadowOf(Looper.getMainLooper()).idle() `when`(bundle.getString("activeFragment")).thenReturn(ActiveFragment.CONTRIBUTIONS.name) - val method: Method = MainActivity::class.java.getDeclaredMethod( - "onRestoreInstanceState", - Bundle::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "onRestoreInstanceState", + Bundle::class.java, + ) method.isAccessible = true method.invoke(activity, bundle) } @@ -406,10 +420,11 @@ class MainActivityUnitTests { fun testOnRestoreInstanceStateCaseNearby() { Shadows.shadowOf(Looper.getMainLooper()).idle() `when`(bundle.getString("activeFragment")).thenReturn(ActiveFragment.NEARBY.name) - val method: Method = MainActivity::class.java.getDeclaredMethod( - "onRestoreInstanceState", - Bundle::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "onRestoreInstanceState", + Bundle::class.java, + ) method.isAccessible = true method.invoke(activity, bundle) } @@ -419,10 +434,11 @@ class MainActivityUnitTests { fun testOnRestoreInstanceStateCaseExplore() { Shadows.shadowOf(Looper.getMainLooper()).idle() `when`(bundle.getString("activeFragment")).thenReturn(ActiveFragment.EXPLORE.name) - val method: Method = MainActivity::class.java.getDeclaredMethod( - "onRestoreInstanceState", - Bundle::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "onRestoreInstanceState", + Bundle::class.java, + ) method.isAccessible = true method.invoke(activity, bundle) } @@ -432,34 +448,34 @@ class MainActivityUnitTests { fun testOnRestoreInstanceStateCaseBookmark() { Shadows.shadowOf(Looper.getMainLooper()).idle() `when`(bundle.getString("activeFragment")).thenReturn(ActiveFragment.BOOKMARK.name) - val method: Method = MainActivity::class.java.getDeclaredMethod( - "onRestoreInstanceState", - Bundle::class.java - ) + val method: Method = + MainActivity::class.java.getDeclaredMethod( + "onRestoreInstanceState", + Bundle::class.java, + ) method.isAccessible = true method.invoke(activity, bundle) } @Test @Throws(Exception::class) - fun testOnSetUpPagerNearBy(){ + fun testOnSetUpPagerNearBy() { val item = Mockito.mock(MenuItem::class.java) `when`(item.title).thenReturn(activity.getString(R.string.nearby_fragment)) activity.navListener.onNavigationItemSelected(item) verify(item, Mockito.times(3)).title - verify(applicationKvStore,Mockito.times(1)) - .putBoolean("last_opened_nearby",true) + verify(applicationKvStore, Mockito.times(1)) + .putBoolean("last_opened_nearby", true) } @Test @Throws(Exception::class) - fun testOnSetUpPagerOtherThanNearBy(){ + fun testOnSetUpPagerOtherThanNearBy() { val item = Mockito.mock(MenuItem::class.java) `when`(item.title).thenReturn(activity.getString(R.string.bookmarks)) activity.navListener.onNavigationItemSelected(item) verify(item, Mockito.times(3)).title - verify(applicationKvStore,Mockito.times(1)) - .putBoolean("last_opened_nearby",false) + verify(applicationKvStore, Mockito.times(1)) + .putBoolean("last_opened_nearby", false) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/coordinates/CoordinateEditHelperUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/coordinates/CoordinateEditHelperUnitTest.kt index c364ccbde2..fb1c4db6b3 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/coordinates/CoordinateEditHelperUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/coordinates/CoordinateEditHelperUnitTest.kt @@ -17,7 +17,9 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock -import org.mockito.Mockito.* +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config @@ -28,7 +30,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class CoordinateEditHelperUnitTest { - private lateinit var context: Context private lateinit var helper: CoordinateEditHelper @@ -55,8 +56,8 @@ class CoordinateEditHelperUnitTest { pageEditClient.edit( anyString(), anyString(), - anyString() - ) + anyString(), + ), ).thenReturn(Observable.just(true)) } @@ -72,7 +73,7 @@ class CoordinateEditHelperUnitTest { helper.makeCoordinatesEdit(context, media, "0.0", "0.0", "0.0F") verify(viewUtilWrapper, times(1)).showShortToast( context, - context.getString(R.string.coordinates_edit_helper_make_edit_toast) + context.getString(R.string.coordinates_edit_helper_make_edit_toast), ) verify(pageEditClient, times(1)).getCurrentWikiText(anyString()) verify(pageEditClient, times(1)).edit(anyString(), anyString(), anyString()) @@ -81,94 +82,118 @@ class CoordinateEditHelperUnitTest { @Test @Throws(Exception::class) fun testGetFormattedWikiTextCaseNewLineContainsLocation() { - val method: Method = CoordinateEditHelper::class.java.getDeclaredMethod( - "getFormattedWikiText", String::class.java, String::class.java - ) + val method: Method = + CoordinateEditHelper::class.java.getDeclaredMethod( + "getFormattedWikiText", + String::class.java, + String::class.java, + ) method.isAccessible = true assertEquals( method.invoke( helper, "== {{int:filedesc}} == {{user|Test}} == \n{{Location|0.0|0.0|0.0F}}", - "{{Location|0.1|0.1|0.1F}}" - ), "== {{int:filedesc}} == {{user|Test}} == \n" + - "{Location|0.1|0.1|0.1F}}\n" + "{{Location|0.1|0.1|0.1F}}", + ), + "== {{int:filedesc}} == {{user|Test}} == \n" + + "{Location|0.1|0.1|0.1F}}\n", ) } @Test @Throws(Exception::class) fun testGetFormattedWikiTextCaseContainsLocation() { - val method: Method = CoordinateEditHelper::class.java.getDeclaredMethod( - "getFormattedWikiText", String::class.java, String::class.java - ) + val method: Method = + CoordinateEditHelper::class.java.getDeclaredMethod( + "getFormattedWikiText", + String::class.java, + String::class.java, + ) method.isAccessible = true assertEquals( method.invoke( helper, "== {{int:filedesc}} == {{Location|0.0|0.0|0.0F}}", - "{{Location|0.1|0.1|0.1F}}" - ), "== {{int:filedesc}} == {{Location|0.1|0.1|0.1F}}" + "{{Location|0.1|0.1|0.1F}}", + ), + "== {{int:filedesc}} == {{Location|0.1|0.1|0.1F}}", ) } @Test @Throws(Exception::class) fun testGetFormattedWikiTextCaseDoesContainsLocationHasSubString() { - val method: Method = CoordinateEditHelper::class.java.getDeclaredMethod( - "getFormattedWikiText", String::class.java, String::class.java - ) + val method: Method = + CoordinateEditHelper::class.java.getDeclaredMethod( + "getFormattedWikiText", + String::class.java, + String::class.java, + ) method.isAccessible = true assertEquals( method.invoke( helper, "== {{int:filedesc}} == {{user|Test}} == {{int:license-header}} ==", - "{{Location|0.1|0.1|0.1F}}" + "{{Location|0.1|0.1|0.1F}}", ), "== {{int:filedesc}} == {{user|Test}} {Location|0.1|0.1|0.1F}}\n" + - "== {{int:license-header}} ==" + "== {{int:license-header}} ==", ) } @Test @Throws(Exception::class) fun testGetFormattedWikiTextCaseDoesContainsLocationDoesNotHaveSubString() { - val method: Method = CoordinateEditHelper::class.java.getDeclaredMethod( - "getFormattedWikiText", String::class.java, String::class.java - ) + val method: Method = + CoordinateEditHelper::class.java.getDeclaredMethod( + "getFormattedWikiText", + String::class.java, + String::class.java, + ) method.isAccessible = true assertEquals( method.invoke( helper, "== {{int:filedesc}} {{int:license-header}} ==", - "{{Location|0.1|0.1|0.1F}}" + "{{Location|0.1|0.1|0.1F}}", ), - "== {{int:filedesc}} {{int:license-header}} =={{Location|0.1|0.1|0.1F}}" + "== {{int:filedesc}} {{int:license-header}} =={{Location|0.1|0.1|0.1F}}", ) } @Test @Throws(Exception::class) fun testGetFormattedWikiTextCaseDoesNotContainFileDesc() { - val method: Method = CoordinateEditHelper::class.java.getDeclaredMethod( - "getFormattedWikiText", String::class.java, String::class.java - ) + val method: Method = + CoordinateEditHelper::class.java.getDeclaredMethod( + "getFormattedWikiText", + String::class.java, + String::class.java, + ) method.isAccessible = true assertEquals( method.invoke( helper, "{{Location|0.0|0.0|0.0F}}", - "{{Location|0.1|0.1|0.1F}}" - ), "== {{int:filedesc}} =={{Location|0.1|0.1|0.1F}}{{Location|0.0|0.0|0.0F}}" + "{{Location|0.1|0.1|0.1F}}", + ), + "== {{int:filedesc}} =={{Location|0.1|0.1|0.1F}}{{Location|0.0|0.0|0.0F}}", ) } @Test @Throws(Exception::class) fun testShowCoordinatesEditNotificationCaseTrue() { - val method: Method = CoordinateEditHelper::class.java.getDeclaredMethod( - "showCoordinatesEditNotification", Context::class.java, Media::class.java, - String::class.java, String::class.java, String::class.java, Boolean::class.java - ) + val method: Method = + CoordinateEditHelper::class.java.getDeclaredMethod( + "showCoordinatesEditNotification", + Context::class.java, + Media::class.java, + String::class.java, + String::class.java, + String::class.java, + Boolean::class.java, + ) method.isAccessible = true assertEquals(method.invoke(helper, context, media, "0.0", "0.0", "0.0F", true), true) } @@ -176,12 +201,17 @@ class CoordinateEditHelperUnitTest { @Test @Throws(Exception::class) fun testShowCoordinatesEditNotificationCaseFalse() { - val method: Method = CoordinateEditHelper::class.java.getDeclaredMethod( - "showCoordinatesEditNotification", Context::class.java, Media::class.java, - String::class.java, String::class.java, String::class.java, Boolean::class.java - ) + val method: Method = + CoordinateEditHelper::class.java.getDeclaredMethod( + "showCoordinatesEditNotification", + Context::class.java, + Media::class.java, + String::class.java, + String::class.java, + String::class.java, + Boolean::class.java, + ) method.isAccessible = true assertEquals(method.invoke(helper, context, media, "0.0", "0.0", "0.0F", false), false) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/customselector/helper/ImageHelperTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/helper/ImageHelperTest.kt index 880eca38bd..d8b501522d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/customselector/helper/ImageHelperTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/helper/ImageHelperTest.kt @@ -3,16 +3,14 @@ package fr.free.nrw.commons.customselector.helper import android.net.Uri import fr.free.nrw.commons.customselector.model.Folder import fr.free.nrw.commons.customselector.model.Image -import org.junit.jupiter.api.Assertions.* - import org.junit.Test +import org.junit.jupiter.api.Assertions.assertEquals import org.mockito.Mockito.mock /** * Custom Selector Image Helper Test */ internal class ImageHelperTest { - var uri: Uri = mock(Uri::class.java) private val folderImage1 = Image(1, "image1", uri, "abc/abc", 1, "bucket1") private val folderImage2 = Image(2, "image1", uri, "xyz/xyz", 2, "bucket2") @@ -42,7 +40,7 @@ internal class ImageHelperTest { */ @Test fun getIndex() { - assertEquals(1,ImageHelper.getIndex(mockImageList, folderImage2)) + assertEquals(1, ImageHelper.getIndex(mockImageList, folderImage2)) } /** @@ -52,4 +50,4 @@ internal class ImageHelperTest { fun getIndexList() { assertEquals(ArrayList(listOf(0)), ImageHelper.getIndexList(mockImageList, folderImageList2)) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListenerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListenerTest.kt index 4e206f2a67..b1bd7ac35e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListenerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/helper/OnSwipeTouchListenerTest.kt @@ -8,8 +8,8 @@ import androidx.test.core.app.ApplicationProvider import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.TestUtility.setFinalStatic +import fr.free.nrw.commons.createTestClient import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -23,7 +23,6 @@ import org.robolectric.annotation.Config @Config(sdk = [21], application = TestCommonsApplication::class) @PrepareForTest(OnSwipeTouchListener::class) internal class OnSwipeTouchListenerTest { - private lateinit var context: Context private lateinit var onSwipeTouchListener: OnSwipeTouchListener private lateinit var gesListener: OnSwipeTouchListener.GestureListener @@ -49,8 +48,9 @@ internal class OnSwipeTouchListenerTest { onSwipeTouchListener = OnSwipeTouchListener(context) gesListener = OnSwipeTouchListener(context).GestureListener() setFinalStatic( - OnSwipeTouchListener::class.java.getDeclaredField("gestureDetector"), - gestureDetector) + OnSwipeTouchListener::class.java.getDeclaredField("gestureDetector"), + gestureDetector, + ) } /** @@ -58,13 +58,12 @@ internal class OnSwipeTouchListenerTest { */ @Test fun onTouch() { - val motionEvent = MotionEvent.obtain(200, 300, MotionEvent.ACTION_MOVE, 15.0f, 10.0f, 0); + val motionEvent = MotionEvent.obtain(200, 300, MotionEvent.ACTION_MOVE, 15.0f, 10.0f, 0) val func = onSwipeTouchListener.javaClass.getDeclaredMethod("onTouch", View::class.java, MotionEvent::class.java) func.isAccessible = true func.invoke(onSwipeTouchListener, view, motionEvent) } - /** * Test onSwipeRight */ @@ -144,4 +143,4 @@ internal class OnSwipeTouchListenerTest { whenever(motionEvent2.y).thenReturn(1f) gesListener.onFling(motionEvent1, motionEvent2, 0f, 2000f) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapterTest.kt index a08acbeef0..08dadca25a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapterTest.kt @@ -1,7 +1,6 @@ package fr.free.nrw.commons.customselector.ui.adapter import android.content.ContentResolver -import fr.free.nrw.commons.R import android.content.Context import android.net.Uri import android.view.LayoutInflater @@ -9,8 +8,8 @@ import android.view.View import android.widget.GridLayout import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.whenever +import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.TestUtility.setFinalStatic import fr.free.nrw.commons.customselector.listeners.FolderClickListener import fr.free.nrw.commons.customselector.model.Folder import fr.free.nrw.commons.customselector.model.Image @@ -22,12 +21,9 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations -import org.powermock.reflect.Whitebox import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import java.lang.reflect.Field -import java.lang.reflect.Modifier /** * Custom Selector Folder Adapter Test. @@ -35,7 +31,6 @@ import java.lang.reflect.Modifier @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class FolderAdapterTest { - private var uri: Uri = Mockito.mock(Uri::class.java) private lateinit var activity: CustomSelectorActivity private lateinit var folderAdapter: FolderAdapter @@ -98,4 +93,4 @@ class FolderAdapterTest { fun getItemCount() { assertEquals(0, folderAdapter.itemCount) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapterTest.kt index 93a1ea8f97..2a4c8c7915 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapterTest.kt @@ -10,7 +10,6 @@ import android.widget.GridLayout import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.TestUtility.setFinalStatic import fr.free.nrw.commons.customselector.listeners.ImageSelectListener import fr.free.nrw.commons.customselector.model.Image import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity @@ -34,8 +33,7 @@ import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import java.lang.reflect.Field -import java.lang.reflect.Modifier -import java.util.* +import java.util.TreeMap import kotlin.collections.ArrayList /** @@ -47,25 +45,28 @@ import kotlin.collections.ArrayList class ImageAdapterTest { @Mock private lateinit var imageLoader: ImageLoader + @Mock private lateinit var imageSelectListener: ImageSelectListener + @Mock private lateinit var context: Context + @Mock private lateinit var mockContentResolver: ContentResolver + @Mock private lateinit var sharedPreferences: SharedPreferences private lateinit var activity: CustomSelectorActivity private lateinit var imageAdapter: ImageAdapter - private lateinit var images : ArrayList + private lateinit var images: ArrayList private lateinit var holder: ImageAdapter.ImageViewHolder private lateinit var selectedImageField: Field private var uri: Uri = Mockito.mock(Uri::class.java) private lateinit var image: Image private val testDispatcher = TestCoroutineDispatcher() - /** * Set up variables. */ @@ -106,7 +107,6 @@ class ImageAdapterTest { */ @Test fun onBindViewHolder() { - whenever(context.contentResolver).thenReturn(mockContentResolver) whenever(mockContentResolver.getType(uri)).thenReturn("jpg") // Parameters. @@ -125,22 +125,38 @@ class ImageAdapterTest { * Test processThumbnailForActionedImage */ @Test - fun processThumbnailForActionedImage() = runBlocking { - Whitebox.setInternalState(imageAdapter, "allImages", listOf(image)) - whenever(imageLoader.nextActionableImage(listOf(image), Dispatchers.IO, Dispatchers.Default, - 0, emptyList())).thenReturn(0) - imageAdapter.processThumbnailForActionedImage(holder, 0, emptyList()) - } + fun processThumbnailForActionedImage() = + runBlocking { + Whitebox.setInternalState(imageAdapter, "allImages", listOf(image)) + whenever( + imageLoader.nextActionableImage( + listOf(image), + Dispatchers.IO, + Dispatchers.Default, + 0, + emptyList(), + ), + ).thenReturn(0) + imageAdapter.processThumbnailForActionedImage(holder, 0, emptyList()) + } /** * Test processThumbnailForActionedImage */ @Test - fun `processThumbnailForActionedImage when reached end of the folder`() = runBlocking { - whenever(imageLoader.nextActionableImage(ArrayList(), Dispatchers.IO, Dispatchers.Default, - 0, emptyList())).thenReturn(-1) - imageAdapter.processThumbnailForActionedImage(holder, 0, emptyList()) - } + fun `processThumbnailForActionedImage when reached end of the folder`() = + runBlocking { + whenever( + imageLoader.nextActionableImage( + ArrayList(), + Dispatchers.IO, + Dispatchers.Default, + 0, + emptyList(), + ), + ).thenReturn(-1) + imageAdapter.processThumbnailForActionedImage(holder, 0, emptyList()) + } /** * Test init. @@ -156,7 +172,12 @@ class ImageAdapterTest { @Test fun selectOrRemoveImage() { // Access function - val func = imageAdapter.javaClass.getDeclaredMethod("selectOrRemoveImage", ImageAdapter.ImageViewHolder::class.java, Int::class.java) + val func = + imageAdapter.javaClass.getDeclaredMethod( + "selectOrRemoveImage", + ImageAdapter.ImageViewHolder::class.java, + Int::class.java, + ) func.isAccessible = true // Parameters @@ -183,11 +204,12 @@ class ImageAdapterTest { images.add(image) Whitebox.setInternalState(imageAdapter, "images", images) // Access function - val func = imageAdapter.javaClass.getDeclaredMethod( - "onThumbnailClicked", - Int::class.java, - ImageAdapter.ImageViewHolder::class.java - ) + val func = + imageAdapter.javaClass.getDeclaredMethod( + "onThumbnailClicked", + Int::class.java, + ImageAdapter.ImageViewHolder::class.java, + ) func.isAccessible = true func.invoke(imageAdapter, 0, holder) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivityTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivityTest.kt index efa04d1b2a..0274fed7d3 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivityTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivityTest.kt @@ -30,12 +30,11 @@ import java.lang.reflect.Method @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class CustomSelectorActivityTest { - private lateinit var activity: CustomSelectorActivity private lateinit var imageFragment: ImageFragment - private lateinit var images : java.util.ArrayList + private lateinit var images: java.util.ArrayList private var uri: Uri = Mockito.mock(Uri::class.java) @@ -52,18 +51,20 @@ class CustomSelectorActivityTest { MockitoAnnotations.openMocks(this) OkHttpConnectionFactory.CLIENT = createTestClient() - activity = Robolectric.buildActivity(CustomSelectorActivity::class.java) - .get() + activity = + Robolectric + .buildActivity(CustomSelectorActivity::class.java) + .get() val onCreate = activity.javaClass.getDeclaredMethod("onCreate", Bundle::class.java) onCreate.isAccessible = true onCreate.invoke(activity, null) - imageFragment = ImageFragment.newInstance(1,0) + imageFragment = ImageFragment.newInstance(1, 0) image = Image(1, "image", uri, "abc/abc", 1, "bucket1") images = ArrayList() Whitebox.setInternalState(activity, "imageFragment", imageFragment) Whitebox.setInternalState(imageFragment, "imageAdapter", Mockito.mock(ImageAdapter::class.java)) - Whitebox.setInternalState(imageFragment,"contributionDao",contributionDao) + Whitebox.setInternalState(imageFragment, "contributionDao", contributionDao) } /** @@ -93,7 +94,7 @@ class CustomSelectorActivityTest { @Test @Throws(Exception::class) fun testOnFolderClick() { - activity.onFolderClick(1, "test", 0); + activity.onFolderClick(1, "test", 0) } /** @@ -102,12 +103,13 @@ class CustomSelectorActivityTest { @Test @Throws(Exception::class) fun testOnActivityResult() { - val func = activity.javaClass.getDeclaredMethod( - "onActivityResult", - Int::class.java, - Int::class.java, - Intent::class.java - ) + val func = + activity.javaClass.getDeclaredMethod( + "onActivityResult", + Int::class.java, + Int::class.java, + Intent::class.java, + ) func.isAccessible = true func.invoke(activity, 512, -1, Mockito.mock(Intent::class.java)) } @@ -118,26 +120,27 @@ class CustomSelectorActivityTest { @Test @Throws(Exception::class) fun testShowWelcomeDialog() { - val func = activity.javaClass.getDeclaredMethod( - "showWelcomeDialog" - ) + val func = + activity.javaClass.getDeclaredMethod( + "showWelcomeDialog", + ) func.isAccessible = true func.invoke(activity) } - /** * Test onLongPress function. */ @Test @Throws(Exception::class) fun testOnLongPress() { - val func = activity.javaClass.getDeclaredMethod( - "onLongPress", - Int::class.java, - ArrayList::class.java, - ArrayList::class.java - ) + val func = + activity.javaClass.getDeclaredMethod( + "onLongPress", + Int::class.java, + ArrayList::class.java, + ArrayList::class.java, + ) images.add(image) func.isAccessible = true @@ -164,7 +167,7 @@ class CustomSelectorActivityTest { activity.onFolderClick(1, "test", 0) activity.onSelectedImagesChanged( ArrayList(arrayListOf(Image(1, "test", Uri.parse("test"), "test", 1))), - 1 + 1, ) activity.onDone() } @@ -176,14 +179,15 @@ class CustomSelectorActivityTest { @Throws(Exception::class) fun testOnClickNotForUpload() { activity.onFolderClick(1, "test", 0) - val method: Method = CustomSelectorActivity::class.java.getDeclaredMethod( - "onClickNotForUpload" - ) + val method: Method = + CustomSelectorActivity::class.java.getDeclaredMethod( + "onClickNotForUpload", + ) method.isAccessible = true method.invoke(activity) activity.onSelectedImagesChanged( ArrayList(arrayListOf(Image(1, "test", Uri.parse("test"), "test", 1))), - 0 + 0, ) method.invoke(activity) } @@ -212,9 +216,10 @@ class CustomSelectorActivityTest { @Test @Throws(Exception::class) fun testOnDestroy() { - val method: Method = CustomSelectorActivity::class.java.getDeclaredMethod( - "onDestroy" - ) + val method: Method = + CustomSelectorActivity::class.java.getDeclaredMethod( + "onDestroy", + ) method.isAccessible = true method.invoke(activity) } @@ -225,9 +230,10 @@ class CustomSelectorActivityTest { @Test @Throws(Exception::class) fun testDisplayUploadLimitWarning() { - val method: Method = CustomSelectorActivity::class.java.getDeclaredMethod( - "displayUploadLimitWarning" - ) + val method: Method = + CustomSelectorActivity::class.java.getDeclaredMethod( + "displayUploadLimitWarning", + ) method.isAccessible = true method.invoke(activity) } @@ -252,11 +258,19 @@ class CustomSelectorActivityTest { // test with list size limit for (i in 1..limit.getInt(activity)) { - images.add(Image(i.toLong(), i.toString(), uri, - "abc/abc", 1, "bucket1")) + images.add( + Image( + i.toLong(), + i.toString(), + uri, + "abc/abc", + 1, + "bucket1", + ), + ) } activity.onFolderClick(1, "test", 0) - activity.onSelectedImagesChanged(images,0) + activity.onSelectedImagesChanged(images, 0) assertEquals(false, overLimit.getBoolean(activity)) assertEquals(0, exceededBy.getInt(activity)) activity.onSelectedImagesChanged(images, 1) @@ -265,20 +279,20 @@ class CustomSelectorActivityTest { // test with list size limit+1 images.add(image) - activity.onSelectedImagesChanged(images,0) + activity.onSelectedImagesChanged(images, 0) assertEquals(true, overLimit.getBoolean(activity)) assertEquals(1, exceededBy.getInt(activity)) - activity.onSelectedImagesChanged(images,1) + activity.onSelectedImagesChanged(images, 1) assertEquals(true, overLimit.getBoolean(activity)) assertEquals(1, exceededBy.getInt(activity)) - //test with list size 1 + // test with list size 1 images.clear() images.add(image) - activity.onSelectedImagesChanged(images,0) + activity.onSelectedImagesChanged(images, 0) assertEquals(false, overLimit.getBoolean(activity)) assertEquals(0, exceededBy.getInt(activity)) - activity.onSelectedImagesChanged(images,1) + activity.onSelectedImagesChanged(images, 1) assertEquals(false, overLimit.getBoolean(activity)) assertEquals(0, exceededBy.getInt(activity)) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelTest.kt index 309392d4dc..d4e5f1818f 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelTest.kt @@ -10,7 +10,6 @@ import org.mockito.MockitoAnnotations * Custom Selector View Model test. */ class CustomSelectorViewModelTest { - private lateinit var viewModel: CustomSelectorViewModel @Mock @@ -23,19 +22,18 @@ class CustomSelectorViewModelTest { * Set up the test. */ @Before - fun setUp(){ + fun setUp() { MockitoAnnotations.initMocks(this) - viewModel = CustomSelectorViewModel(context, imageFileLoader); + viewModel = CustomSelectorViewModel(context, imageFileLoader) } /** * Test onCleared(); */ @Test - fun testOnCleared(){ + fun testOnCleared() { val func = viewModel.javaClass.getDeclaredMethod("onCleared") func.isAccessible = true - func.invoke(viewModel); + func.invoke(viewModel) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/FolderFragmentTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/FolderFragmentTest.kt index bac523d476..49da532591 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/FolderFragmentTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/FolderFragmentTest.kt @@ -40,14 +40,13 @@ import java.lang.reflect.Field @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class FolderFragmentTest { - private lateinit var fragment: FolderFragment private lateinit var view: View - private lateinit var selectorRV : RecyclerView - private lateinit var loader : ProgressBar + private lateinit var selectorRV: RecyclerView + private lateinit var loader: ProgressBar private lateinit var layoutInflater: LayoutInflater private lateinit var context: Context - private lateinit var viewModelField:Field + private lateinit var viewModelField: Field @Mock private lateinit var adapter: FolderAdapter @@ -80,7 +79,7 @@ class FolderFragmentTest { loader = view.findViewById(R.id.loader) Whitebox.setInternalState(fragment, "folderAdapter", adapter) - Whitebox.setInternalState(fragment, "selectorRV", selectorRV ) + Whitebox.setInternalState(fragment, "selectorRV", selectorRV) Whitebox.setInternalState(fragment, "loader", loader) viewModelField = fragment.javaClass.getDeclaredField("viewModel") @@ -137,5 +136,4 @@ class FolderFragmentTest { func.isAccessible = true func.invoke(fragment) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoaderTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoaderTest.kt index f89fc467cb..7b4891c98c 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoaderTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoaderTest.kt @@ -18,14 +18,11 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import org.powermock.reflect.Whitebox import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import org.robolectric.fakes.RoboCursor import java.io.File -import java.lang.reflect.Field -import java.lang.reflect.Modifier import kotlin.coroutines.CoroutineContext /** @@ -35,12 +32,11 @@ import kotlin.coroutines.CoroutineContext @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ImageFileLoaderTest { - @Mock private lateinit var mockContentResolver: ContentResolver @Mock - private lateinit var context: Context; + private lateinit var context: Context @Mock private lateinit var imageLoaderListener: ImageLoaderListener @@ -62,17 +58,19 @@ class ImageFileLoaderTest { coroutineContext = Dispatchers.Main imageCursor = RoboCursor() imageFileLoader = ImageFileLoader(context) - projection = listOf( - MediaStore.Images.Media._ID, - MediaStore.Images.Media.DISPLAY_NAME, - MediaStore.Images.Media.DATA, - MediaStore.Images.Media.BUCKET_ID, - MediaStore.Images.Media.BUCKET_DISPLAY_NAME, - MediaStore.Images.Media.DATE_ADDED - ) + projection = + listOf( + MediaStore.Images.Media._ID, + MediaStore.Images.Media.DISPLAY_NAME, + MediaStore.Images.Media.DATA, + MediaStore.Images.Media.BUCKET_ID, + MediaStore.Images.Media.BUCKET_DISPLAY_NAME, + MediaStore.Images.Media.DATE_ADDED, + ) setFinalStatic( - ImageFileLoader::class.java.getDeclaredField("coroutineContext"), - coroutineContext) + ImageFileLoader::class.java.getDeclaredField("coroutineContext"), + coroutineContext, + ) } /** @@ -88,10 +86,11 @@ class ImageFileLoaderTest { */ @Test fun testGetImages() { - val func = imageFileLoader.javaClass.getDeclaredMethod( - "getImages", - ImageLoaderListener::class.java - ) + val func = + imageFileLoader.javaClass.getDeclaredMethod( + "getImages", + ImageLoaderListener::class.java, + ) func.isAccessible = true val image1 = arrayOf(1, "imageLoaderTestFile", "src/test/resources/imageLoaderTestFile", 1, "downloads") @@ -99,31 +98,32 @@ class ImageFileLoaderTest { File("src/test/resources/imageLoaderTestFile").createNewFile() imageCursor.setColumnNames(projection) - imageCursor.setResults(arrayOf(image1, image2)); + imageCursor.setResults(arrayOf(image1, image2)) - val contentResolver: ContentResolver = mock { - on { - query( - same(MediaStore.Images.Media.EXTERNAL_CONTENT_URI), - anyOrNull(), - anyOrNull(), - anyOrNull(), - anyOrNull(), - anyOrNull() - ) - } doReturn imageCursor; - } + val contentResolver: ContentResolver = + mock { + on { + query( + same(MediaStore.Images.Media.EXTERNAL_CONTENT_URI), + anyOrNull(), + anyOrNull(), + anyOrNull(), + anyOrNull(), + anyOrNull(), + ) + } doReturn imageCursor + } // test null cursor. `when`( - context.contentResolver + context.contentResolver, ).thenReturn(mockContentResolver) - func.invoke(imageFileLoader, imageLoaderListener); + func.invoke(imageFileLoader, imageLoaderListener) // test demo cursor. `when`( - context.contentResolver + context.contentResolver, ).thenReturn(contentResolver) - func.invoke(imageFileLoader, imageLoaderListener); + func.invoke(imageFileLoader, imageLoaderListener) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFragmentTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFragmentTest.kt index e517f9a2c7..eeb6db46a5 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFragmentTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFragmentTest.kt @@ -46,12 +46,11 @@ import java.lang.reflect.Field @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ImageFragmentTest { - private lateinit var fragment: ImageFragment private lateinit var activity: CustomSelectorActivity private lateinit var view: View - private lateinit var selectorRV : RecyclerView - private lateinit var loader : ProgressBar + private lateinit var selectorRV: RecyclerView + private lateinit var loader: ProgressBar private lateinit var layoutInflater: LayoutInflater private lateinit var context: Context private lateinit var viewModelField: Field @@ -75,7 +74,7 @@ class ImageFragmentTest { * Setup the image fragment. */ @Before - fun setUp(){ + fun setUp() { MockitoAnnotations.initMocks(this) context = ApplicationProvider.getApplicationContext() OkHttpConnectionFactory.CLIENT = createTestClient() @@ -83,7 +82,7 @@ class ImageFragmentTest { Fresco.initialize(context) activity = Robolectric.buildActivity(CustomSelectorActivity::class.java).create().get() - fragment = ImageFragment.newInstance(1,0) + fragment = ImageFragment.newInstance(1, 0) val fragmentManager: FragmentManager = activity.supportFragmentManager val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction() fragmentTransaction.add(fragment, null) @@ -95,9 +94,9 @@ class ImageFragmentTest { loader = view.findViewById(R.id.loader) Whitebox.setInternalState(fragment, "imageAdapter", adapter) - Whitebox.setInternalState(fragment, "selectorRV", selectorRV ) + Whitebox.setInternalState(fragment, "selectorRV", selectorRV) Whitebox.setInternalState(fragment, "loader", loader) - Whitebox.setInternalState(fragment, "filteredImages", arrayListOf(image,image)) + Whitebox.setInternalState(fragment, "filteredImages", arrayListOf(image, image)) Whitebox.setInternalState(fragment, "contributionDao", contributionDao) viewModelField = fragment.javaClass.getDeclaredField("viewModel") @@ -109,9 +108,9 @@ class ImageFragmentTest { */ @Test @Throws(Exception::class) - fun testOnCreate(){ + fun testOnCreate() { Shadows.shadowOf(Looper.getMainLooper()).idle() - fragment.onCreate(savedInstanceState); + fragment.onCreate(savedInstanceState) } /** @@ -129,11 +128,11 @@ class ImageFragmentTest { * Test handleResult. */ @Test - fun testHandleResult(){ + fun testHandleResult() { val func = fragment.javaClass.getDeclaredMethod("handleResult", Result::class.java) func.isAccessible = true func.invoke(fragment, Result(CallbackStatus.SUCCESS, arrayListOf())) - func.invoke(fragment, Result(CallbackStatus.SUCCESS, arrayListOf(image,image))) + func.invoke(fragment, Result(CallbackStatus.SUCCESS, arrayListOf(image, image))) } /** @@ -184,5 +183,4 @@ class ImageFragmentTest { func.isAccessible = true func.invoke(fragment) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageLoaderTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageLoaderTest.kt index 01e0ff279c..64447384b8 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageLoaderTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageLoaderTest.kt @@ -4,7 +4,8 @@ import android.content.ContentResolver import android.content.Context import android.content.SharedPreferences import android.net.Uri -import com.nhaarman.mockitokotlin2.* +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.TestUtility.setFinalStatic import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao @@ -18,29 +19,32 @@ import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.upload.FileProcessor import fr.free.nrw.commons.upload.FileUtilsWrapper import io.reactivex.Single -import org.junit.Assert import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.* +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.setMain import org.junit.After +import org.junit.Assert import org.junit.Before import org.junit.Test -import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll import org.junit.runner.RunWith -import org.mockito.* +import org.mockito.BDDMockito +import org.mockito.Mock +import org.mockito.MockedStatic +import org.mockito.Mockito import org.mockito.Mockito.mockStatic -import org.powermock.api.mockito.PowerMockito +import org.mockito.MockitoAnnotations import org.powermock.core.classloader.annotations.PrepareForTest -import org.powermock.modules.junit4.PowerMockRunner import org.powermock.reflect.Whitebox import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import java.io.File import java.io.FileInputStream -import java.lang.reflect.Field -import java.lang.reflect.Modifier -import java.util.* +import java.util.Calendar +import java.util.Date import kotlin.collections.HashMap /** @@ -51,9 +55,8 @@ import kotlin.collections.HashMap @Config(sdk = [21], application = TestCommonsApplication::class) @ExperimentalCoroutinesApi class ImageLoaderTest { - @Mock - private lateinit var uri:Uri + private lateinit var uri: Uri @Mock private lateinit var mediaClient: MediaClient @@ -91,9 +94,9 @@ class ImageLoaderTest { @ExperimentalCoroutinesApi private val testDispacher = TestCoroutineDispatcher() - private lateinit var imageLoader: ImageLoader; + private lateinit var imageLoader: ImageLoader private var mapImageSHA1: HashMap = HashMap() - private var mapHolderImage : HashMap = HashMap() + private var mapHolderImage: HashMap = HashMap() private var mapResult: HashMap = HashMap() private var mapModifiedImageSHA1: HashMap = HashMap() private lateinit var image: Image @@ -111,24 +114,32 @@ class ImageLoaderTest { MockitoAnnotations.initMocks(this) imageLoader = - ImageLoader(mediaClient, fileProcessor, fileUtilsWrapper, uploadedStatusDao, - notForUploadStatusDao, context) - uploadedStatus= UploadedStatus( - "testSha1", - "testSha1", - false, - false, - Calendar.getInstance().time - ) + ImageLoader( + mediaClient, + fileProcessor, + fileUtilsWrapper, + uploadedStatusDao, + notForUploadStatusDao, + context, + ) + uploadedStatus = + UploadedStatus( + "testSha1", + "testSha1", + false, + false, + Calendar.getInstance().time, + ) image = Image(1, "test", uri, "test", 0, "test") - Whitebox.setInternalState(imageLoader, "mapImageSHA1", mapImageSHA1); - Whitebox.setInternalState(imageLoader, "mapHolderImage", mapHolderImage); - Whitebox.setInternalState(imageLoader, "mapModifiedImageSHA1", mapModifiedImageSHA1); - Whitebox.setInternalState(imageLoader, "mapResult", mapResult); + Whitebox.setInternalState(imageLoader, "mapImageSHA1", mapImageSHA1) + Whitebox.setInternalState(imageLoader, "mapHolderImage", mapHolderImage) + Whitebox.setInternalState(imageLoader, "mapModifiedImageSHA1", mapModifiedImageSHA1) + Whitebox.setInternalState(imageLoader, "mapResult", mapResult) setFinalStatic( - ImageLoader::class.java.getDeclaredField("context"), - context) + ImageLoader::class.java.getDeclaredField("context"), + context, + ) whenever(contentResolver.openInputStream(uri)).thenReturn(inputStream) whenever(context.contentResolver).thenReturn(contentResolver) whenever(fileUtilsWrapper.getSHA1(inputStream)).thenReturn("testSha1") @@ -143,110 +154,119 @@ class ImageLoaderTest { fun tearDown() { Dispatchers.resetMain() testDispacher.cleanupTestCoroutines() - mockedPickedFiles.close(); + mockedPickedFiles.close() } /** * Test queryAndSetView with upload Status as null. */ @Test - fun testQueryAndSetViewUploadedStatusNull() = testDispacher.runBlockingTest { - whenever(uploadedStatusDao.getUploadedFromImageSHA1(any())).thenReturn(null) - whenever(notForUploadStatusDao.find(any())).thenReturn(0) - mapModifiedImageSHA1[image] = "testSha1" - mapImageSHA1[uri] = "testSha1" - whenever(context.getSharedPreferences("custom_selector", 0)) - .thenReturn(Mockito.mock(SharedPreferences::class.java)) - - mapResult["testSha1"] = ImageLoader.Result.TRUE - imageLoader.queryAndSetView(holder, image, testDispacher, testDispacher, ArrayList()) - - mapResult["testSha1"] = ImageLoader.Result.FALSE - imageLoader.queryAndSetView(holder, image, testDispacher, testDispacher, ArrayList()) - } + fun testQueryAndSetViewUploadedStatusNull() = + testDispacher.runBlockingTest { + whenever(uploadedStatusDao.getUploadedFromImageSHA1(any())).thenReturn(null) + whenever(notForUploadStatusDao.find(any())).thenReturn(0) + mapModifiedImageSHA1[image] = "testSha1" + mapImageSHA1[uri] = "testSha1" + whenever(context.getSharedPreferences("custom_selector", 0)) + .thenReturn(Mockito.mock(SharedPreferences::class.java)) + + mapResult["testSha1"] = ImageLoader.Result.TRUE + imageLoader.queryAndSetView(holder, image, testDispacher, testDispacher, ArrayList()) + + mapResult["testSha1"] = ImageLoader.Result.FALSE + imageLoader.queryAndSetView(holder, image, testDispacher, testDispacher, ArrayList()) + } /** * Test queryAndSetView with upload Status not null (ie retrieved from table) */ @Test - fun testQueryAndSetViewUploadedStatusNotNull() = testDispacher.runBlockingTest { - whenever(uploadedStatusDao.getUploadedFromImageSHA1(any())).thenReturn(uploadedStatus) - whenever(notForUploadStatusDao.find(any())).thenReturn(0) - whenever(context.getSharedPreferences("custom_selector", 0)) - .thenReturn(Mockito.mock(SharedPreferences::class.java)) - imageLoader.queryAndSetView(holder, image, testDispacher, testDispacher, ArrayList()) - } + fun testQueryAndSetViewUploadedStatusNotNull() = + testDispacher.runBlockingTest { + whenever(uploadedStatusDao.getUploadedFromImageSHA1(any())).thenReturn(uploadedStatus) + whenever(notForUploadStatusDao.find(any())).thenReturn(0) + whenever(context.getSharedPreferences("custom_selector", 0)) + .thenReturn(Mockito.mock(SharedPreferences::class.java)) + imageLoader.queryAndSetView(holder, image, testDispacher, testDispacher, ArrayList()) + } /** * Test nextActionableImage */ @Test - fun testNextActionableImage() = testDispacher.runBlockingTest { - whenever(notForUploadStatusDao.find(any())).thenReturn(0) - whenever(uploadedStatusDao.findByImageSHA1(any(), any())).thenReturn(0) - whenever(uploadedStatusDao.findByModifiedImageSHA1(any(), any())).thenReturn(0) + fun testNextActionableImage() = + testDispacher.runBlockingTest { + whenever(notForUploadStatusDao.find(any())).thenReturn(0) + whenever(uploadedStatusDao.findByImageSHA1(any(), any())).thenReturn(0) + whenever(uploadedStatusDao.findByModifiedImageSHA1(any(), any())).thenReturn(0) // mockStatic(PickedFiles::class.java) - BDDMockito.given(PickedFiles.pickedExistingPicture(context, image.uri)) - .willReturn(UploadableFile(uri, File("ABC"))) - whenever(fileUtilsWrapper.getFileInputStream("ABC")).thenReturn(inputStream) - whenever(fileUtilsWrapper.getSHA1(inputStream)).thenReturn("testSha1") - whenever(PickedFiles.pickedExistingPicture(context, Uri.parse("test"))).thenReturn( - uploadableFile - ) - imageLoader.nextActionableImage(listOf(image), testDispacher, testDispacher, 0, emptyList()) - - whenever(notForUploadStatusDao.find(any())).thenReturn(1) - imageLoader.nextActionableImage(listOf(image), testDispacher, testDispacher, 0, emptyList()) - - whenever(uploadedStatusDao.findByImageSHA1(any(), any())).thenReturn(2) - imageLoader.nextActionableImage(listOf(image), testDispacher, testDispacher, 0, emptyList()) - - whenever(uploadedStatusDao.findByModifiedImageSHA1(any(), any())).thenReturn(2) - imageLoader.nextActionableImage(listOf(image), testDispacher, testDispacher, 0, emptyList()) - } + BDDMockito + .given(PickedFiles.pickedExistingPicture(context, image.uri)) + .willReturn(UploadableFile(uri, File("ABC"))) + whenever(fileUtilsWrapper.getFileInputStream("ABC")).thenReturn(inputStream) + whenever(fileUtilsWrapper.getSHA1(inputStream)).thenReturn("testSha1") + whenever(PickedFiles.pickedExistingPicture(context, Uri.parse("test"))).thenReturn( + uploadableFile, + ) + imageLoader.nextActionableImage(listOf(image), testDispacher, testDispacher, 0, emptyList()) + + whenever(notForUploadStatusDao.find(any())).thenReturn(1) + imageLoader.nextActionableImage(listOf(image), testDispacher, testDispacher, 0, emptyList()) + + whenever(uploadedStatusDao.findByImageSHA1(any(), any())).thenReturn(2) + imageLoader.nextActionableImage(listOf(image), testDispacher, testDispacher, 0, emptyList()) + + whenever(uploadedStatusDao.findByModifiedImageSHA1(any(), any())).thenReturn(2) + imageLoader.nextActionableImage(listOf(image), testDispacher, testDispacher, 0, emptyList()) + } /** * Test getSha1 */ @Test @ExperimentalCoroutinesApi - fun testGetSha1() = testDispacher.runBlockingTest { - - BDDMockito.given(PickedFiles.pickedExistingPicture(context, image.uri)) - .willReturn(UploadableFile(uri, File("ABC"))) + fun testGetSha1() = + testDispacher.runBlockingTest { + BDDMockito + .given(PickedFiles.pickedExistingPicture(context, image.uri)) + .willReturn(UploadableFile(uri, File("ABC"))) + whenever(fileUtilsWrapper.getFileInputStream("ABC")).thenReturn(inputStream) + whenever(fileUtilsWrapper.getSHA1(inputStream)).thenReturn("testSha1") - whenever(fileUtilsWrapper.getFileInputStream("ABC")).thenReturn(inputStream) - whenever(fileUtilsWrapper.getSHA1(inputStream)).thenReturn("testSha1") - - Assert.assertEquals("testSha1", imageLoader.getSHA1(image, testDispacher)); - whenever(PickedFiles.pickedExistingPicture(context, Uri.parse("test"))).thenReturn( - uploadableFile - ) + Assert.assertEquals("testSha1", imageLoader.getSHA1(image, testDispacher)) + whenever(PickedFiles.pickedExistingPicture(context, Uri.parse("test"))).thenReturn( + uploadableFile, + ) - mapModifiedImageSHA1[image] = "testSha2" - Assert.assertEquals("testSha2", imageLoader.getSHA1(image, testDispacher)); - } + mapModifiedImageSHA1[image] = "testSha2" + Assert.assertEquals("testSha2", imageLoader.getSHA1(image, testDispacher)) + } /** * Test getResultFromUploadedStatus. */ @Test fun testGetResultFromUploadedStatus() { - val func = imageLoader.javaClass.getDeclaredMethod( - "getResultFromUploadedStatus", - UploadedStatus::class.java) + val func = + imageLoader.javaClass.getDeclaredMethod( + "getResultFromUploadedStatus", + UploadedStatus::class.java, + ) func.isAccessible = true // test Result.INVALID - uploadedStatus.lastUpdated = Date(0); - Assert.assertEquals(ImageLoader.Result.INVALID, - imageLoader.getResultFromUploadedStatus(uploadedStatus)) + uploadedStatus.lastUpdated = Date(0) + Assert.assertEquals( + ImageLoader.Result.INVALID, + imageLoader.getResultFromUploadedStatus(uploadedStatus), + ) // test Result.TRUE - uploadedStatus.imageResult = true; - Assert.assertEquals(ImageLoader.Result.TRUE, - imageLoader.getResultFromUploadedStatus(uploadedStatus)) + uploadedStatus.imageResult = true + Assert.assertEquals( + ImageLoader.Result.TRUE, + imageLoader.getResultFromUploadedStatus(uploadedStatus), + ) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt index 0d5d79909d..9e60b74e6a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt @@ -6,24 +6,19 @@ import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever -import fr.free.nrw.commons.FakeContextWrapper import fr.free.nrw.commons.Media import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.actions.PageEditClient -import fr.free.nrw.commons.contributions.ContributionsListFragment import fr.free.nrw.commons.review.ReviewController import io.reactivex.Observable -import io.reactivex.Single +import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue -import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.junit.runner.Runner import org.mockito.ArgumentMatchers import org.mockito.Mock -import org.mockito.Mockito import org.mockito.MockitoAnnotations import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment @@ -37,18 +32,17 @@ import org.robolectric.annotation.LooperMode @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class DeleteHelperTest { - @Mock private lateinit var callback: ReviewController.ReviewCallback @Mock - internal lateinit var pageEditClient: PageEditClient + internal lateinit var pageEditClient: PageEditClient @Mock - internal lateinit var context: Context + internal lateinit var context: Context @Mock - internal lateinit var media: Media + internal lateinit var media: Media lateinit var deleteHelper: DeleteHelper @@ -67,11 +61,11 @@ class DeleteHelperTest { @Test fun makeDeletion() { whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(true)) + .thenReturn(Observable.just(true)) whenever(pageEditClient.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(true)) + .thenReturn(Observable.just(true)) whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(true)) + .thenReturn(Observable.just(true)) whenever(media.displayTitle).thenReturn("Test file") @@ -90,11 +84,11 @@ class DeleteHelperTest { @Test(expected = RuntimeException::class) fun makeDeletionForPrependEditFailure() { whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(false)) + .thenReturn(Observable.just(false)) whenever(pageEditClient.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(true)) + .thenReturn(Observable.just(true)) whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(true)) + .thenReturn(Observable.just(true)) whenever(media.displayTitle).thenReturn("Test file") whenever(media.filename).thenReturn("Test file.jpg") whenever(media.author).thenReturn("Creator (page does not exist)") @@ -105,11 +99,11 @@ class DeleteHelperTest { @Test(expected = RuntimeException::class) fun makeDeletionForEditFailure() { whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(true)) + .thenReturn(Observable.just(true)) whenever(pageEditClient.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(true)) + .thenReturn(Observable.just(true)) whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(false)) + .thenReturn(Observable.just(false)) whenever(media.displayTitle).thenReturn("Test file") whenever(media.filename).thenReturn("Test file.jpg") whenever(media.author).thenReturn("Creator (page does not exist)") @@ -120,11 +114,11 @@ class DeleteHelperTest { @Test(expected = RuntimeException::class) fun makeDeletionForAppendEditFailure() { whenever(pageEditClient.prependEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(true)) + .thenReturn(Observable.just(true)) whenever(pageEditClient.appendEdit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(false)) + .thenReturn(Observable.just(false)) whenever(pageEditClient.edit(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString())) - .thenReturn(Observable.just(true)) + .thenReturn(Observable.just(true)) whenever(media.displayTitle).thenReturn("Test file") whenever(media.filename).thenReturn("Test file.jpg") whenever(media.author).thenReturn("Creator (page does not exist)") @@ -141,21 +135,21 @@ class DeleteHelperTest { @Test fun askReasonAndExecuteCopyrightViolationTest() { val mContext = RuntimeEnvironment.getApplication().applicationContext - deleteHelper.askReasonAndExecute(media, mContext, "My Question", ReviewController.DeleteReason.COPYRIGHT_VIOLATION, callback); + deleteHelper.askReasonAndExecute(media, mContext, "My Question", ReviewController.DeleteReason.COPYRIGHT_VIOLATION, callback) } @Test fun alertDialogPositiveButtonDisableTest() { val mContext = RuntimeEnvironment.getApplication().applicationContext - deleteHelper.askReasonAndExecute(media, mContext, "My Question", ReviewController.DeleteReason.COPYRIGHT_VIOLATION, callback); + deleteHelper.askReasonAndExecute(media, mContext, "My Question", ReviewController.DeleteReason.COPYRIGHT_VIOLATION, callback) assertEquals(false, deleteHelper.dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled) } - @Test + @Test fun alertDialogPositiveButtonEnableTest() { val mContext = RuntimeEnvironment.getApplication().applicationContext - deleteHelper.askReasonAndExecute(media, mContext, "My Question", ReviewController.DeleteReason.COPYRIGHT_VIOLATION, callback); - deleteHelper.listener.onClick(deleteHelper.dialog,1,true); + deleteHelper.askReasonAndExecute(media, mContext, "My Question", ReviewController.DeleteReason.COPYRIGHT_VIOLATION, callback) + deleteHelper.listener.onClick(deleteHelper.dialog, 1, true) assertEquals(true, deleteHelper.dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt index 812f32f9a6..75aadc17a4 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt @@ -17,19 +17,25 @@ import org.junit.Test import org.mockito.ArgumentMatchers.anyInt import org.mockito.InjectMocks import org.mockito.Mock -import org.mockito.Mockito.* +import org.mockito.Mockito.any +import org.mockito.Mockito.anyString +import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import java.util.* - +import java.util.Date class ReasonBuilderTest { - @Mock internal var sessionManager: SessionManager? = null + @Mock internal var okHttpJsonApiClient: OkHttpJsonApiClient? = null + @Mock internal var context: Context? = null + @Mock internal var viewUtilWrapper: ViewUtilWrapper? = null @@ -56,13 +62,13 @@ class ReasonBuilderTest { `when`(sessionManager?.userName).thenReturn("Testuser") `when`(sessionManager?.doesAccountExist()).thenReturn(true) `when`(okHttpJsonApiClient!!.getAchievements(anyString())) - .thenReturn(Single.just(mock(FeedbackResponse::class.java))) + .thenReturn(Single.just(mock(FeedbackResponse::class.java))) `when`(okHttpJsonApiClient!!.getLeaderboard(anyString(), anyString(), anyString(), anyString(), anyString())) .thenReturn(Observable.just(mock(LeaderboardResponse::class.java))) `when`(okHttpJsonApiClient!!.setAvatar(anyString(), anyString())) .thenReturn(Single.just(mock(UpdateAvatarResponse::class.java))) - val media = media(filename="test_file", dateUploaded = Date()) + val media = media(filename = "test_file", dateUploaded = Date()) reasonBuilder!!.getReason(media, "test") verify(sessionManager, times(0))!!.forceLogin(any(Context::class.java)) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditActivityUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditActivityUnitTest.kt index 19cc945cdf..00f438e1e3 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditActivityUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditActivityUnitTest.kt @@ -10,7 +10,6 @@ import android.os.Looper import android.view.LayoutInflater import android.view.View import androidx.recyclerview.widget.RecyclerView -import androidx.test.core.app.ApplicationProvider import fr.free.nrw.commons.Media import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication @@ -26,7 +25,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.powermock.reflect.Whitebox @@ -39,13 +37,12 @@ import org.robolectric.annotation.LooperMode import org.robolectric.shadows.ShadowAlertDialog import org.robolectric.shadows.ShadowProgressDialog import java.lang.reflect.Method -import java.util.* +import java.util.Date @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class DescriptionEditActivityUnitTest { - private lateinit var context: Context private lateinit var activity: Activity private lateinit var uploadMediaDetails: ArrayList @@ -64,10 +61,19 @@ class DescriptionEditActivityUnitTest { fun setUp() { MockitoAnnotations.initMocks(this) context = RuntimeEnvironment.getApplication().applicationContext - uploadMediaDetails = mutableListOf(UploadMediaDetail("en", "desc")) + uploadMediaDetails = + mutableListOf(UploadMediaDetail("en", "desc")) as ArrayList - media = Media("filename", "creator", "url", "thumburl", - "localpath", Date(197000), "extmetadata") + media = + Media( + "filename", + "creator", + "url", + "thumburl", + "localpath", + Date(197000), + "extmetadata", + ) val intent = Intent().putExtra("title", "read") val bundle = Bundle() @@ -87,7 +93,7 @@ class DescriptionEditActivityUnitTest { Whitebox.setInternalState(activity, "binding", binding) Whitebox.setInternalState(activity, "savedLanguageValue", "bn") Whitebox.setInternalState(activity, "media", media) - Whitebox.setInternalState(activity,"descriptionAndCaptions",uploadMediaDetails) + Whitebox.setInternalState(activity, "descriptionAndCaptions", uploadMediaDetails) `when`(uploadMediaDetailAdapter.items).thenReturn(uploadMediaDetails) } @@ -101,9 +107,10 @@ class DescriptionEditActivityUnitTest { @Throws(Exception::class) fun testShowLoggingProgressBar() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = DescriptionEditActivity::class.java.getDeclaredMethod( - "showLoggingProgressBar" - ) + val method: Method = + DescriptionEditActivity::class.java.getDeclaredMethod( + "showLoggingProgressBar", + ) method.isAccessible = true method.invoke(activity) val dialog: ProgressDialog = ShadowProgressDialog.getLatestDialog() as ProgressDialog @@ -114,9 +121,11 @@ class DescriptionEditActivityUnitTest { @Throws(Exception::class) fun testUpdateDescription() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = DescriptionEditActivity::class.java.getDeclaredMethod( - "updateDescription", List::class.java - ) + val method: Method = + DescriptionEditActivity::class.java.getDeclaredMethod( + "updateDescription", + List::class.java, + ) method.isAccessible = true method.invoke(activity, mutableListOf(UploadMediaDetail("en", "desc"))) assertEquals(activity.isFinishing, true) @@ -126,9 +135,11 @@ class DescriptionEditActivityUnitTest { @Throws(Exception::class) fun testOnSubmitButtonClicked() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = DescriptionEditActivity::class.java.getDeclaredMethod( - "onSubmitButtonClicked", View::class.java - ) + val method: Method = + DescriptionEditActivity::class.java.getDeclaredMethod( + "onSubmitButtonClicked", + View::class.java, + ) method.isAccessible = true method.invoke(activity, null) assertEquals(activity.isFinishing, true) @@ -138,9 +149,11 @@ class DescriptionEditActivityUnitTest { @Throws(Exception::class) fun testOnBackButtonClicked() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = DescriptionEditActivity::class.java.getDeclaredMethod( - "onBackButtonClicked", View::class.java - ) + val method: Method = + DescriptionEditActivity::class.java.getDeclaredMethod( + "onBackButtonClicked", + View::class.java, + ) method.isAccessible = true method.invoke(activity, null) assertEquals(activity.isFinishing, true) @@ -150,9 +163,11 @@ class DescriptionEditActivityUnitTest { @Throws(Exception::class) fun testOnPrimaryCaptionTextChange() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = DescriptionEditActivity::class.java.getDeclaredMethod( - "onPrimaryCaptionTextChange", Boolean::class.java - ) + val method: Method = + DescriptionEditActivity::class.java.getDeclaredMethod( + "onPrimaryCaptionTextChange", + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, true) } @@ -161,17 +176,19 @@ class DescriptionEditActivityUnitTest { @Throws(Exception::class) fun testShowInfoAlert() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = DescriptionEditActivity::class.java.getDeclaredMethod( - "showInfoAlert", Int::class.java, Int::class.java - ) + val method: Method = + DescriptionEditActivity::class.java.getDeclaredMethod( + "showInfoAlert", + Int::class.java, + Int::class.java, + ) method.isAccessible = true method.invoke( activity, android.R.string.ok, - android.R.string.ok + android.R.string.ok, ) val dialog: AlertDialog = ShadowAlertDialog.getLatestDialog() as AlertDialog assertEquals(dialog.isShowing, true) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditHelperUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditHelperUnitTest.kt index 614ab97483..5415a87a61 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditHelperUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditHelperUnitTest.kt @@ -10,12 +10,13 @@ import org.junit.Test import org.junit.jupiter.api.Assertions.assertEquals import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock -import org.mockito.Mockito.* +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import java.lang.reflect.Method class DescriptionEditHelperUnitTest { - private lateinit var helper: DescriptionEditHelper @Mock @@ -43,8 +44,8 @@ class DescriptionEditHelperUnitTest { pageEditClient.edit( anyString(), anyString(), - anyString() - ) + anyString(), + ), ).thenReturn(Observable.just(true)) helper.addDescription(context, media, "test") verify(pageEditClient, times(1)).edit(anyString(), anyString(), anyString()) @@ -58,56 +59,67 @@ class DescriptionEditHelperUnitTest { anyString(), anyString(), anyString(), - anyString() - ) + anyString(), + ), ).thenReturn(Observable.just(0)) helper.addCaption(context, media, "test", "test") verify(pageEditClient, times(1)).setCaptions( anyString(), anyString(), anyString(), - anyString() + anyString(), ) } @Test fun testShowCaptionEditNotificationCaseFalse() { - val method: Method = DescriptionEditHelper::class.java.getDeclaredMethod( - "showCaptionEditNotification", Context::class.java, Media::class.java, - Int::class.java - ) + val method: Method = + DescriptionEditHelper::class.java.getDeclaredMethod( + "showCaptionEditNotification", + Context::class.java, + Media::class.java, + Int::class.java, + ) method.isAccessible = true assertEquals(method.invoke(helper, context, media, 0), false) } @Test fun testShowCaptionEditNotificationCaseTrue() { - val method: Method = DescriptionEditHelper::class.java.getDeclaredMethod( - "showCaptionEditNotification", Context::class.java, Media::class.java, - Int::class.java - ) + val method: Method = + DescriptionEditHelper::class.java.getDeclaredMethod( + "showCaptionEditNotification", + Context::class.java, + Media::class.java, + Int::class.java, + ) method.isAccessible = true assertEquals(method.invoke(helper, context, media, 1), true) } @Test fun testShowDescriptionEditNotificationCaseFalse() { - val method: Method = DescriptionEditHelper::class.java.getDeclaredMethod( - "showDescriptionEditNotification", Context::class.java, Media::class.java, - Boolean::class.java - ) + val method: Method = + DescriptionEditHelper::class.java.getDeclaredMethod( + "showDescriptionEditNotification", + Context::class.java, + Media::class.java, + Boolean::class.java, + ) method.isAccessible = true assertEquals(method.invoke(helper, context, media, false), false) } @Test fun testShowDescriptionEditNotificationCaseTrue() { - val method: Method = DescriptionEditHelper::class.java.getDeclaredMethod( - "showDescriptionEditNotification", Context::class.java, Media::class.java, - Boolean::class.java - ) + val method: Method = + DescriptionEditHelper::class.java.getDeclaredMethod( + "showDescriptionEditNotification", + Context::class.java, + Media::class.java, + Boolean::class.java, + ) method.isAccessible = true assertEquals(method.invoke(helper, context, media, true), true) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/BasePagingPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/BasePagingPresenterTest.kt index 69cc2eadfa..e3d69d41c6 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/BasePagingPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/BasePagingPresenterTest.kt @@ -7,7 +7,11 @@ import com.jraska.livedata.test import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever -import fr.free.nrw.commons.explore.paging.* +import fr.free.nrw.commons.explore.paging.BasePagingPresenter +import fr.free.nrw.commons.explore.paging.FooterItem +import fr.free.nrw.commons.explore.paging.LoadingState +import fr.free.nrw.commons.explore.paging.PageableBaseDataSource +import fr.free.nrw.commons.explore.paging.PagingContract import io.reactivex.processors.PublishProcessor import io.reactivex.schedulers.TestScheduler import org.junit.Before @@ -17,7 +21,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations class BasePagingPresenterTest { - @Rule @JvmField var instantTaskExecutorRule = InstantTaskExecutorRule() @@ -45,7 +48,7 @@ class BasePagingPresenterTest { MockitoAnnotations.openMocks(this) whenever(pageableBaseDataSource.pagingResults).thenReturn(searchResults) whenever(pageableBaseDataSource.loadingStates).thenReturn(loadingStates) - whenever(pageableBaseDataSource.noItemsLoadedQueries) + whenever(pageableBaseDataSource.noItemsLoadedEvent) .thenReturn(noItemLoadedQueries) testScheduler = TestScheduler() basePagingPresenter = @@ -70,7 +73,8 @@ class BasePagingPresenterTest { @Test fun `Complete offers an empty list item and hides initial loader`() { onLoadingState(LoadingState.Complete) - basePagingPresenter.listFooterData.test() + basePagingPresenter.listFooterData + .test() .assertValue(emptyList()) verify(view).hideInitialLoadProgress() } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreFragmentUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreFragmentUnitTest.kt index 4e0f0712a0..41c999791a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreFragmentUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreFragmentUnitTest.kt @@ -2,7 +2,11 @@ package fr.free.nrw.commons.explore import android.content.Context import android.os.Looper.getMainLooper -import android.view.* +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction import androidx.test.core.app.ApplicationProvider @@ -10,8 +14,8 @@ import com.google.android.material.tabs.TabLayout import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.contributions.MainActivity +import fr.free.nrw.commons.createTestClient import org.junit.Assert import org.junit.Before import org.junit.Ignore @@ -34,7 +38,6 @@ import org.robolectric.fakes.RoboMenuItem @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ExploreFragmentUnitTest { - private lateinit var fragment: ExploreFragment private lateinit var fragmentManager: FragmentManager private lateinit var context: Context @@ -69,7 +72,6 @@ class ExploreFragmentUnitTest { layoutInflater = LayoutInflater.from(activity) view = fragment.onCreateView(layoutInflater, null, null) as View viewPager = view.findViewById(R.id.viewPager) - } @Test @@ -101,7 +103,8 @@ class ExploreFragmentUnitTest { Assert.assertEquals(fragment.onBackPressed(), true) } - @Test @Ignore("TODO fix this test") + @Test + @Ignore("TODO fix this test") @Throws(Exception::class) fun testOnBackPressedCaseTrueSelectedTabNonZero() { Whitebox.setInternalState(fragment, "mobileRootFragment", exploreRootFragment) @@ -150,5 +153,4 @@ class ExploreFragmentUnitTest { fragment.onCreateOptionsMenu(menu, inflater) verify(inflater).inflate(R.menu.menu_search, menu) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreListRootFragmentUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreListRootFragmentUnitTest.kt index 582bafd6cc..9dc94293f0 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreListRootFragmentUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreListRootFragmentUnitTest.kt @@ -11,8 +11,8 @@ import fr.free.nrw.commons.Media import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.contributions.MainActivity +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.databinding.FragmentFeaturedRootBinding import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment import fr.free.nrw.commons.media.MediaDetailPagerFragment @@ -36,7 +36,6 @@ import java.lang.reflect.Field @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ExploreListRootFragmentUnitTest { - private lateinit var fragment: ExploreListRootFragment private lateinit var fragmentManager: FragmentManager private lateinit var context: Context @@ -83,7 +82,6 @@ class ExploreListRootFragmentUnitTest { Whitebox.setInternalState(fragment, "mediaDetails", mediaDetails) Whitebox.setInternalState(fragment, "listFragment", listFragment) - `when`(childFragmentManager.beginTransaction()).thenReturn(childFragmentTransaction) `when`(childFragmentTransaction.hide(any())).thenReturn(childFragmentTransaction) `when`(childFragmentTransaction.add(anyInt(), any())).thenReturn(childFragmentTransaction) @@ -249,5 +247,4 @@ class ExploreListRootFragmentUnitTest { fun `testBackPressed_Case null != mediaDetails && mediaDetails_isNotVisible`() { Assert.assertEquals(fragment.backPressed(), false) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/PageableBaseDataSourceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/PageableBaseDataSourceTest.kt index f2c1ac20ed..c0f9bd7430 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/PageableBaseDataSourceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/PageableBaseDataSourceTest.kt @@ -2,7 +2,12 @@ package fr.free.nrw.commons.explore import androidx.lifecycle.LiveData import androidx.paging.PagedList -import com.nhaarman.mockitokotlin2.* +import com.nhaarman.mockitokotlin2.KArgumentCaptor +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.spy +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.explore.depictions.search.LoadFunction import fr.free.nrw.commons.explore.paging.LiveDataConverter import fr.free.nrw.commons.explore.paging.PageableBaseDataSource @@ -14,7 +19,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations class PageableBaseDataSourceTest { - @Mock private lateinit var liveDataConverter: LiveDataConverter @@ -23,17 +27,18 @@ class PageableBaseDataSourceTest { @Before fun setUp() { MockitoAnnotations.openMocks(this) - pageableBaseDataSource = object: PageableBaseDataSource(liveDataConverter){ - override val loadFunction: LoadFunction - get() = mock() - - } + pageableBaseDataSource = + object : PageableBaseDataSource(liveDataConverter) { + override val loadFunction: LoadFunction + get() = mock() + } } @Test fun `onQueryUpdated emits new liveData`() { val (_, liveData) = expectNewLiveData() - pageableBaseDataSource.pagingResults.test() + pageableBaseDataSource.pagingResults + .test() .also { pageableBaseDataSource.onQueryUpdated("test") } .assertValue(liveData) } @@ -42,14 +47,15 @@ class PageableBaseDataSourceTest { fun `onQueryUpdated invokes livedatconverter with no items emitter`() { val (zeroItemsFuncCaptor, _) = expectNewLiveData() pageableBaseDataSource.onQueryUpdated("test") - pageableBaseDataSource.noItemsLoadedQueries.test() + pageableBaseDataSource.noItemsLoadedEvent + .test() .also { zeroItemsFuncCaptor.firstValue.invoke() } .assertValue("test") } /* - * Just for coverage, no way to really assert this - * */ + * Just for coverage, no way to really assert this + * */ @Test fun `retryFailedRequest does nothing without a factory`() { pageableBaseDataSource.retryFailedRequest() @@ -65,7 +71,11 @@ class PageableBaseDataSourceTest { verify(dataSourceFactory).retryFailedRequest() } - private fun expectNewLiveData(): Triple Unit>, LiveData>, KArgumentCaptor>> { + private fun expectNewLiveData(): Triple< + KArgumentCaptor<() -> Unit>, + LiveData>, + KArgumentCaptor>, + > { val captor = argumentCaptor<() -> Unit>() val dataSourceFactoryCaptor = argumentCaptor>() val liveData: LiveData> = mock() diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/PagingDataSourceFactoryTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/PagingDataSourceFactoryTest.kt index 49ea93c8a5..9460999f49 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/PagingDataSourceFactoryTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/PagingDataSourceFactoryTest.kt @@ -10,7 +10,6 @@ import fr.free.nrw.commons.explore.paging.PagingDataSourceFactory import io.reactivex.processors.PublishProcessor import org.hamcrest.CoreMatchers.instanceOf import org.hamcrest.MatcherAssert -import org.junit.Assert import org.junit.Before import org.junit.Ignore import org.junit.Test @@ -19,7 +18,6 @@ import org.mockito.Mockito import org.mockito.MockitoAnnotations class PagingDataSourceFactoryTest { - @Mock private lateinit var depictsClient: DepictsClient @@ -32,16 +30,17 @@ class PagingDataSourceFactoryTest { @Before fun setUp() { MockitoAnnotations.openMocks(this) - factory = object : PagingDataSourceFactory(loadingStates) { - override val loadFunction get() = function - } + factory = + object : PagingDataSourceFactory(loadingStates) { + override val loadFunction get() = function + } } @Test fun `create returns a dataSource`() { MatcherAssert.assertThat( factory.create(), - instanceOf(PagingDataSource::class.java) + instanceOf(PagingDataSource::class.java), ) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/PagingDataSourceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/PagingDataSourceTest.kt index a6ca017ab0..f8621ad880 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/PagingDataSourceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/PagingDataSourceTest.kt @@ -1,7 +1,11 @@ package fr.free.nrw.commons.explore import androidx.paging.PositionalDataSource -import com.nhaarman.mockitokotlin2.* +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.never +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.explore.depictions.search.LoadingStates import fr.free.nrw.commons.explore.paging.LoadingState import fr.free.nrw.commons.explore.paging.PagingDataSource @@ -16,7 +20,6 @@ import org.mockito.Mockito.verifyNoInteractions import org.mockito.MockitoAnnotations class PagingDataSourceTest { - private lateinit var loadingStates: PublishProcessor private lateinit var searchDepictionsDataSource: TestPagingDataSource @@ -31,7 +34,7 @@ class PagingDataSourceTest { searchDepictionsDataSource = TestPagingDataSource( loadingStates, - mockGetItems + mockGetItems, ) } @@ -97,7 +100,8 @@ class PagingDataSourceTest { val callback: PositionalDataSource.LoadRangeCallback = mock() val params = PositionalDataSource.LoadRangeParams(0, 1) whenever(mockGetItems.getItems(params.loadSize, params.startPosition)) - .thenThrow(RuntimeException()).thenReturn(emptyList()) + .thenThrow(RuntimeException()) + .thenReturn(emptyList()) val testSubscriber = loadingStates.test() searchDepictionsDataSource.loadRange(params, callback) verify(callback, never()).onResult(any()) @@ -107,17 +111,24 @@ class PagingDataSourceTest { LoadingState.Loading, LoadingState.Error, LoadingState.Loading, - LoadingState.Complete + LoadingState.Complete, ) } } -class TestPagingDataSource(loadingStates: LoadingStates, val mockGetItems: MockGetItems) : - PagingDataSource(loadingStates) { - override fun getItems(loadSize: Int, startPosition: Int): List = - mockGetItems.getItems(loadSize, startPosition) +class TestPagingDataSource( + loadingStates: LoadingStates, + val mockGetItems: MockGetItems, +) : PagingDataSource(loadingStates) { + override fun getItems( + loadSize: Int, + startPosition: Int, + ): List = mockGetItems.getItems(loadSize, startPosition) } interface MockGetItems { - fun getItems(loadSize: Int, startPosition: Int): List + fun getItems( + loadSize: Int, + startPosition: Int, + ): List } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/categories/parent/PageableParentCategoriesDataSourceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/categories/parent/PageableParentCategoriesDataSourceTest.kt index 77b3876c74..5a08dea611 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/categories/parent/PageableParentCategoriesDataSourceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/categories/parent/PageableParentCategoriesDataSourceTest.kt @@ -12,9 +12,10 @@ import org.junit.Test import org.mockito.Mock import org.mockito.MockitoAnnotations -class PageableParentCategoriesDataSourceTest{ +class PageableParentCategoriesDataSourceTest { @Mock lateinit var categoryClient: CategoryClient + @Mock lateinit var liveDataConverter: LiveDataConverter diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/categories/sub/PageableSubCategoriesDataSourceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/categories/sub/PageableSubCategoriesDataSourceTest.kt index 41e024db58..a7c2af98be 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/categories/sub/PageableSubCategoriesDataSourceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/categories/sub/PageableSubCategoriesDataSourceTest.kt @@ -12,9 +12,10 @@ import org.junit.Test import org.mockito.Mock import org.mockito.MockitoAnnotations -class PageableSubCategoriesDataSourceTest{ +class PageableSubCategoriesDataSourceTest { @Mock lateinit var categoryClient: CategoryClient + @Mock lateinit var liveDataConverter: LiveDataConverter diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/DepictsClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/DepictsClientTest.kt index 689fa8e7b0..3c1d7ade4e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/DepictsClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/DepictsClientTest.kt @@ -9,17 +9,20 @@ import fr.free.nrw.commons.mwapi.Result import fr.free.nrw.commons.mwapi.SparqlResponse import fr.free.nrw.commons.upload.depicts.DepictsInterface import fr.free.nrw.commons.upload.structure.depictions.DepictedItem +import fr.free.nrw.commons.wikidata.model.DataValue import fr.free.nrw.commons.wikidata.model.DepictSearchResponse +import fr.free.nrw.commons.wikidata.model.Entities +import fr.free.nrw.commons.wikidata.model.SnakPartial +import fr.free.nrw.commons.wikidata.model.StatementPartial +import fr.free.nrw.commons.wikidata.model.WikiBaseEntityValue import io.reactivex.Single import org.junit.Before import org.junit.Test import org.mockito.Mock import org.mockito.MockitoAnnotations -import fr.free.nrw.commons.wikidata.model.* import java.lang.reflect.Method class DepictsClientTest { - @Mock private lateinit var depictsInterface: DepictsInterface private lateinit var depictsClient: DepictsClient @@ -35,16 +38,16 @@ class DepictsClientTest { val depictSearchResponse = mock() whenever(depictsInterface.searchForDepicts("query", "1", "en", "en", "0")) .thenReturn(Single.just(depictSearchResponse)) - whenever(depictSearchResponse.search).thenReturn(listOf(depictSearchItem("1"),depictSearchItem("2"))) + whenever(depictSearchResponse.search).thenReturn(listOf(depictSearchItem("1"), depictSearchItem("2"))) val entities = mock() whenever(depictsInterface.getEntities("1|2")).thenReturn(Single.just(entities)) whenever(entities.entities()).thenReturn(emptyMap()) - depictsClient.searchForDepictions("query", 1, 0) + depictsClient + .searchForDepictions("query", 1, 0) .test() .assertValue(emptyList()) } - @Test fun getEntities() { val entities = mock() @@ -64,25 +67,37 @@ class DepictsClientTest { whenever(binding2.id).thenReturn("2") val entities = mock() val entity = mock() - val statementPartial = mock() + val statementPartial = mock() whenever(depictsInterface.getEntities("1|2")).thenReturn(Single.just(entities)) whenever(entities.entities()).thenReturn(mapOf("en" to entity)) whenever(entity.statements).thenReturn(mapOf("P31" to listOf(statementPartial))) whenever(statementPartial.mainSnak).thenReturn( - Snak_partial("test", "P31", + SnakPartial( + "test", + "P31", DataValue.EntityId( - WikiBaseEntityValue("wikibase-entityid", "Q10", 10L) - ) - ) + WikiBaseEntityValue("wikibase-entityid", "Q10", 10L), + ), + ), ) whenever(depictsInterface.getEntities("Q10")).thenReturn(Single.just(entities)) whenever(entity.id()).thenReturn("Q10") - depictsClient.toDepictions(Single.just(sparqlResponse)) + depictsClient + .toDepictions(Single.just(sparqlResponse)) .test() - .assertValue(listOf( - DepictedItem("", "", null, - listOf("Q10"), emptyList(), false, "Q10") - )) + .assertValue( + listOf( + DepictedItem( + "", + "", + null, + listOf("Q10"), + emptyList(), + false, + "Q10", + ), + ), + ) } @Test @@ -99,34 +114,49 @@ class DepictsClientTest { val entity = mock() whenever(depictsInterface.getEntities("1|2")).thenReturn(Single.just(entities)) whenever(entities.entities()).thenReturn(mapOf("en" to entity)) - whenever(entity.descriptions()).thenReturn(mapOf("en" to - Entities.Label("en", "Test description") - )) + whenever(entity.descriptions()).thenReturn( + mapOf( + "en" to + Entities.Label("en", "Test description"), + ), + ) whenever(entity.id()).thenReturn("Q10") - depictsClient.toDepictions(Single.just(sparqlResponse)) + depictsClient + .toDepictions(Single.just(sparqlResponse)) .test() - .assertValue(listOf( - DepictedItem("", "", null, listOf("Q10"), - emptyList(), false, "Q10") - )) + .assertValue( + listOf( + DepictedItem( + "", + "", + null, + listOf("Q10"), + emptyList(), + false, + "Q10", + ), + ), + ) } @Test fun `Test mapToDepictItem when description is not empty`() { - val method: Method = DepictsClient::class.java.getDeclaredMethod( - "mapToDepictItem", - Entities.Entity::class.java - ) + val method: Method = + DepictsClient::class.java.getDeclaredMethod( + "mapToDepictItem", + Entities.Entity::class.java, + ) method.isAccessible = true method.invoke(depictsClient, entity(descriptions = mapOf("en" to "Test"))) } @Test fun `Test mapToDepictItem when description is empty and P31 doesn't exists`() { - val method: Method = DepictsClient::class.java.getDeclaredMethod( - "mapToDepictItem", - Entities.Entity::class.java - ) + val method: Method = + DepictsClient::class.java.getDeclaredMethod( + "mapToDepictItem", + Entities.Entity::class.java, + ) method.isAccessible = true method.invoke(depictsClient, entity()) } @@ -135,23 +165,26 @@ class DepictsClientTest { fun `Test mapToDepictItem when description is empty and P31 exists`() { val entities = mock() val entity = mock() - val statementPartial = mock() + val statementPartial = mock() whenever(entity.statements).thenReturn(mapOf("P31" to listOf(statementPartial))) whenever(statementPartial.mainSnak).thenReturn( - Snak_partial("test", "P31", + SnakPartial( + "test", + "P31", DataValue.EntityId( - WikiBaseEntityValue("wikibase-entityid", "Q10", 10L) - ) - ) + WikiBaseEntityValue("wikibase-entityid", "Q10", 10L), + ), + ), ) whenever(depictsInterface.getEntities("Q10")).thenReturn(Single.just(entities)) whenever(entities.entities()) .thenReturn(mapOf("test" to entity)) whenever(entity.id()).thenReturn("Q10") - val method: Method = DepictsClient::class.java.getDeclaredMethod( - "mapToDepictItem", - Entities.Entity::class.java - ) + val method: Method = + DepictsClient::class.java.getDeclaredMethod( + "mapToDepictItem", + Entities.Entity::class.java, + ) method.isAccessible = true method.invoke(depictsClient, entity) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/PageableDepictionsDataSourceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/PageableDepictionsDataSourceTest.kt index 8dfc9b279d..027c90f9c0 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/PageableDepictionsDataSourceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/PageableDepictionsDataSourceTest.kt @@ -8,7 +8,6 @@ import org.junit.Assert import org.junit.Test class PageableDepictionsDataSourceTest { - @Test fun `loadFunction loads depictions`() { val depictsClient: DepictsClient = mock() @@ -18,8 +17,7 @@ class PageableDepictionsDataSourceTest { pageableDepictionsDataSource.onQueryUpdated("test") Assert.assertEquals( pageableDepictionsDataSource.loadFunction.invoke(0, 1), - emptyList() + emptyList(), ) } } - diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivityUnitTests.kt index 5db886cdff..bf5aca6e2f 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivityUnitTests.kt @@ -1,18 +1,12 @@ package fr.free.nrw.commons.explore.depictions import android.content.Intent -import android.view.LayoutInflater -import android.view.View -import android.widget.FrameLayout import androidx.fragment.app.FragmentManager import androidx.test.core.app.ApplicationProvider -import androidx.viewpager.widget.ViewPager -import com.google.android.material.tabs.TabLayout import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.createTestClient -import fr.free.nrw.commons.databinding.ActivityWikidataItemDetailsBinding import fr.free.nrw.commons.explore.depictions.media.DepictedImagesFragment import fr.free.nrw.commons.media.MediaDetailPagerFragment import fr.free.nrw.commons.upload.structure.depictions.DepictedItem @@ -35,7 +29,6 @@ import org.robolectric.fakes.RoboMenuItem @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class WikidataItemDetailsActivityUnitTests { - private lateinit var activity: WikidataItemDetailsActivity @Mock @@ -53,32 +46,31 @@ class WikidataItemDetailsActivityUnitTests { @Mock private lateinit var wikidataItem: DepictedItem - @Before fun setUp() { MockitoAnnotations.openMocks(this) OkHttpConnectionFactory.CLIENT = createTestClient() - val intent = Intent( - ApplicationProvider.getApplicationContext(), - WikidataItemDetailsActivity::class.java - ) + val intent = + Intent( + ApplicationProvider.getApplicationContext(), + WikidataItemDetailsActivity::class.java, + ) intent.putExtra("wikidataItemName", "depictionName") intent.putExtra("entityId", 0) activity = - Robolectric.buildActivity(WikidataItemDetailsActivity::class.java, intent).create() + Robolectric + .buildActivity(WikidataItemDetailsActivity::class.java, intent) + .create() .get() Whitebox.setInternalState(activity, "mediaDetailPagerFragment", mediaDetailPagerFragment) Whitebox.setInternalState( activity, "depictionImagesListFragment", - depictionImagesListFragment + depictionImagesListFragment, ) Whitebox.setInternalState(activity, "supportFragmentManager", supportFragmentManager) - Whitebox.setInternalState(activity, "wikidataItem", wikidataItem) - - } @Test @@ -142,7 +134,7 @@ class WikidataItemDetailsActivityUnitTests { fun testOnOptionsItemSelectedCaseOne() { Assert.assertEquals( activity.onOptionsItemSelected(RoboMenuItem(R.id.browser_actions_menu_items)), - true + true, ) } @@ -169,5 +161,4 @@ class WikidataItemDetailsActivityUnitTests { fun testOnMediaClicked() { activity.onMediaClicked(0) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/child/PageableChildDepictionsDataSourceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/child/PageableChildDepictionsDataSourceTest.kt index 809f7b5d40..7102b84523 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/child/PageableChildDepictionsDataSourceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/child/PageableChildDepictionsDataSourceTest.kt @@ -14,6 +14,7 @@ import org.mockito.MockitoAnnotations class PageableChildDepictionsDataSourceTest { @Mock lateinit var okHttpJsonApiClient: OkHttpJsonApiClient + @Mock lateinit var liveDataConverter: LiveDataConverter diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSourceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSourceTest.kt index c65ccb7021..e51ca29b95 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSourceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/media/PageableDepictedMediaDataSourceTest.kt @@ -2,20 +2,19 @@ package fr.free.nrw.commons.explore.depictions.media import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.whenever -import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.media.WikidataMediaClient import io.reactivex.Single import org.junit.Assert import org.junit.Test -class PageableDepictedMediaDataSourceTest{ +class PageableDepictedMediaDataSourceTest { @Test fun `loadFunction loads Media`() { val mediaClient = mock() - whenever(mediaClient.fetchImagesForDepictedItem("test",0,1)) + whenever(mediaClient.fetchImagesForDepictedItem("test", 0, 1)) .thenReturn(Single.just(emptyList())) val pageableDepictedMediaDataSource = PageableDepictedMediaDataSource(mock(), mediaClient) pageableDepictedMediaDataSource.onQueryUpdated("test") - Assert.assertEquals(pageableDepictedMediaDataSource.loadFunction(0,1), emptyList()) + Assert.assertEquals(pageableDepictedMediaDataSource.loadFunction(0, 1), emptyList()) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/parent/PageableParentDepictionsDataSourceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/parent/PageableParentDepictionsDataSourceTest.kt index b0e2f81885..de573641c8 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/parent/PageableParentDepictionsDataSourceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/parent/PageableParentDepictionsDataSourceTest.kt @@ -33,4 +33,3 @@ class PageableParentDepictionsDataSourceTest { Assert.assertEquals(dataSource.loadFunction(1, 0), listOf(depictedItem())) } } - diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/media/MediaConverterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/media/MediaConverterTest.kt index 6d75598acf..e1e1b2ed74 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/media/MediaConverterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/media/MediaConverterTest.kt @@ -1,36 +1,34 @@ package fr.free.nrw.commons.explore.media -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.eq -import com.nhaarman.mockitokotlin2.notNull import fr.free.nrw.commons.Media +import fr.free.nrw.commons.wikidata.model.Entities +import fr.free.nrw.commons.wikidata.model.gallery.ExtMetadata +import fr.free.nrw.commons.wikidata.model.gallery.ImageInfo +import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test -import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations -import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage -import fr.free.nrw.commons.wikidata.model.gallery.ExtMetadata -import fr.free.nrw.commons.wikidata.model.gallery.ImageInfo -import fr.free.nrw.commons.wikidata.model.Entities import java.lang.IllegalArgumentException class MediaConverterTest { @Mock lateinit var page: MwQueryPage + @Mock lateinit var entity: Entities.Entity + @Mock lateinit var imageInfo: ImageInfo + @Mock lateinit var metadata: ExtMetadata lateinit var mediaConverter: MediaConverter lateinit var media: Media - @Before fun setUp() { MockitoAnnotations.openMocks(this) @@ -63,4 +61,4 @@ class MediaConverterTest { media = mediaConverter.convert(page, entity, imageInfo) assertEquals(media.thumbUrl, "thumbUrl") } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/media/PageableMediaDataSourceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/media/PageableMediaDataSourceTest.kt index 5832e21862..1d487bf542 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/media/PageableMediaDataSourceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/media/PageableMediaDataSourceTest.kt @@ -13,6 +13,7 @@ import org.mockito.MockitoAnnotations class PageableMediaDataSourceTest { @Mock lateinit var mediaConverter: MediaConverter + @Mock lateinit var mediaClient: MediaClient @@ -27,6 +28,6 @@ class PageableMediaDataSourceTest { .thenReturn(Single.just(emptyList())) val pageableMediaDataSource = PageableMediaDataSource(mock(), mediaClient) pageableMediaDataSource.onQueryUpdated("test") - Assert.assertEquals(pageableMediaDataSource.loadFunction(0,1), emptyList()) + Assert.assertEquals(pageableMediaDataSource.loadFunction(0, 1), emptyList()) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt index 6aa0aae5f0..3e550b6705 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt @@ -6,25 +6,43 @@ import android.database.Cursor import android.database.MatrixCursor import android.database.sqlite.SQLiteDatabase import android.os.RemoteException -import com.nhaarman.mockitokotlin2.* +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.anyOrNull +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.eq +import com.nhaarman.mockitokotlin2.inOrder +import com.nhaarman.mockitokotlin2.isA +import com.nhaarman.mockitokotlin2.isNull +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.explore.models.RecentSearch import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.BASE_URI import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.uriForId -import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.* -import org.junit.Assert.* +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.ALL_FIELDS +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_LAST_USED +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_NAME +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.CREATE_TABLE_STATEMENT +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.DROP_TABLE_STATEMENT +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.onCreate +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.onDelete +import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.onUpdate +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.verifyNoInteractions import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import java.util.* +import java.util.Date @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class RecentSearchesDaoTest { - private val columns = arrayOf(COLUMN_ID, COLUMN_NAME, COLUMN_LAST_USED) private val client: ContentProviderClient = mock() private val database: SQLiteDatabase = mock() @@ -238,11 +256,11 @@ class RecentSearchesDaoTest { assertEquals(123L, recentSearch?.lastSearched?.time) verify(client).query( - eq(BASE_URI), - eq(ALL_FIELDS), - eq("$COLUMN_NAME=?"), - queryCaptor.capture(), - isNull() + eq(BASE_URI), + eq(ALL_FIELDS), + eq("$COLUMN_NAME=?"), + queryCaptor.capture(), + isNull(), ) assertEquals("butterfly", queryCaptor.firstValue[0]) } @@ -274,11 +292,11 @@ class RecentSearchesDaoTest { assertEquals("butterfly", result[0]) verify(client).query( - eq(BASE_URI), - eq(ALL_FIELDS), - isNull(), - queryCaptor.capture(), - eq("$COLUMN_LAST_USED DESC") + eq(BASE_URI), + eq(ALL_FIELDS), + isNull(), + queryCaptor.capture(), + eq("$COLUMN_LAST_USED DESC"), ) assertEquals(0, queryCaptor.firstValue.size) } @@ -299,10 +317,10 @@ class RecentSearchesDaoTest { * Unit Test for creating entries in recent searches database. * @param rowCount No of rows */ - private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply { - for (i in 0 until rowCount) { - addRow(listOf("1", "butterfly", "123")) + private fun createCursor(rowCount: Int) = + MatrixCursor(columns, rowCount).apply { + for (i in 0 until rowCount) { + addRow(listOf("1", "butterfly", "123")) + } } - } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragmentUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragmentUnitTest.kt index 85dcaf97b4..8173dfc443 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragmentUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragmentUnitTest.kt @@ -3,17 +3,12 @@ package fr.free.nrw.commons.explore.recentsearches import android.content.Context import android.content.DialogInterface import android.view.LayoutInflater -import android.view.View import android.widget.ArrayAdapter -import android.widget.ImageView -import android.widget.ListView -import android.widget.TextView import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction import androidx.test.core.app.ApplicationProvider import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.OkHttpConnectionFactory -import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.contributions.MainActivity import fr.free.nrw.commons.createTestClient @@ -34,7 +29,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class RecentSearchesFragmentUnitTest { - private lateinit var fragment: RecentSearchesFragment private lateinit var fragmentManager: FragmentManager private lateinit var context: Context @@ -43,7 +37,6 @@ class RecentSearchesFragmentUnitTest { @Mock private lateinit var recentSearchesDao: RecentSearchesDao - @Mock private lateinit var adapter: ArrayAdapter<*> @@ -96,10 +89,11 @@ class RecentSearchesFragmentUnitTest { @Test @Throws(Exception::class) fun testShowDeleteRecentAlertDialog() { - val method: Method = RecentSearchesFragment::class.java.getDeclaredMethod( - "showDeleteRecentAlertDialog", - Context::class.java - ) + val method: Method = + RecentSearchesFragment::class.java.getDeclaredMethod( + "showDeleteRecentAlertDialog", + Context::class.java, + ) method.isAccessible = true method.invoke(fragment, context) } @@ -107,11 +101,12 @@ class RecentSearchesFragmentUnitTest { @Test @Throws(Exception::class) fun testSetDeleteRecentPositiveButton() { - val method: Method = RecentSearchesFragment::class.java.getDeclaredMethod( - "setDeleteRecentPositiveButton", - Context::class.java, - DialogInterface::class.java - ) + val method: Method = + RecentSearchesFragment::class.java.getDeclaredMethod( + "setDeleteRecentPositiveButton", + Context::class.java, + DialogInterface::class.java, + ) method.isAccessible = true method.invoke(fragment, context, dialog) } @@ -119,11 +114,12 @@ class RecentSearchesFragmentUnitTest { @Test @Throws(Exception::class) fun testShowDeleteAlertDialog() { - val method: Method = RecentSearchesFragment::class.java.getDeclaredMethod( - "showDeleteAlertDialog", - Context::class.java, - Int::class.java - ) + val method: Method = + RecentSearchesFragment::class.java.getDeclaredMethod( + "showDeleteAlertDialog", + Context::class.java, + Int::class.java, + ) method.isAccessible = true method.invoke(fragment, context, 0) } @@ -131,14 +127,14 @@ class RecentSearchesFragmentUnitTest { @Test @Throws(Exception::class) fun testSetDeletePositiveButton() { - val method: Method = RecentSearchesFragment::class.java.getDeclaredMethod( - "setDeletePositiveButton", - Context::class.java, - DialogInterface::class.java, - Int::class.java - ) + val method: Method = + RecentSearchesFragment::class.java.getDeclaredMethod( + "setDeletePositiveButton", + Context::class.java, + DialogInterface::class.java, + Int::class.java, + ) method.isAccessible = true method.invoke(fragment, context, dialog, 0) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/search/SearchActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/search/SearchActivityUnitTests.kt index c0b99c6607..751046e7f7 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/search/SearchActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/search/SearchActivityUnitTests.kt @@ -1,16 +1,12 @@ package fr.free.nrw.commons.explore.search import android.content.Context -import android.widget.SearchView -import androidx.fragment.app.FragmentController import androidx.fragment.app.FragmentManager import androidx.test.core.app.ApplicationProvider -import androidx.viewpager.widget.ViewPager import com.nhaarman.mockitokotlin2.verify import fr.free.nrw.commons.Media import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.ViewPagerAdapter -import fr.free.nrw.commons.databinding.ActivitySearchBinding import fr.free.nrw.commons.explore.SearchActivity import fr.free.nrw.commons.explore.categories.search.SearchCategoryFragment import fr.free.nrw.commons.explore.depictions.search.SearchDepictionsFragment @@ -37,12 +33,10 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import java.lang.reflect.Method - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class SearchActivityUnitTests { - @Mock private lateinit var activity: SearchActivity @@ -175,10 +169,11 @@ class SearchActivityUnitTests { fun testHandleSearchCaseEmpty() { Whitebox.setInternalState(activity, "recentSearchesFragment", recentSearchesFragment) val query = "" - val method: Method = SearchActivity::class.java.getDeclaredMethod( - "handleSearch", - CharSequence::class.java - ) + val method: Method = + SearchActivity::class.java.getDeclaredMethod( + "handleSearch", + CharSequence::class.java, + ) method.isAccessible = true method.invoke(activity, query) verify(recentSearchesFragment).updateRecentSearches() @@ -209,10 +204,11 @@ class SearchActivityUnitTests { `when`(searchMediaFragment.isRemoving).thenReturn(false) `when`(searchCategoryFragment.isRemoving).thenReturn(false) - val method: Method = SearchActivity::class.java.getDeclaredMethod( - "handleSearch", - CharSequence::class.java - ) + val method: Method = + SearchActivity::class.java.getDeclaredMethod( + "handleSearch", + CharSequence::class.java, + ) method.isAccessible = true method.invoke(activity, query) verify(recentSearchesDao).find(query) @@ -220,5 +216,4 @@ class SearchActivityUnitTests { verify(searchMediaFragment).onQueryUpdated(query) verify(searchCategoryFragment).onQueryUpdated(query) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackContentCreatorUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackContentCreatorUnitTests.kt index a238d8f7ae..5ca2f544b3 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackContentCreatorUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackContentCreatorUnitTests.kt @@ -22,7 +22,7 @@ class FeedbackContentCreatorUnitTests { private lateinit var feedback: Feedback private lateinit var context: Context - + @Before fun setup() { MockitoAnnotations.openMocks(this) @@ -37,5 +37,4 @@ class FeedbackContentCreatorUnitTests { Assert.assertNotNull(creator.getSectionText()) Assert.assertNotNull(creator.getSectionTitle()) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackDialogTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackDialogTests.kt index 7b3c2738cf..e625057078 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackDialogTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackDialogTests.kt @@ -7,9 +7,9 @@ import androidx.test.core.app.ApplicationProvider import com.nhaarman.mockitokotlin2.doReturn import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.TestUtility.setFinalStatic import fr.free.nrw.commons.contributions.MainActivity +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.databinding.DialogFeedbackBinding import fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText import org.junit.Assert @@ -66,8 +66,9 @@ class FeedbackDialogTests { val editable = mock(Editable::class.java) val ed = mock(PasteSensitiveTextInputEditText::class.java) setFinalStatic( - DialogFeedbackBinding::class.java.getDeclaredField("feedbackItemEditText"), - ed) + DialogFeedbackBinding::class.java.getDeclaredField("feedbackItemEditText"), + ed, + ) `when`(ed?.text).thenReturn(editable) doReturn(editable).`when`(ed)?.text doReturn("").`when`(editable).toString() @@ -80,13 +81,13 @@ class FeedbackDialogTests { val editable: Editable = mock(Editable::class.java) val ed = mock(PasteSensitiveTextInputEditText::class.java) setFinalStatic( - DialogFeedbackBinding::class.java.getDeclaredField("feedbackItemEditText"), - ed) + DialogFeedbackBinding::class.java.getDeclaredField("feedbackItemEditText"), + ed, + ) `when`(ed?.text).thenReturn(editable) `when`(editable.toString()).thenReturn("1234") Assert.assertEquals(ed.text.toString(), "1234") dialog.submitFeedback() } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackUnitTests.kt index dc690e4598..81c3bc1517 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/feedback/FeedbackUnitTests.kt @@ -61,5 +61,4 @@ class FeedbackUnitTests { feedback.networkType = "network" Assert.assertEquals(feedback.networkType, "network") } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/filepicker/FilePickerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/filepicker/FilePickerTest.kt index b98c5a1c9a..171e04b4e0 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/filepicker/FilePickerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/filepicker/FilePickerTest.kt @@ -15,9 +15,13 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.* -import org.mockito.Mockito.`when` +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.Captor +import org.mockito.Mock import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode @@ -28,11 +32,10 @@ import kotlin.random.Random.Default.nextBoolean @Config( sdk = [21], application = TestCommonsApplication::class, - shadows = [ShadowFileProvider::class] + shadows = [ShadowFileProvider::class], ) @LooperMode(LooperMode.Mode.PAUSED) class FilePickerTest { - @Mock internal lateinit var activity: Activity @@ -64,7 +67,7 @@ class FilePickerTest { FilePicker.openGallery(activity, 0, nextBoolean()) verify(activity).startActivityForResult( ArgumentMatchers.any(), - requestCodeCaptor?.capture()?.toInt()!! + requestCodeCaptor?.capture()?.toInt()!!, ) assertEquals(requestCodeCaptor?.value, RequestCodes.PICK_PICTURE_FROM_GALLERY) } @@ -79,7 +82,7 @@ class FilePickerTest { FilePicker.openCameraForImage(activity, 0) verify(activity).startActivityForResult( ArgumentMatchers.any(), - requestCodeCaptor?.capture()?.toInt()!! + requestCodeCaptor?.capture()?.toInt()!!, ) assertEquals(requestCodeCaptor?.value, RequestCodes.TAKE_PICTURE) } @@ -87,10 +90,11 @@ class FilePickerTest { @Test fun testCreateCameraPictureFile() { val mockFilePicker = mock(FilePicker::class.java) - val method: Method = FilePicker::class.java.getDeclaredMethod( - "createCameraPictureFile", - Context::class.java - ) + val method: Method = + FilePicker::class.java.getDeclaredMethod( + "createCameraPictureFile", + Context::class.java, + ) method.isAccessible = true method.invoke(mockFilePicker, context) } @@ -98,11 +102,12 @@ class FilePickerTest { @Test fun testCreateCameraForImageIntent() { val mockFilePicker = mock(FilePicker::class.java) - val method: Method = FilePicker::class.java.getDeclaredMethod( - "createCameraForImageIntent", - Context::class.java, - Int::class.java - ) + val method: Method = + FilePicker::class.java.getDeclaredMethod( + "createCameraForImageIntent", + Context::class.java, + Int::class.java, + ) method.isAccessible = true method.invoke(mockFilePicker, context, 0) } @@ -112,11 +117,12 @@ class FilePickerTest { val mockFilePicker = mock(FilePicker::class.java) val mockUri = mock(Uri::class.java) val mockContext = mock(Context::class.java) - val method: Method = FilePicker::class.java.getDeclaredMethod( - "revokeWritePermission", - Context::class.java, - Uri::class.java - ) + val method: Method = + FilePicker::class.java.getDeclaredMethod( + "revokeWritePermission", + Context::class.java, + Uri::class.java, + ) method.isAccessible = true method.invoke(mockFilePicker, mockContext, mockUri) } @@ -124,10 +130,11 @@ class FilePickerTest { @Test fun testRestoreType() { val mockFilePicker = mock(FilePicker::class.java) - val method: Method = FilePicker::class.java.getDeclaredMethod( - "restoreType", - Context::class.java - ) + val method: Method = + FilePicker::class.java.getDeclaredMethod( + "restoreType", + Context::class.java, + ) method.isAccessible = true method.invoke(mockFilePicker, context) } @@ -135,10 +142,11 @@ class FilePickerTest { @Test fun testTakenCameraPicture() { val mockFilePicker = mock(FilePicker::class.java) - val method: Method = FilePicker::class.java.getDeclaredMethod( - "takenCameraPicture", - Context::class.java - ) + val method: Method = + FilePicker::class.java.getDeclaredMethod( + "takenCameraPicture", + Context::class.java, + ) method.isAccessible = true method.invoke(mockFilePicker, context) } @@ -148,10 +156,11 @@ class FilePickerTest { val mockFilePicker = mock(FilePicker::class.java) `when`(PreferenceManager.getDefaultSharedPreferences(activity)).thenReturn(sharedPref) `when`(sharedPref.getString("last_photo", null)).thenReturn("") - val method: Method = FilePicker::class.java.getDeclaredMethod( - "takenCameraPicture", - Context::class.java - ) + val method: Method = + FilePicker::class.java.getDeclaredMethod( + "takenCameraPicture", + Context::class.java, + ) method.isAccessible = true method.invoke(mockFilePicker, activity) } @@ -159,10 +168,11 @@ class FilePickerTest { @Test fun testTakenCameraVideo() { val mockFilePicker = mock(FilePicker::class.java) - val method: Method = FilePicker::class.java.getDeclaredMethod( - "takenCameraVideo", - Context::class.java - ) + val method: Method = + FilePicker::class.java.getDeclaredMethod( + "takenCameraVideo", + Context::class.java, + ) method.isAccessible = true method.invoke(mockFilePicker, context) } @@ -172,10 +182,11 @@ class FilePickerTest { val mockFilePicker = mock(FilePicker::class.java) `when`(PreferenceManager.getDefaultSharedPreferences(activity)).thenReturn(sharedPref) `when`(sharedPref.getString("last_video", null)).thenReturn("") - val method: Method = FilePicker::class.java.getDeclaredMethod( - "takenCameraVideo", - Context::class.java - ) + val method: Method = + FilePicker::class.java.getDeclaredMethod( + "takenCameraVideo", + Context::class.java, + ) method.isAccessible = true method.invoke(mockFilePicker, activity) } @@ -184,10 +195,11 @@ class FilePickerTest { fun testIsPhoto() { val mockFilePicker = mock(FilePicker::class.java) val mockIntent = mock(Intent::class.java) - val method: Method = FilePicker::class.java.getDeclaredMethod( - "isPhoto", - Intent::class.java - ) + val method: Method = + FilePicker::class.java.getDeclaredMethod( + "isPhoto", + Intent::class.java, + ) method.isAccessible = true method.invoke(mockFilePicker, mockIntent) } @@ -201,24 +213,28 @@ class FilePickerTest { mockIntent, activity, object : DefaultCallback() { - override fun onCanceled(source: FilePicker.ImageSource, type: Int) { + override fun onCanceled( + source: FilePicker.ImageSource, + type: Int, + ) { super.onCanceled(source, type) } override fun onImagePickerError( e: Exception, source: FilePicker.ImageSource, - type: Int + type: Int, ) { } override fun onImagesPicked( imagesFiles: List, source: FilePicker.ImageSource, - type: Int + type: Int, ) { } - }) + }, + ) } @Test @@ -230,4 +246,4 @@ class FilePickerTest { verify(activity).startActivityForResult(ArgumentMatchers.any(), requestCodeCaptor?.capture()?.toInt()!!) assertEquals(requestCodeCaptor?.value, RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/leaderboard/LeaderboardFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/leaderboard/LeaderboardFragmentUnitTests.kt index 7efc3b9450..9ee45fd102 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/leaderboard/LeaderboardFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/leaderboard/LeaderboardFragmentUnitTests.kt @@ -4,20 +4,15 @@ import android.accounts.Account import android.content.Context import android.os.Looper.getMainLooper import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup -import android.widget.Button -import android.widget.ProgressBar -import android.widget.Spinner import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction -import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ApplicationProvider import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.auth.SessionManager +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.databinding.FragmentLeaderboardBinding import fr.free.nrw.commons.profile.ProfileActivity import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment @@ -44,13 +39,11 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class LeaderboardFragmentUnitTests { - private lateinit var fragment: LeaderboardFragment private lateinit var fragmentManager: FragmentManager private lateinit var context: Context private lateinit var layoutInflater: LayoutInflater - @Mock private lateinit var viewModel: LeaderboardListViewModel @@ -104,9 +97,10 @@ class LeaderboardFragmentUnitTests { @Test @Throws(Exception::class) fun testRefreshLeaderboard() { - val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( - "refreshLeaderboard" - ) + val method: Method = + LeaderboardFragment::class.java.getDeclaredMethod( + "refreshLeaderboard", + ) method.isAccessible = true method.invoke(fragment) } @@ -114,9 +108,10 @@ class LeaderboardFragmentUnitTests { @Test @Throws(Exception::class) fun testScrollToUserRank() { - val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( - "scrollToUserRank" - ) + val method: Method = + LeaderboardFragment::class.java.getDeclaredMethod( + "scrollToUserRank", + ) method.isAccessible = true method.invoke(fragment) } @@ -126,9 +121,10 @@ class LeaderboardFragmentUnitTests { fun testScrollToUserRankCaseNonZeroTrue() { Whitebox.setInternalState(fragment, "userRank", 1) `when`(adapter.itemCount).thenReturn(3) - val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( - "scrollToUserRank" - ) + val method: Method = + LeaderboardFragment::class.java.getDeclaredMethod( + "scrollToUserRank", + ) method.isAccessible = true method.invoke(fragment) } @@ -138,26 +134,27 @@ class LeaderboardFragmentUnitTests { fun testScrollToUserRankCaseNonZeroFalse() { Whitebox.setInternalState(fragment, "userRank", 1) `when`(adapter.itemCount).thenReturn(1) - val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( - "scrollToUserRank" - ) + val method: Method = + LeaderboardFragment::class.java.getDeclaredMethod( + "scrollToUserRank", + ) method.isAccessible = true method.invoke(fragment) } - @Test @Throws(Exception::class) fun testSetLeaderboard() { Whitebox.setInternalState(fragment, "sessionManager", sessionManager) `when`(sessionManager.currentAccount).thenReturn(account) - val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( - "setLeaderboard", - String::class.java, - String::class.java, - Int::class.java, - Int::class.java - ) + val method: Method = + LeaderboardFragment::class.java.getDeclaredMethod( + "setLeaderboard", + String::class.java, + String::class.java, + Int::class.java, + Int::class.java, + ) method.isAccessible = true method.invoke(fragment, "", "", 0, 0) } @@ -165,9 +162,10 @@ class LeaderboardFragmentUnitTests { @Test @Throws(Exception::class) fun testHideProgressBar() { - val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( - "hideProgressBar" - ) + val method: Method = + LeaderboardFragment::class.java.getDeclaredMethod( + "hideProgressBar", + ) method.isAccessible = true method.invoke(fragment) } @@ -175,9 +173,10 @@ class LeaderboardFragmentUnitTests { @Test @Throws(Exception::class) fun testShowProgressBar() { - val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( - "showProgressBar" - ) + val method: Method = + LeaderboardFragment::class.java.getDeclaredMethod( + "showProgressBar", + ) method.isAccessible = true method.invoke(fragment) } @@ -185,9 +184,10 @@ class LeaderboardFragmentUnitTests { @Test @Throws(Exception::class) fun testOnError() { - val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( - "onError" - ) + val method: Method = + LeaderboardFragment::class.java.getDeclaredMethod( + "onError", + ) method.isAccessible = true method.invoke(fragment) } @@ -195,10 +195,11 @@ class LeaderboardFragmentUnitTests { @Test @Throws(Exception::class) fun testMenuVisibilityOverrideNotVisible() { - val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( - "setMenuVisibility", - Boolean::class.java - ) + val method: Method = + LeaderboardFragment::class.java.getDeclaredMethod( + "setMenuVisibility", + Boolean::class.java, + ) method.isAccessible = true method.invoke(fragment, false) Assert.assertNull(ShadowToast.getLatestToast()) @@ -209,23 +210,22 @@ class LeaderboardFragmentUnitTests { fun testMenuVisibilityOverrideVisibleWithContext() { shadowOf(getMainLooper()).idle() `when`(parentView.context).thenReturn(context) - val method: Method = LeaderboardFragment::class.java.getDeclaredMethod( - "setMenuVisibility", - Boolean::class.java - ) + val method: Method = + LeaderboardFragment::class.java.getDeclaredMethod( + "setMenuVisibility", + Boolean::class.java, + ) method.isAccessible = true method.invoke(fragment, true) - if(isBetaFlavour) { + if (isBetaFlavour) { Assert.assertEquals( ShadowToast.getTextOfLatestToast().toString(), - context.getString(R.string.leaderboard_unavailable_beta) + context.getString(R.string.leaderboard_unavailable_beta), ) } else { Assert.assertNull( - ShadowToast.getTextOfLatestToast() + ShadowToast.getTextOfLatestToast(), ) } - } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/leaderboard/LeaderboardListAdapterUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/leaderboard/LeaderboardListAdapterUnitTests.kt index d65c11f42b..50932d67b9 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/leaderboard/LeaderboardListAdapterUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/leaderboard/LeaderboardListAdapterUnitTests.kt @@ -28,7 +28,6 @@ import java.lang.reflect.Field @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class LeaderboardListAdapterUnitTests { - private lateinit var context: Context private lateinit var adapter: LeaderboardListAdapter @@ -97,5 +96,4 @@ class LeaderboardListAdapterUnitTests { `when`(pagedList.size).thenReturn(list.size) return pagedList } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/location/LatLngTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/location/LatLngTest.kt index 1e7fa67fed..051b18d18f 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/location/LatLngTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/location/LatLngTest.kt @@ -9,7 +9,6 @@ class LatLngTest { @Before fun setup() { - } @Test @@ -40,14 +39,14 @@ class LatLngTest { fun testHashCodeDiffersWenLngZero() { latLng1 = LatLng(2.0, 0.0, 0.0f) latLng2 = LatLng(1.0, 0.0, 0.0f) - assert(latLng1.hashCode()!=latLng2.hashCode()) + assert(latLng1.hashCode() != latLng2.hashCode()) } @Test fun testHashCodeDiffersWenLatZero() { latLng1 = LatLng(0.0, 1.0, 0.0f) latLng2 = LatLng(0.0, 2.0, 0.0f) - assert(latLng1.hashCode()!=latLng2.hashCode()) + assert(latLng1.hashCode() != latLng2.hashCode()) } @Test @@ -62,4 +61,4 @@ class LatLngTest { latLng1 = LatLng(1.0, 2.0, 5.0f) assert(latLng1.toString().equals("lat/lng: (1.0,2.0)")) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/locationpicker/LocationPickerActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/locationpicker/LocationPickerActivityUnitTests.kt index e52028844a..6405ac9db8 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/locationpicker/LocationPickerActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/locationpicker/LocationPickerActivityUnitTests.kt @@ -19,12 +19,12 @@ import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_ZO import io.reactivex.android.plugins.RxAndroidPlugins import io.reactivex.schedulers.Schedulers import org.junit.Assert -import org.junit.Assert.* import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.* +import org.mockito.Mockito.any +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.osmdroid.util.GeoPoint import org.powermock.reflect.Whitebox @@ -40,7 +40,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class LocationPickerActivityUnitTests { - private lateinit var activity: LocationPickerActivity private lateinit var context: Context @@ -114,9 +113,10 @@ class LocationPickerActivityUnitTests { @Test @Throws(Exception::class) fun testAddCredits() { - val method: Method = LocationPickerActivity::class.java.getDeclaredMethod( - "addCredits" - ) + val method: Method = + LocationPickerActivity::class.java.getDeclaredMethod( + "addCredits", + ) method.isAccessible = true method.invoke(activity) verify(tvAttribution).text = any() @@ -126,9 +126,10 @@ class LocationPickerActivityUnitTests { @Test @Throws(Exception::class) fun testOnClickModifyLocation() { - val method: Method = LocationPickerActivity::class.java.getDeclaredMethod( - "onClickModifyLocation" - ) + val method: Method = + LocationPickerActivity::class.java.getDeclaredMethod( + "onClickModifyLocation", + ) method.isAccessible = true method.invoke(activity) verify(placeSelectedButton, times(1)).visibility = View.VISIBLE @@ -145,9 +146,10 @@ class LocationPickerActivityUnitTests { @Test @Throws(Exception::class) fun testOnClickRemoveLocation() { - val method: Method = LocationPickerActivity::class.java.getDeclaredMethod( - "onClickRemoveLocation" - ) + val method: Method = + LocationPickerActivity::class.java.getDeclaredMethod( + "onClickRemoveLocation", + ) method.isAccessible = true method.invoke(activity) } @@ -156,22 +158,20 @@ class LocationPickerActivityUnitTests { @Throws(Exception::class) fun testPlaceSelected() { Shadows.shadowOf(Looper.getMainLooper()).idle() - Whitebox.setInternalState(activity,"activity", "NoLocationUploadActivity") + Whitebox.setInternalState(activity, "activity", "NoLocationUploadActivity") val position = GeoPoint(51.50550, -0.07520) - val method: Method = LocationPickerActivity::class.java.getDeclaredMethod( - "placeSelected" - ) + val method: Method = + LocationPickerActivity::class.java.getDeclaredMethod( + "placeSelected", + ) `when`(mapView.mapCenter).thenReturn(position) `when`(mapView.zoomLevel).thenReturn(15) method.isAccessible = true method.invoke(activity) verify(applicationKvStore, times(1)).putString( LAST_LOCATION, - position.latitude.toString() + "," + position.longitude.toString() + position.latitude.toString() + "," + position.longitude.toString(), ) verify(applicationKvStore, times(1)).putString(LAST_ZOOM, mapView.zoomLevel.toString()) } - - - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/locationpicker/LocationPickerViewModelUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/locationpicker/LocationPickerViewModelUnitTests.kt index b75927ffd8..84a74741dd 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/locationpicker/LocationPickerViewModelUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/locationpicker/LocationPickerViewModelUnitTests.kt @@ -18,7 +18,6 @@ import retrofit2.Call import retrofit2.Response class LocationPickerViewModelUnitTests { - private lateinit var viewModel: LocationPickerViewModel @Mock @@ -68,5 +67,4 @@ class LocationPickerViewModelUnitTests { fun testGetResult() { viewModel.result } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/login/LoginActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/login/LoginActivityUnitTests.kt index 3a0bf557ad..ba6aa6c55d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/login/LoginActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/login/LoginActivityUnitTests.kt @@ -9,8 +9,8 @@ import androidx.test.core.app.ApplicationProvider import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.auth.LoginActivity +import fr.free.nrw.commons.createTestClient import org.junit.Assert import org.junit.Before import org.junit.Test @@ -23,11 +23,9 @@ import org.robolectric.annotation.Config import org.robolectric.fakes.RoboMenuItem import java.lang.reflect.Field - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class LoginActivityUnitTests { - @Mock private lateinit var activity: LoginActivity @@ -46,7 +44,6 @@ class LoginActivityUnitTests { @Before fun setUp() { - MockitoAnnotations.openMocks(this) OkHttpConnectionFactory.CLIENT = createTestClient() @@ -116,5 +113,4 @@ class LoginActivityUnitTests { fun testSetContentView() { activity.setContentView(view, params) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/media/CustomOkHttpNetworkFetcherUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/media/CustomOkHttpNetworkFetcherUnitTest.kt index 2c7e12a4cc..16a35a67ba 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/media/CustomOkHttpNetworkFetcherUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/media/CustomOkHttpNetworkFetcherUnitTest.kt @@ -12,7 +12,13 @@ import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.kvstore.JsonKvStore -import okhttp3.* +import okhttp3.CacheControl +import okhttp3.Call +import okhttp3.OkHttpClient +import okhttp3.Protocol +import okhttp3.Request +import okhttp3.Response +import okhttp3.ResponseBody import org.junit.Assert import org.junit.Before import org.junit.Test @@ -24,7 +30,6 @@ import java.lang.reflect.Method import java.util.concurrent.Executor class CustomOkHttpNetworkFetcherUnitTest { - private lateinit var fetcher: CustomOkHttpNetworkFetcher private lateinit var okHttpClient: OkHttpClient private lateinit var state: CustomOkHttpNetworkFetcher.OkHttpNetworkFetchState @@ -91,8 +96,8 @@ class CustomOkHttpNetworkFetcherUnitTest { whenever( defaultKvStore.getBoolean( CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED, - false - ) + false, + ), ).thenReturn(true) fetcher.fetch(state, callback) verify(callback).onFailure(any()) @@ -105,8 +110,8 @@ class CustomOkHttpNetworkFetcherUnitTest { whenever( defaultKvStore.getBoolean( CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED, - false - ) + false, + ), ).thenReturn(false) fetcher.fetch(state, callback) fetcher.onFetchCompletion(state, 0) @@ -120,8 +125,8 @@ class CustomOkHttpNetworkFetcherUnitTest { whenever( defaultKvStore.getBoolean( CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED, - false - ) + false, + ), ).thenReturn(false) fetcher.fetch(state, callback) verify(callback).onFailure(any()) @@ -138,9 +143,11 @@ class CustomOkHttpNetworkFetcherUnitTest { @Throws(Exception::class) fun testOnFetchCancellationRequested() { Whitebox.setInternalState(fetcher, "mCancellationExecutor", executor) - val method: Method = CustomOkHttpNetworkFetcher::class.java.getDeclaredMethod( - "onFetchCancellationRequested", Call::class.java, - ) + val method: Method = + CustomOkHttpNetworkFetcher::class.java.getDeclaredMethod( + "onFetchCancellationRequested", + Call::class.java, + ) method.isAccessible = true method.invoke(fetcher, call) verify(executor).execute(any()) @@ -152,13 +159,14 @@ class CustomOkHttpNetworkFetcherUnitTest { whenever(response.body).thenReturn(body) whenever(response.isSuccessful).thenReturn(false) whenever(call.isCanceled()).thenReturn(true) - val method: Method = CustomOkHttpNetworkFetcher::class.java.getDeclaredMethod( - "onFetchResponse", - CustomOkHttpNetworkFetcher.OkHttpNetworkFetchState::class.java, - Call::class.java, - Response::class.java, - NetworkFetcher.Callback::class.java, - ) + val method: Method = + CustomOkHttpNetworkFetcher::class.java.getDeclaredMethod( + "onFetchResponse", + CustomOkHttpNetworkFetcher.OkHttpNetworkFetchState::class.java, + Call::class.java, + Response::class.java, + NetworkFetcher.Callback::class.java, + ) method.isAccessible = true method.invoke(fetcher, state, call, response, callback) verify(callback).onCancellation() @@ -174,22 +182,25 @@ class CustomOkHttpNetworkFetcherUnitTest { whenever(body.contentLength()).thenReturn(-1) // Build Response object with Content-Range header - val responseBuilder = Response.Builder() - .request(Request.Builder().url("http://example.com").build()) - .protocol(Protocol.HTTP_1_1) - .code(200) - .message("OK") - .header("Content-Range", "bytes 200-1000/67589") - .body(body) + val responseBuilder = + Response + .Builder() + .request(Request.Builder().url("http://example.com").build()) + .protocol(Protocol.HTTP_1_1) + .code(200) + .message("OK") + .header("Content-Range", "bytes 200-1000/67589") + .body(body) whenever(call.execute()).thenReturn(responseBuilder.build()) - val method: Method = CustomOkHttpNetworkFetcher::class.java.getDeclaredMethod( - "onFetchResponse", - CustomOkHttpNetworkFetcher.OkHttpNetworkFetchState::class.java, - Call::class.java, - Response::class.java, - NetworkFetcher.Callback::class.java, - ) + val method: Method = + CustomOkHttpNetworkFetcher::class.java.getDeclaredMethod( + "onFetchResponse", + CustomOkHttpNetworkFetcher.OkHttpNetworkFetchState::class.java, + Call::class.java, + Response::class.java, + NetworkFetcher.Callback::class.java, + ) method.isAccessible = true method.invoke(fetcher, state, call, responseBuilder.build(), callback) verify(callback).onResponse(null, 0) @@ -205,25 +216,27 @@ class CustomOkHttpNetworkFetcherUnitTest { whenever(body.contentLength()).thenReturn(-1) // Build Response object with Content-Range header - val responseBuilder = Response.Builder() - .request(Request.Builder().url("http://example.com").build()) - .protocol(Protocol.HTTP_1_1) - .code(200) - .message("OK") - .header("Content-Range", "Test") - .body(body) + val responseBuilder = + Response + .Builder() + .request(Request.Builder().url("http://example.com").build()) + .protocol(Protocol.HTTP_1_1) + .code(200) + .message("OK") + .header("Content-Range", "Test") + .body(body) whenever(call.execute()).thenReturn(responseBuilder.build()) - val method: Method = CustomOkHttpNetworkFetcher::class.java.getDeclaredMethod( - "onFetchResponse", - CustomOkHttpNetworkFetcher.OkHttpNetworkFetchState::class.java, - Call::class.java, - Response::class.java, - NetworkFetcher.Callback::class.java, - ) + val method: Method = + CustomOkHttpNetworkFetcher::class.java.getDeclaredMethod( + "onFetchResponse", + CustomOkHttpNetworkFetcher.OkHttpNetworkFetchState::class.java, + Call::class.java, + Response::class.java, + NetworkFetcher.Callback::class.java, + ) method.isAccessible = true method.invoke(fetcher, state, call, responseBuilder.build(), callback) verify(callback).onFailure(any()) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaClientTest.kt index c7f5a0e47a..cc6cdb6179 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaClientTest.kt @@ -6,6 +6,11 @@ import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.Media import fr.free.nrw.commons.explore.media.MediaConverter import fr.free.nrw.commons.media.model.PageMediaListResponse +import fr.free.nrw.commons.wikidata.model.Entities +import fr.free.nrw.commons.wikidata.model.gallery.ImageInfo +import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResult import io.reactivex.Single import media import org.junit.Before @@ -13,15 +18,8 @@ import org.junit.Test import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.MockitoAnnotations -import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResult -import fr.free.nrw.commons.wikidata.model.gallery.ImageInfo -import fr.free.nrw.commons.wikidata.model.Entities - class MediaClientTest { - @Mock internal lateinit var mediaInterface: MediaInterface @@ -71,9 +69,10 @@ class MediaClientTest { @Test fun `checkFileExistsUsingSha returns false with no Images`() { - val mwQueryResponse = mockQuery { - whenever(allImages()).thenReturn(listOf()) - } + val mwQueryResponse = + mockQuery { + whenever(allImages()).thenReturn(listOf()) + } whenever(mediaInterface.checkFileExistsUsingSha("")) .thenReturn(Single.just(mwQueryResponse)) mediaClient.checkFileExistsUsingSha("").test().assertValue(false) @@ -81,9 +80,10 @@ class MediaClientTest { @Test fun `checkFileExistsUsingSha returns true with Images`() { - val mwQueryResponse = mockQuery { - whenever(allImages()).thenReturn(listOf(mock())) - } + val mwQueryResponse = + mockQuery { + whenever(allImages()).thenReturn(listOf(mock())) + } whenever(mediaInterface.checkFileExistsUsingSha("")) .thenReturn(Single.just(mwQueryResponse)) mediaClient.checkFileExistsUsingSha("").test().assertValue(true) @@ -101,7 +101,7 @@ class MediaClientTest { mediaClient.getMediaListFromCategory("").test().assertError { true } mediaClient.resetCategoryContinuation("") - val (resetMwQueryResponse, resetMedia)=expectSuccessfulMapping() + val (resetMwQueryResponse, resetMedia) = expectSuccessfulMapping() whenever(mediaInterface.getMediaListFromCategory("", 10, emptyMap())) .thenReturn(Single.just(resetMwQueryResponse)) mediaClient.getMediaListFromCategory("").test().assertValues(listOf(resetMedia)) @@ -121,7 +121,8 @@ class MediaClientTest { val (mwQueryResponse, media) = expectSuccessfulMapping() whenever(mediaInterface.getMediaListFromSearch("", 0, 1)) .thenReturn(Single.just(mwQueryResponse)) - mediaClient.getMediaListFromSearch("", 0, 1) + mediaClient + .getMediaListFromSearch("", 0, 1) .test() .assertValues(listOf(media)) } @@ -131,7 +132,8 @@ class MediaClientTest { val (mwQueryResponse, media) = expectSuccessfulMapping() whenever(mediaInterface.fetchImagesForDepictedItem("haswbstatement:${BuildConfig.DEPICTS_PROPERTY}=", "0", "1")) .thenReturn(Single.just(mwQueryResponse)) - mediaClient.fetchImagesForDepictedItem("", 0, 1) + mediaClient + .fetchImagesForDepictedItem("", 0, 1) .test() .assertValues(listOf(media)) } @@ -178,7 +180,7 @@ class MediaClientTest { fun `getEntities invokes interface with non empty ids`() { val entities = mock() whenever(mediaDetailInterface.getEntity("1|2")).thenReturn(Single.just(entities)) - mediaClient.getEntities(listOf("1","2")).test().assertValue(entities) + mediaClient.getEntities(listOf("1", "2")).test().assertValue(entities) } @Test @@ -213,21 +215,21 @@ class MediaClientTest { return mwQueryResponse } - private fun expectResponseWithPageId(expectedId: Int): MwQueryResponse { - return mockQuery { + private fun expectResponseWithPageId(expectedId: Int): MwQueryResponse = + mockQuery { val mwQueryPage = mock() whenever(firstPage()).thenReturn(mwQueryPage) whenever(mwQueryPage.pageId()).thenReturn(expectedId) } - } private fun expectSuccessfulMapping(continuationMap: Map? = emptyMap()): Pair { val media = media() val mwQueryPage = mock() - val mwQueryResponse = mockQuery { - whenever(pages()).thenReturn(listOf(mwQueryPage, mwQueryPage)) - whenever(mwQueryPage.pageId()).thenReturn(1, 2) - } + val mwQueryResponse = + mockQuery { + whenever(pages()).thenReturn(listOf(mwQueryPage, mwQueryPage)) + whenever(mwQueryPage.pageId()).thenReturn(1, 2) + } whenever(mwQueryResponse.continuation()).thenReturn(continuationMap) val entities = mock() whenever(mediaDetailInterface.getEntity("M1|M2")).thenReturn(Single.just(entities)) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailFragmentUnitTests.kt index a153027c85..7f9e3d5763 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailFragmentUnitTests.kt @@ -12,7 +12,13 @@ import android.view.View import android.view.View.GONE import android.view.ViewTreeObserver import android.webkit.WebView -import android.widget.* +import android.widget.Button +import android.widget.LinearLayout +import android.widget.ListView +import android.widget.ProgressBar +import android.widget.ScrollView +import android.widget.Spinner +import android.widget.TextView import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction import androidx.test.core.app.ApplicationProvider @@ -25,7 +31,6 @@ import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.LocationPicker.LocationPickerActivity import fr.free.nrw.commons.Media import fr.free.nrw.commons.OkHttpConnectionFactory -import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.databinding.FragmentMediaDetailBinding @@ -41,8 +46,18 @@ import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.* -import org.mockito.Mockito.* +import org.mockito.ArgumentMatchers +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.anyString +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations import org.powermock.reflect.Whitebox import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner @@ -54,15 +69,15 @@ import org.robolectric.shadows.ShadowActivity import org.robolectric.shadows.ShadowIntent import java.lang.reflect.Field import java.lang.reflect.Method -import java.util.* +import java.util.Date +import java.util.Locale @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class MediaDetailFragmentUnitTests { - - private val REQUEST_CODE = 1001 - private val LAST_LOCATION = "last_location_while_uploading" + private val requestCode = 1001 + private val lastLocation = "last_location_while_uploading" private lateinit var fragment: MediaDetailFragment private lateinit var fragmentManager: FragmentManager private lateinit var layoutInflater: LayoutInflater @@ -80,7 +95,6 @@ class MediaDetailFragmentUnitTests { @Mock private lateinit var delete: Button - private var isDeleted = true @Mock @@ -138,21 +152,20 @@ class MediaDetailFragmentUnitTests { private lateinit var intent: Intent private lateinit var activity: SearchActivity - + @Mock private lateinit var mockContext: Context - + @Mock private lateinit var mockSharedPreferences: SharedPreferences - + @Mock - private lateinit var mockSharedPreferencesEditor: SharedPreferences.Editor + private lateinit var mockSharedPreferencesEditor: SharedPreferences.Editor private lateinit var binding: FragmentMediaDetailBinding @Before fun setUp() { - MockitoAnnotations.openMocks(this) context = ApplicationProvider.getApplicationContext() @@ -221,19 +234,19 @@ class MediaDetailFragmentUnitTests { @Test @Throws(Exception::class) fun testOnActivityResultLocationPickerActivity() { - fragment.onActivityResult(REQUEST_CODE, Activity.RESULT_CANCELED, intent) + fragment.onActivityResult(requestCode, Activity.RESULT_CANCELED, intent) } @Test @Throws(Exception::class) fun `test OnActivity Result Cancelled LocationPickerActivity`() { - fragment.onActivityResult(REQUEST_CODE, Activity.RESULT_CANCELED, intent) + fragment.onActivityResult(requestCode, Activity.RESULT_CANCELED, intent) } @Test @Throws(Exception::class) fun `test OnActivity Result Cancelled DescriptionEditActivity`() { - fragment.onActivityResult(REQUEST_CODE, Activity.RESULT_OK, intent) + fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent) } @Test @@ -254,7 +267,7 @@ class MediaDetailFragmentUnitTests { fun testOnUpdateCoordinatesClickedCurrentLocationNull() { `when`(media.coordinates).thenReturn(null) `when`(locationManager.lastLocation).thenReturn(null) - `when`(applicationKvStore.getString(LAST_LOCATION)).thenReturn("37.773972,-122.431297") + `when`(applicationKvStore.getString(lastLocation)).thenReturn("37.773972,-122.431297") fragment.onUpdateCoordinatesClicked() Mockito.verify(media, Mockito.times(1)).coordinates Mockito.verify(locationManager, Mockito.times(1)).lastLocation @@ -268,7 +281,7 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testOnUpdateCoordinatesClickedNotNullValue() { `when`(media.coordinates).thenReturn(LatLng(-0.000001, -0.999999, 0f)) - `when`(applicationKvStore.getString(LAST_LOCATION)).thenReturn("37.773972,-122.431297") + `when`(applicationKvStore.getString(lastLocation)).thenReturn("37.773972,-122.431297") fragment.onUpdateCoordinatesClicked() Mockito.verify(media, Mockito.times(3)).coordinates val shadowActivity: ShadowActivity = shadowOf(activity) @@ -282,7 +295,7 @@ class MediaDetailFragmentUnitTests { fun testOnUpdateCoordinatesClickedCurrentLocationNotNull() { `when`(media.coordinates).thenReturn(null) `when`(locationManager.lastLocation).thenReturn(LatLng(-0.000001, -0.999999, 0f)) - `when`(applicationKvStore.getString(LAST_LOCATION)).thenReturn("37.773972,-122.431297") + `when`(applicationKvStore.getString(lastLocation)).thenReturn("37.773972,-122.431297") fragment.onUpdateCoordinatesClicked() Mockito.verify(locationManager, Mockito.times(3)).lastLocation @@ -327,10 +340,11 @@ class MediaDetailFragmentUnitTests { @Test @Throws(Exception::class) fun testExtractDescription() { - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "extractDescription", - String::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "extractDescription", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "") } @@ -339,9 +353,10 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testGetDescription() { `when`(media.filename).thenReturn("") - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "getDescription" - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "getDescription", + ) method.isAccessible = true method.invoke(fragment) } @@ -352,7 +367,8 @@ class MediaDetailFragmentUnitTests { `when`(media.filename).thenReturn("") val method: Method = MediaDetailFragment::class.java.getDeclaredMethod("getDescriptions", String::class.java) method.isAccessible = true - val s = "=={{int:filedesc}}==\n" + + val s = + "=={{int:filedesc}}==\n" + "{{Information\n" + "|description={{en|1=Antique cash register in a cafe, Darjeeling}}\n" + "|date=2017-05-17 17:07:26\n" + @@ -373,7 +389,8 @@ class MediaDetailFragmentUnitTests { `when`(media.filename).thenReturn("") val method: Method = MediaDetailFragment::class.java.getDeclaredMethod("getDescriptions", String::class.java) method.isAccessible = true - val s = "=={{int:filedesc}}==\n" + + val s = + "=={{int:filedesc}}==\n" + "{{Information\n" + "|description={{en|1=[[:en:Fitzrovia Chapel|Fitzrovia Chapel]] ceiling
\n" + "{{On Wikidata|Q17549757}}}}\n" + @@ -396,7 +413,8 @@ class MediaDetailFragmentUnitTests { `when`(media.filename).thenReturn("") val method: Method = MediaDetailFragment::class.java.getDeclaredMethod("getDescriptions", String::class.java) method.isAccessible = true - val s = "=={{int:filedesc}}==\n" + + val s = + "=={{int:filedesc}}==\n" + "{{Information\n" + "|description={{en|1=[[:en:Fitzrovia Chapel|Fitzrovia Chapel]] ceiling
\n" + "}}{{Listed building England|1223496}}\n" + @@ -419,13 +437,18 @@ class MediaDetailFragmentUnitTests { `when`(media.filename).thenReturn("") val method: Method = MediaDetailFragment::class.java.getDeclaredMethod("getDescriptions", String::class.java) method.isAccessible = true - val s = "=={{int:filedesc}}==\n" + + val s = + "=={{int:filedesc}}==\n" + "{{Artwork\n" + " |artist = {{Creator:Filippo Peroni}} Restored by {{Creator:Adam Cuerden}}\n" + " |author = \n" + " |title = Ricchi giardini nel Palazzo di Monforte a Palermo\n" + - " |description = {{en|''Ricchi giardini nel Palazzo di Monforte a Palermo'', set design for ''I Vespri siciliani'' act 5 (undated).}} {{it|''Ricchi giardini nel Palazzo di Monforte a Palermo'', bozzetto per ''I Vespri siciliani'' atto 5 (s.d.).}}\n" + - " |date = {{between|1855|1878}} (Premiére of the opera and death of the artist, respectively)\n" + + " |description = {{en|''Ricchi giardini nel Palazzo di Monforte a Palermo''," + + " set design for ''I Vespri siciliani'' act 5 (undated).}} {{it|''Ricchi" + + " giardini nel Palazzo di Monforte a Palermo'', bozzetto per ''I Vespri" + + " siciliani'' atto 5 (s.d.).}}\n" + + " |date = {{between|1855|1878}} (Premiére of the opera and death of the artist, " + + "respectively)\n" + " |medium = {{technique|watercolor|and=tempera|and2=|over=paper}}\n" + " |dimensions = {{Size|unit=mm|height=210|width=270}}\n" + " |institution = {{Institution:Archivio Storico Ricordi}}\n" + @@ -438,17 +461,28 @@ class MediaDetailFragmentUnitTests { " |notes = \n" + " |accession number = ICON000132\n" + " |place of creation = \n" + - " |source = [https://www.archivioricordi.com/chi-siamo/glam-archivio-ricordi/#/ Archivio Storico Ricordi], [https://www.digitalarchivioricordi.com/it/works/display/108/Vespri_Siciliani__I Collezione Digitale Ricordi]\n" + + " |source = [https://www.archivioricordi.com/chi-siamo/glam-archivio-ricordi/#/" + + " Archivio Storico Ricordi], [https://www.digitalarchivioricordi.com/it/" + + "works/display/108/Vespri_Siciliani__I Collezione Digitale Ricordi]\n" + " |permission={{PermissionTicket|id=2022031410007974|user=Ruthven}} \n" + " |other_versions = \n" + - "* [[:File:Ricchi giardini nel Palazzo di Monforte a Palermo, bozzetto di Filippo Peroni per I Vespri siciliani (s.d.) - Archivio Storico Ricordi ICON000132 - Restoration.jpg]] - Restoration (JPEG)\n" + - "* [[:File:Ricchi giardini nel Palazzo di Monforte a Palermo, bozzetto di Filippo Peroni per I Vespri siciliani (s.d.) - Archivio Storico Ricordi ICON000132 - Restoration.png]] - Restoration (PNG)\n" + - "* [[:File:Ricchi giardini nel Palazzo di Monforte a Palermo, bozzetto di Filippo Peroni per I Vespri siciliani (s.d.) - Archivio Storico Ricordi ICON000132.jpg]] - Original (JPEG)\n" + + "* [[:File:Ricchi giardini nel Palazzo di Monforte a Palermo, bozzetto di" + + " Filippo Peroni per I Vespri siciliani (s.d.) - Archivio Storico Ricordi" + + " ICON000132 - Restoration.jpg]] - Restoration (JPEG)\n" + + "* [[:File:Ricchi giardini nel Palazzo di Monforte a Palermo, bozzetto di" + + " Filippo Peroni per I Vespri siciliani (s.d.) - Archivio Storico Ricordi" + + " ICON000132 - Restoration.png]] - Restoration (PNG)\n" + + "* [[:File:Ricchi giardini nel Palazzo di Monforte a Palermo, bozzetto di" + + " Filippo Peroni per I Vespri siciliani (s.d.) - Archivio Storico Ricordi" + + " ICON000132.jpg]] - Original (JPEG)\n" + " |references = \n" + " |wikidata = \n" + "}}" - val map = linkedMapOf("en" to "''Ricchi giardini nel Palazzo di Monforte a Palermo'', set design for ''I Vespri siciliani'' act 5 (undated).", - "it" to "''Ricchi giardini nel Palazzo di Monforte a Palermo'', bozzetto per ''I Vespri siciliani'' atto 5 (s.d.).") + val map = + linkedMapOf( + "en" to "''Ricchi giardini nel Palazzo di Monforte a Palermo'', set design for ''I Vespri siciliani'' act 5 (undated).", + "it" to "''Ricchi giardini nel Palazzo di Monforte a Palermo'', bozzetto per ''I Vespri siciliani'' atto 5 (s.d.).", + ) Assert.assertEquals(map, method.invoke(fragment, s)) } @@ -458,7 +492,8 @@ class MediaDetailFragmentUnitTests { `when`(media.filename).thenReturn("") val method: Method = MediaDetailFragment::class.java.getDeclaredMethod("getDescriptions", String::class.java) method.isAccessible = true - val s = "=={{int:filedesc}}==\n" + + val s = + "=={{int:filedesc}}==\n" + "{{Information\n" + "|Description ={{en|1=The interior of Sacred Heart RC Church, Wimbledon, London.}}\n" + "|Source ={{own}}\n" + @@ -475,9 +510,10 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testGetDescriptionList() { `when`(media.filename).thenReturn("") - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "getDescriptionList" - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "getDescriptionList", + ) method.isAccessible = true method.invoke(fragment) } @@ -486,9 +522,10 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testGetCaptions() { `when`(media.captions).thenReturn(mapOf(Pair("a", "b"))) - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "getCaptions" - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "getCaptions", + ) method.isAccessible = true method.invoke(fragment) } @@ -497,9 +534,10 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testGetCaptionsCaseEmpty() { `when`(media.captions).thenReturn(mapOf()) - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "getCaptions" - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "getCaptions", + ) method.isAccessible = true method.invoke(fragment) } @@ -512,9 +550,10 @@ class MediaDetailFragmentUnitTests { MediaDetailFragment::class.java.getDeclaredField("descriptionHtmlCode") field.isAccessible = true field.set(fragment, null) - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "setUpCaptionAndDescriptionLayout" - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "setUpCaptionAndDescriptionLayout", + ) method.isAccessible = true method.invoke(fragment) } @@ -557,10 +596,11 @@ class MediaDetailFragmentUnitTests { @Test @Throws(Exception::class) fun testPrettyCoordinatesCaseNull() { - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "prettyCoordinates", - Media::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "prettyCoordinates", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } @@ -569,10 +609,11 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testPrettyCoordinates() { `when`(media.coordinates).thenReturn(LatLng(-0.000001, -0.999999, 0f)) - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "prettyCoordinates", - Media::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "prettyCoordinates", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } @@ -581,10 +622,11 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testPrettyUploadedDateCaseNull() { `when`(media.dateUploaded).thenReturn(null) - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "prettyUploadedDate", - Media::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "prettyUploadedDate", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } @@ -593,10 +635,11 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testPrettyUploadedDateCaseNonNull() { `when`(media.dateUploaded).thenReturn(Date(2000, 1, 1)) - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "prettyUploadedDate", - Media::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "prettyUploadedDate", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } @@ -605,10 +648,11 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testPrettyLicenseCaseNull() { `when`(media.license).thenReturn(null) - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "prettyLicense", - Media::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "prettyLicense", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } @@ -617,10 +661,11 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testPrettyLicenseCaseNonNull() { `when`(media.license).thenReturn("licence") - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "prettyLicense", - Media::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "prettyLicense", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } @@ -628,10 +673,11 @@ class MediaDetailFragmentUnitTests { @Test @Throws(Exception::class) fun testPrettyDiscussion() { - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "prettyDiscussion", - String::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "prettyDiscussion", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "mock") } @@ -639,10 +685,11 @@ class MediaDetailFragmentUnitTests { @Test @Throws(Exception::class) fun testExtractCaptionDescription() { - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "extractCaptionDescription", - String::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "extractCaptionDescription", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "mock") } @@ -650,10 +697,11 @@ class MediaDetailFragmentUnitTests { @Test @Throws(Exception::class) fun testGetDescriptions() { - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "getDescriptions", - String::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "getDescriptions", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "mock") } @@ -662,10 +710,11 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testPrettyCaptionCaseEmpty() { `when`(media.captions).thenReturn(mapOf(Pair("a", ""))) - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "prettyCaption", - Media::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "prettyCaption", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } @@ -674,10 +723,11 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testPrettyCaptionCaseNonEmpty() { `when`(media.captions).thenReturn(mapOf(Pair("a", "b"))) - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "prettyCaption", - Media::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "prettyCaption", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } @@ -686,10 +736,11 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testPrettyCaption() { `when`(media.captions).thenReturn(mapOf()) - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "prettyCaption", - Media::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "prettyCaption", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } @@ -697,9 +748,10 @@ class MediaDetailFragmentUnitTests { @Test @Throws(Exception::class) fun testSetupImageView() { - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "setupImageView" - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "setupImageView", + ) method.isAccessible = true method.invoke(fragment) } @@ -707,10 +759,11 @@ class MediaDetailFragmentUnitTests { @Test @Throws(Exception::class) fun testOnDiscussionLoaded() { - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "onDiscussionLoaded", - String::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "onDiscussionLoaded", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "") } @@ -723,16 +776,25 @@ class MediaDetailFragmentUnitTests { `when`(media.imageUrl).thenReturn("test@example.com") `when`(spinner.selectedItemPosition).thenReturn(0) `when`(reasonListEnglishMappings?.get(spinner.selectedItemPosition)).thenReturn("TESTING") - `when`(applicationKvStore.getBoolean(String.format(MediaDetailFragment.NOMINATING_FOR_DELETION_MEDIA,media.imageUrl - ))).thenReturn(true) - doReturn(Single.just(true)).`when`(deleteHelper).makeDeletion(ArgumentMatchers.any(),ArgumentMatchers.any(), ArgumentMatchers.any()) + `when`( + applicationKvStore.getBoolean( + String.format( + MediaDetailFragment.NOMINATING_FOR_DELETION_MEDIA, + media.imageUrl, + ), + ), + ).thenReturn(true) + doReturn( + Single.just(true), + ).`when`(deleteHelper).makeDeletion(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()) doReturn(Single.just("")).`when`(reasonBuilder).getReason(ArgumentMatchers.any(), ArgumentMatchers.any()) - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "onDeleteClicked", - Spinner::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "onDeleteClicked", + Spinner::class.java, + ) method.isAccessible = true method.invoke(fragment, spinner) } @@ -769,9 +831,10 @@ class MediaDetailFragmentUnitTests { @Throws(Exception::class) fun testDisplayMediaDetails() { whenever(media.filename).thenReturn("File:Example.jpg") - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "displayMediaDetails" - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "displayMediaDetails", + ) method.isAccessible = true method.invoke(fragment) verify(media, times(4)).filename @@ -780,10 +843,11 @@ class MediaDetailFragmentUnitTests { @Test @Throws(Exception::class) fun testGotoCategoryEditor() { - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "gotoCategoryEditor", - String::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "gotoCategoryEditor", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "[[Category:Test]]") } @@ -791,14 +855,15 @@ class MediaDetailFragmentUnitTests { @Test @Throws(Exception::class) fun testOnMediaRefreshed() { - val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( - "onMediaRefreshed", - Media::class.java - ) + val method: Method = + MediaDetailFragment::class.java.getDeclaredMethod( + "onMediaRefreshed", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } - + @Test fun testOnImageBackgroundChangedWithDifferentColor() { val spyFragment = spy(fragment) @@ -808,11 +873,10 @@ class MediaDetailFragmentUnitTests { spyFragment.onImageBackgroundChanged(color) - verify(simpleDraweeView, times(1)).setBackgroundColor(color) + verify(simpleDraweeView, times(1)).setBackgroundColor(color) verify(mockSharedPreferencesEditor, times(1)).putInt(anyString(), anyInt()) } - @Test fun testOnImageBackgroundChangedWithSameColor() { val spyFragment = spy(fragment) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailPagerFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailPagerFragmentUnitTests.kt index 33cfc3c452..a6c925543f 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailPagerFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailPagerFragmentUnitTests.kt @@ -20,8 +20,8 @@ import com.nhaarman.mockitokotlin2.verify import fr.free.nrw.commons.Media import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.auth.SessionManager +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.databinding.FragmentMediaDetailPagerBinding import fr.free.nrw.commons.explore.SearchActivity import io.reactivex.android.plugins.RxAndroidPlugins @@ -77,9 +77,8 @@ class MediaDetailPagerFragmentUnitTests { fun setUp() { RxAndroidPlugins.setMainThreadSchedulerHandler { Schedulers.trampoline() } RxJavaPlugins.setNewThreadSchedulerHandler { Schedulers.trampoline() } - - MockitoAnnotations.openMocks(this) + MockitoAnnotations.openMocks(this) context = ApplicationProvider.getApplicationContext() @@ -111,7 +110,7 @@ class MediaDetailPagerFragmentUnitTests { doReturn(menuItem).`when`(menuItem).isEnabled = any() doReturn(menuItem).`when`(menuItem).isVisible = any() } - + @After fun tearDown() { RxAndroidPlugins.reset() @@ -152,10 +151,11 @@ class MediaDetailPagerFragmentUnitTests { @Test @Throws(Exception::class) fun testSetWallpaperCaseNull() { - val method: Method = MediaDetailPagerFragment::class.java.getDeclaredMethod( - "setWallpaper", - Media::class.java - ) + val method: Method = + MediaDetailPagerFragment::class.java.getDeclaredMethod( + "setWallpaper", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } @@ -165,22 +165,23 @@ class MediaDetailPagerFragmentUnitTests { fun testSetWallpaperCaseNonNull() { Shadows.shadowOf(Looper.getMainLooper()).idle() `when`(media.imageUrl).thenReturn("url") - val method: Method = MediaDetailPagerFragment::class.java.getDeclaredMethod( - "setWallpaper", - Media::class.java - ) + val method: Method = + MediaDetailPagerFragment::class.java.getDeclaredMethod( + "setWallpaper", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } - @Test @Throws(Exception::class) fun testSetAvatarCaseNull() { - val method: Method = MediaDetailPagerFragment::class.java.getDeclaredMethod( - "setAvatar", - Media::class.java - ) + val method: Method = + MediaDetailPagerFragment::class.java.getDeclaredMethod( + "setAvatar", + Media::class.java, + ) method.isAccessible = true method.invoke(fragment, media) } @@ -237,11 +238,12 @@ class MediaDetailPagerFragmentUnitTests { } private fun invokeHandleBackgroundColorMenuItems(getBitmap: Callable) { - val method: Method = MediaDetailPagerFragment::class.java.getDeclaredMethod( - "handleBackgroundColorMenuItems", - Callable::class.java, - Menu::class.java - ) + val method: Method = + MediaDetailPagerFragment::class.java.getDeclaredMethod( + "handleBackgroundColorMenuItems", + Callable::class.java, + Menu::class.java, + ) method.isAccessible = true method.invoke(fragment, getBitmap, menu) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/media/ZoomableActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/media/ZoomableActivityUnitTests.kt index 7543562cb6..848e0881aa 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/media/ZoomableActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/media/ZoomableActivityUnitTests.kt @@ -29,7 +29,6 @@ import java.lang.reflect.Field @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ZoomableActivityUnitTests { - private lateinit var context: Context private lateinit var activity: ZoomableActivity private lateinit var viewModelField: Field @@ -70,18 +69,18 @@ class ZoomableActivityUnitTests { * Test handleResult. */ @Test - fun testHandleResult(){ + fun testHandleResult() { val func = activity.javaClass.getDeclaredMethod("handleResult", Result::class.java) func.isAccessible = true func.invoke(activity, Result(CallbackStatus.SUCCESS, arrayListOf())) - func.invoke(activity, Result(CallbackStatus.SUCCESS, arrayListOf(image,image))) + func.invoke(activity, Result(CallbackStatus.SUCCESS, arrayListOf(image, image))) } /** * Test onLeftSwiped. */ @Test - fun testOnLeftSwiped(){ + fun testOnLeftSwiped() { val func = activity.javaClass.getDeclaredMethod("onLeftSwiped", Boolean::class.java) func.isAccessible = true func.invoke(activity, true) @@ -97,7 +96,7 @@ class ZoomableActivityUnitTests { * Test onRightSwiped. */ @Test - fun testOnRightSwiped(){ + fun testOnRightSwiped() { val func = activity.javaClass.getDeclaredMethod("onRightSwiped", Boolean::class.java) func.isAccessible = true func.invoke(activity, true) @@ -113,7 +112,7 @@ class ZoomableActivityUnitTests { * Test onUpSwiped. */ @Test - fun testOnUpSwiped(){ + fun testOnUpSwiped() { val func = activity.javaClass.getDeclaredMethod("onUpSwiped") func.isAccessible = true func.invoke(activity) @@ -123,7 +122,7 @@ class ZoomableActivityUnitTests { * Test onDownSwiped. */ @Test - fun testOnDownSwiped(){ + fun testOnDownSwiped() { val func = activity.javaClass.getDeclaredMethod("onDownSwiped") func.isAccessible = true func.invoke(activity) @@ -133,20 +132,19 @@ class ZoomableActivityUnitTests { * Test onBackPressed. */ @Test - fun testOnBackPressed(){ + fun testOnBackPressed() { val func = activity.javaClass.getDeclaredMethod("onBackPressed") func.isAccessible = true func.invoke(activity) } - /** * Test onDestroy. */ @Test - fun testOnDestroy(){ + fun testOnDestroy() { val func = activity.javaClass.getDeclaredMethod("onDestroy") func.isAccessible = true func.invoke(activity) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/media/zoomControllers/MultiPointerGestureDetectorUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/media/zoomControllers/MultiPointerGestureDetectorUnitTest.kt index d07acdd543..2f62589a74 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/media/zoomControllers/MultiPointerGestureDetectorUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/media/zoomControllers/MultiPointerGestureDetectorUnitTest.kt @@ -14,7 +14,6 @@ import org.powermock.reflect.Whitebox import java.lang.reflect.Method class MultiPointerGestureDetectorUnitTest { - private lateinit var detector: MultiPointerGestureDetector @Mock @@ -42,9 +41,10 @@ class MultiPointerGestureDetectorUnitTest { @Test @Throws(Exception::class) fun testSetAvatarCaseNull() { - val method: Method = MultiPointerGestureDetector::class.java.getDeclaredMethod( - "shouldStartGesture" - ) + val method: Method = + MultiPointerGestureDetector::class.java.getDeclaredMethod( + "shouldStartGesture", + ) method.isAccessible = true Assert.assertEquals(method.invoke(detector), true) } @@ -52,9 +52,10 @@ class MultiPointerGestureDetectorUnitTest { @Test @Throws(Exception::class) fun testStartGesture() { - val method: Method = MultiPointerGestureDetector::class.java.getDeclaredMethod( - "startGesture" - ) + val method: Method = + MultiPointerGestureDetector::class.java.getDeclaredMethod( + "startGesture", + ) method.isAccessible = true method.invoke(detector) verify(listener).onGestureBegin(detector) @@ -64,9 +65,10 @@ class MultiPointerGestureDetectorUnitTest { @Throws(Exception::class) fun testStopGesture() { Whitebox.setInternalState(detector, "mGestureInProgress", true) - val method: Method = MultiPointerGestureDetector::class.java.getDeclaredMethod( - "stopGesture" - ) + val method: Method = + MultiPointerGestureDetector::class.java.getDeclaredMethod( + "stopGesture", + ) method.isAccessible = true method.invoke(detector) verify(listener).onGestureEnd(detector) @@ -77,9 +79,11 @@ class MultiPointerGestureDetectorUnitTest { fun testUpdatePointersOnTap() { whenever(event.pointerCount).thenReturn(3) whenever(event.actionMasked).thenReturn(MotionEvent.ACTION_UP) - val method: Method = MultiPointerGestureDetector::class.java.getDeclaredMethod( - "updatePointersOnTap", MotionEvent::class.java - ) + val method: Method = + MultiPointerGestureDetector::class.java.getDeclaredMethod( + "updatePointersOnTap", + MotionEvent::class.java, + ) method.isAccessible = true method.invoke(detector, event) verify(event, times(2)).actionIndex @@ -172,5 +176,4 @@ class MultiPointerGestureDetectorUnitTest { whenever(event.actionMasked).thenReturn(MotionEvent.ACTION_CANCEL) Assert.assertEquals(detector.onTouchEvent(event), true) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/media/zoomControllers/TransformGestureDetectorUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/media/zoomControllers/TransformGestureDetectorUnitTest.kt index 3b1f8b352d..6252f6fb85 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/media/zoomControllers/TransformGestureDetectorUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/media/zoomControllers/TransformGestureDetectorUnitTest.kt @@ -14,7 +14,6 @@ import org.powermock.reflect.Whitebox import java.lang.reflect.Method class TransformGestureDetectorUnitTest { - private lateinit var detector: TransformGestureDetector @Mock @@ -155,7 +154,6 @@ class TransformGestureDetectorUnitTest { Assert.assertEquals(detector.rotation, 0f) } - @Test @Throws(Exception::class) fun testGetRotationCaseGreaterThan2() { @@ -177,11 +175,13 @@ class TransformGestureDetectorUnitTest { val array = FloatArray(2) array[0] = 0.0f array[1] = 1.0f - val method: Method = TransformGestureDetector::class.java.getDeclaredMethod( - "calcAverage", FloatArray::class.java, Int::class.java - ) + val method: Method = + TransformGestureDetector::class.java.getDeclaredMethod( + "calcAverage", + FloatArray::class.java, + Int::class.java, + ) method.isAccessible = true Assert.assertEquals(method.invoke(detector, array, 2), 0.5f) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/UserClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/UserClientTest.kt index b8728f485a..52c7953ec5 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/UserClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/UserClientTest.kt @@ -1,18 +1,21 @@ package fr.free.nrw.commons.mwapi +import fr.free.nrw.commons.utils.DateUtil +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResult +import fr.free.nrw.commons.wikidata.mwapi.UserInfo import io.reactivex.Observable import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test -import org.mockito.* -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResult -import fr.free.nrw.commons.wikidata.mwapi.UserInfo -import fr.free.nrw.commons.utils.DateUtil -import java.util.* +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import java.util.Date -class UserClientTest{ +class UserClientTest { @Mock internal var userInterface: UserInterface? = null @@ -33,8 +36,9 @@ class UserClientTest{ Mockito.`when`(mwQueryResult.userInfo()).thenReturn(userInfo) val mockResponse = Mockito.mock(MwQueryResponse::class.java) Mockito.`when`(mockResponse.query()).thenReturn(mwQueryResult) - Mockito.`when`(userInterface!!.getUserBlockInfo()) - .thenReturn(Observable.just(mockResponse)) + Mockito + .`when`(userInterface!!.getUserBlockInfo()) + .thenReturn(Observable.just(mockResponse)) val isBanned = userClient!!.isUserBlockedFromCommons().blockingGet() assertTrue(isBanned) @@ -51,8 +55,9 @@ class UserClientTest{ Mockito.`when`(mwQueryResult.userInfo()).thenReturn(userInfo) val mockResponse = Mockito.mock(MwQueryResponse::class.java) Mockito.`when`(mockResponse.query()).thenReturn(mwQueryResult) - Mockito.`when`(userInterface!!.getUserBlockInfo()) - .thenReturn(Observable.just(mockResponse)) + Mockito + .`when`(userInterface!!.getUserBlockInfo()) + .thenReturn(Observable.just(mockResponse)) val isBanned = userClient!!.isUserBlockedFromCommons().blockingGet() assertTrue(isBanned) @@ -66,11 +71,11 @@ class UserClientTest{ Mockito.`when`(mwQueryResult.userInfo()).thenReturn(userInfo) val mockResponse = Mockito.mock(MwQueryResponse::class.java) Mockito.`when`(mockResponse.query()).thenReturn(mwQueryResult) - Mockito.`when`(userInterface!!.getUserBlockInfo()) - .thenReturn(Observable.just(mockResponse)) + Mockito + .`when`(userInterface!!.getUserBlockInfo()) + .thenReturn(Observable.just(mockResponse)) val isBanned = userClient!!.isUserBlockedFromCommons().blockingGet() assertFalse(isBanned) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/navtab/MoreBottomSheetFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/navtab/MoreBottomSheetFragmentUnitTests.kt index a5faed37e5..68adb62957 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/navtab/MoreBottomSheetFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/navtab/MoreBottomSheetFragmentUnitTests.kt @@ -16,8 +16,8 @@ import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.actions.PageEditClient +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.feedback.model.Feedback import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.profile.ProfileActivity @@ -27,7 +27,10 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.* +import org.mockito.Mockito.anyString +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.powermock.reflect.Whitebox import org.robolectric.Robolectric @@ -40,12 +43,10 @@ import org.robolectric.shadows.ShadowAlertDialog import org.robolectric.shadows.ShadowDialog import java.lang.reflect.Method - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class MoreBottomSheetFragmentUnitTests { - private lateinit var fragment: MoreBottomSheetFragment private lateinit var view: View private lateinit var layoutInflater: LayoutInflater @@ -75,7 +76,7 @@ class MoreBottomSheetFragmentUnitTests { Whitebox.setInternalState(fragment, "pageEditClient", pageEditClient) `when`(store.getBoolean(CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED)).thenReturn( - true + true, ) layoutInflater = LayoutInflater.from(activity) @@ -197,8 +198,7 @@ class MoreBottomSheetFragmentUnitTests { Assert.assertEquals(startedIntent.`data`, Uri.parse("mailto:")) Assert.assertEquals( startedIntent.extras?.get(Intent.EXTRA_SUBJECT), - CommonsApplication.FEEDBACK_EMAIL_SUBJECT + CommonsApplication.FEEDBACK_EMAIL_SUBJECT, ) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/navtab/MoreBottomSheetLoggedOutFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/navtab/MoreBottomSheetLoggedOutFragmentUnitTests.kt index 3a9741f372..c2906b501e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/navtab/MoreBottomSheetLoggedOutFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/navtab/MoreBottomSheetLoggedOutFragmentUnitTests.kt @@ -5,6 +5,7 @@ import androidx.fragment.app.testing.FragmentScenario import androidx.fragment.app.testing.launchFragmentInContainer import androidx.lifecycle.Lifecycle import androidx.test.ext.junit.runners.AndroidJUnit4 +import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication import org.junit.Before import org.junit.Test @@ -12,7 +13,6 @@ import org.junit.runner.RunWith import org.robolectric.Shadows import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode -import fr.free.nrw.commons.R @RunWith(AndroidJUnit4::class) @Config(sdk = [21], application = TestCommonsApplication::class) @@ -22,12 +22,13 @@ class MoreBottomSheetLoggedOutFragmentUnitTests { @Before fun setUp() { - scenario = launchFragmentInContainer( - initialState = Lifecycle.State.RESUMED, - themeResId = R.style.LightAppTheme - ) { - MoreBottomSheetLoggedOutFragment() - } + scenario = + launchFragmentInContainer( + initialState = Lifecycle.State.RESUMED, + themeResId = R.style.LightAppTheme, + ) { + MoreBottomSheetLoggedOutFragment() + } } @Test @@ -57,4 +58,4 @@ class MoreBottomSheetLoggedOutFragmentUnitTests { Shadows.shadowOf(Looper.getMainLooper()).idle() scenario.onFragment { it.onLogoutClicked() } } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/nearby/AdvanceQueryFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/nearby/AdvanceQueryFragmentUnitTests.kt index e8bf0460b0..409d15080b 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/nearby/AdvanceQueryFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/AdvanceQueryFragmentUnitTests.kt @@ -34,7 +34,6 @@ import org.robolectric.annotation.LooperMode @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class AdvanceQueryFragmentUnitTests { - private lateinit var view: View private lateinit var activity: MainActivity private lateinit var layoutInflater: LayoutInflater @@ -105,7 +104,6 @@ class AdvanceQueryFragmentUnitTests { assertEquals(defaultQuery, etQuery.text.toString()) } - @Test fun `when no query is passed in fragment argument, nothing is visible in text field`() { `when`(bundle.getString("query")).thenReturn("") diff --git a/app/src/test/kotlin/fr/free/nrw/commons/nearby/CheckboxTriStatesTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/nearby/CheckBoxTriStatesTest.kt similarity index 97% rename from app/src/test/kotlin/fr/free/nrw/commons/nearby/CheckboxTriStatesTest.kt rename to app/src/test/kotlin/fr/free/nrw/commons/nearby/CheckBoxTriStatesTest.kt index fe5d352f32..b2177b1d2d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/nearby/CheckboxTriStatesTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/CheckBoxTriStatesTest.kt @@ -21,6 +21,7 @@ import org.robolectric.annotation.Config class CheckBoxTriStatesTest { @Mock internal lateinit var callback: CheckBoxTriStates.Callback + @Mock internal lateinit var onCheckChangeListener: CompoundButton.OnCheckedChangeListener private lateinit var checkBoxTriStates: CheckBoxTriStates @@ -52,7 +53,7 @@ class CheckBoxTriStatesTest { */ @Test fun testSetStateWhenDiffState() { - NearbyController.currentLocation = LatLng(0.0,0.0,0.0f) + NearbyController.currentLocation = LatLng(0.0, 0.0, 0.0f) checkBoxTriStates.state = CHECKED checkBoxTriStates.setState(UNCHECKED) verify(callback).filterByMarkerType(null, UNCHECKED, false, true) @@ -68,4 +69,4 @@ class CheckBoxTriStatesTest { checkBoxTriStates.setState(UNCHECKED) verifyNoInteractions(callback) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/nearby/CommonPlaceClickActionsUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/nearby/CommonPlaceClickActionsUnitTest.kt index 7d0cc5eed1..3cfbf9d134 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/nearby/CommonPlaceClickActionsUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/CommonPlaceClickActionsUnitTest.kt @@ -22,7 +22,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class CommonPlaceClickActionsUnitTest { - private lateinit var commonPlaceClickActions: CommonPlaceClickActions @Mock @@ -65,10 +64,11 @@ class CommonPlaceClickActionsUnitTest { @Test @Throws(Exception::class) fun testStoreSharedPrefs() { - val method: Method = CommonPlaceClickActions::class.java.getDeclaredMethod( - "storeSharedPrefs", - Place::class.java - ) + val method: Method = + CommonPlaceClickActions::class.java.getDeclaredMethod( + "storeSharedPrefs", + Place::class.java, + ) method.isAccessible = true method.invoke(commonPlaceClickActions, place) } @@ -76,10 +76,11 @@ class CommonPlaceClickActionsUnitTest { @Test @Throws(Exception::class) fun testOpenWebView() { - val method: Method = CommonPlaceClickActions::class.java.getDeclaredMethod( - "openWebView", - Uri::class.java - ) + val method: Method = + CommonPlaceClickActions::class.java.getDeclaredMethod( + "openWebView", + Uri::class.java, + ) method.isAccessible = true Assert.assertEquals(method.invoke(commonPlaceClickActions, uri), true) } @@ -87,9 +88,10 @@ class CommonPlaceClickActionsUnitTest { @Test @Throws(Exception::class) fun testShowLoginDialog() { - val method: Method = CommonPlaceClickActions::class.java.getDeclaredMethod( - "showLoginDialog" - ) + val method: Method = + CommonPlaceClickActions::class.java.getDeclaredMethod( + "showLoginDialog", + ) method.isAccessible = true method.invoke(commonPlaceClickActions) } @@ -97,11 +99,11 @@ class CommonPlaceClickActionsUnitTest { @Test @Throws(Exception::class) fun testSetPositiveButton() { - val method: Method = CommonPlaceClickActions::class.java.getDeclaredMethod( - "setPositiveButton" - ) + val method: Method = + CommonPlaceClickActions::class.java.getDeclaredMethod( + "setPositiveButton", + ) method.isAccessible = true method.invoke(commonPlaceClickActions) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/nearby/LabelTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/nearby/LabelTest.kt index e66765ef61..446bad2cb3 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/nearby/LabelTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/LabelTest.kt @@ -1,7 +1,6 @@ package fr.free.nrw.commons.nearby import fr.free.nrw.commons.R -import fr.free.nrw.commons.R.* import org.junit.Before import org.junit.Test @@ -33,5 +32,4 @@ class LabelTest { var nullLabel: Label = Label.fromText("a random text not exist in label texts") assert(nullLabel.icon.equals(R.drawable.round_icon_unknown)) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyControllerTest.kt index 5f0c264f93..60fabc8571 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyControllerTest.kt @@ -22,13 +22,13 @@ import org.powermock.reflect.Whitebox import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode -import java.util.* +import java.util.Collections +import java.util.Locale @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class NearbyControllerTest { - @Mock private lateinit var place: Place @@ -47,7 +47,6 @@ class NearbyControllerTest { @Mock private lateinit var currentLatLng: LatLng - private lateinit var context: Context private var customQuery: String = "test" private lateinit var nearbyController: NearbyController @@ -69,7 +68,7 @@ class NearbyControllerTest { searchLatLong, false, true, - customQuery + customQuery, ) nearbyController.loadAttractionsFromLocation( currentLatLng, @@ -79,13 +78,13 @@ class NearbyControllerTest { false, true, false, - customQuery + customQuery, ) Mockito.verify(nearbyPlaces).radiusExpander( eq(searchLatLong), any(String::class.java), eq(false), - eq(customQuery) + eq(customQuery), ) } @@ -98,13 +97,13 @@ class NearbyControllerTest { searchLatLong, false, true, - null + null, ) Mockito.verify(nearbyPlaces).radiusExpander( eq(searchLatLong), any(String::class.java), eq(false), - eq(null) + eq(null), ) } @@ -116,50 +115,56 @@ class NearbyControllerTest { null, false, true, - customQuery - ), null + customQuery, + ), + null, ) } @Test fun testLoadAttractionsFromLocationCase1() { - val place1 = Place( - "en", - "placeName", - Label.FOREST, - "placeDescription", - LatLng(0.0, 0.0, 1.0F), - "placeCategory", - Sitelinks.Builder().build(), - "picName", - false, - "entityID" - ) - val place2 = Place( - "en", - "placeName", - Label.FOREST, - "placeDescription", - LatLng(-40.69, -74.04, 1.0F), - "placeCategory", - Sitelinks.Builder().build(), - "picName", - false, - "entityID" - ) + val place1 = + Place( + "en", + "placeName", + Label.FOREST, + "placeDescription", + LatLng(0.0, 0.0, 1.0F), + "placeCategory", + Sitelinks.Builder().build(), + "picName", + false, + "entityID", + ) + val place2 = + Place( + "en", + "placeName", + Label.FOREST, + "placeDescription", + LatLng(-40.69, -74.04, 1.0F), + "placeCategory", + Sitelinks.Builder().build(), + "picName", + false, + "entityID", + ) `when`( nearbyPlaces.radiusExpander( - searchLatLong, Locale.getDefault().language, false, - customQuery - ) + searchLatLong, + Locale.getDefault().language, + false, + customQuery, + ), ).thenReturn(mutableListOf(place1, place2)) - val result = nearbyController.loadAttractionsFromLocation( - currentLatLng, - searchLatLong, - false, - true, - customQuery - ) + val result = + nearbyController.loadAttractionsFromLocation( + currentLatLng, + searchLatLong, + false, + true, + customQuery, + ) nearbyController.loadAttractionsFromLocation( currentLatLng, screenTopRight, @@ -168,7 +173,7 @@ class NearbyControllerTest { false, true, false, - customQuery + customQuery, ) assertEquals(result.currentLatLng, currentLatLng) assertEquals(result.searchLatLng, searchLatLong) @@ -176,86 +181,96 @@ class NearbyControllerTest { @Test fun testLoadAttractionsFromLocationCase2() { - val place1 = Place( - "en", - "placeName", - Label.FOREST, - "placeDescription", - LatLng(0.0, 0.0, 1.0F), - "placeCategory", - Sitelinks.Builder().build(), - "picName", - false, - "entityID" - ) - val place2 = Place( - "en", - "placeName", - Label.FOREST, - "placeDescription", - LatLng(40.69, -74.04, 1.0F), - "placeCategory", - Sitelinks.Builder().build(), - "picName", - false, - "entityID" - ) + val place1 = + Place( + "en", + "placeName", + Label.FOREST, + "placeDescription", + LatLng(0.0, 0.0, 1.0F), + "placeCategory", + Sitelinks.Builder().build(), + "picName", + false, + "entityID", + ) + val place2 = + Place( + "en", + "placeName", + Label.FOREST, + "placeDescription", + LatLng(40.69, -74.04, 1.0F), + "placeCategory", + Sitelinks.Builder().build(), + "picName", + false, + "entityID", + ) `when`( nearbyPlaces.radiusExpander( - searchLatLong, Locale.getDefault().language, false, - customQuery - ) + searchLatLong, + Locale.getDefault().language, + false, + customQuery, + ), ).thenReturn(mutableListOf(place1, place2)) - val result = nearbyController.loadAttractionsFromLocation( - currentLatLng, - searchLatLong, - false, - true, - customQuery - ) + val result = + nearbyController.loadAttractionsFromLocation( + currentLatLng, + searchLatLong, + false, + true, + customQuery, + ) assertEquals(result.currentLatLng, currentLatLng) assertEquals(result.searchLatLng, searchLatLong) } @Test fun testLoadAttractionsFromLocationCase3() { - val place1 = Place( - "en", - "placeName", - Label.FOREST, - "placeDescription", - LatLng(0.0, 0.0, 1.0F), - "placeCategory", - Sitelinks.Builder().build(), - "picName", - false, - "entityID" - ) - val place2 = Place( - "en", - "placeName", - Label.FOREST, - "placeDescription", - LatLng(40.69, 74.04, 1.0F), - "placeCategory", - Sitelinks.Builder().build(), - "picName", - false, - "entityID" - ) + val place1 = + Place( + "en", + "placeName", + Label.FOREST, + "placeDescription", + LatLng(0.0, 0.0, 1.0F), + "placeCategory", + Sitelinks.Builder().build(), + "picName", + false, + "entityID", + ) + val place2 = + Place( + "en", + "placeName", + Label.FOREST, + "placeDescription", + LatLng(40.69, 74.04, 1.0F), + "placeCategory", + Sitelinks.Builder().build(), + "picName", + false, + "entityID", + ) `when`( nearbyPlaces.radiusExpander( - searchLatLong, Locale.getDefault().language, false, - customQuery - ) + searchLatLong, + Locale.getDefault().language, + false, + customQuery, + ), ).thenReturn(mutableListOf(place1, place2)) - val result = nearbyController.loadAttractionsFromLocation( - currentLatLng, - searchLatLong, - false, - true, - customQuery - ) + val result = + nearbyController.loadAttractionsFromLocation( + currentLatLng, + searchLatLong, + false, + true, + customQuery, + ) assertEquals(result.currentLatLng, currentLatLng) assertEquals(result.searchLatLng, searchLatLong) } @@ -265,108 +280,117 @@ class NearbyControllerTest { assertEquals( loadAttractionsFromLocationToBaseMarkerOptions( currentLatLng, - null - ), listOf() + null, + ), + listOf(), ) } @Test fun testLoadAttractionsFromLocationToBaseMarkerOptionsCaseIsMonument() { - place = Place( - "en", - "placeName", - Label.FOREST, - "placeDescription", - currentLatLng, - "placeCategory", - Sitelinks.Builder().build(), - "picName", - false, - "entityID" - ) + place = + Place( + "en", + "placeName", + Label.FOREST, + "placeDescription", + currentLatLng, + "placeCategory", + Sitelinks.Builder().build(), + "picName", + false, + "entityID", + ) place.isMonument = true `when`(currentLatLng.latitude).thenReturn(0.0) `when`(currentLatLng.longitude).thenReturn(0.0) assertEquals( loadAttractionsFromLocationToBaseMarkerOptions( currentLatLng, - listOf(place) - )[0].place, place + listOf(place), + )[0].place, + place, ) } @Test fun testLoadAttractionsFromLocationToBaseMarkerOptionsCaseIsNotMonumentPicNotEmpty() { - place = Place( - "en", - "placeName", - Label.FOREST, - "placeDescription", - currentLatLng, - "placeCategory", - Sitelinks.Builder().build(), - "picName", - false, - "entityID" - ) + place = + Place( + "en", + "placeName", + Label.FOREST, + "placeDescription", + currentLatLng, + "placeCategory", + Sitelinks.Builder().build(), + "picName", + false, + "entityID", + ) place.isMonument = false `when`(currentLatLng.latitude).thenReturn(0.0) `when`(currentLatLng.longitude).thenReturn(0.0) assertEquals( loadAttractionsFromLocationToBaseMarkerOptions( currentLatLng, - listOf(place) - )[0].place, place + listOf(place), + )[0].place, + place, ) } @Test fun testLoadAttractionsFromLocationToBaseMarkerOptionsCaseIsNotMonumentPicEmptyPlaceDoesNotExists() { - place = Place( - "en", - "placeName", - Label.FOREST, - "placeDescription", - currentLatLng, - "placeCategory", - Sitelinks.Builder().build(), - "", - false, - "entityID" - ) + place = + Place( + "en", + "placeName", + Label.FOREST, + "placeDescription", + currentLatLng, + "placeCategory", + Sitelinks.Builder().build(), + "", + false, + "entityID", + ) place.isMonument = false `when`(currentLatLng.latitude).thenReturn(0.0) `when`(currentLatLng.longitude).thenReturn(0.0) assertEquals( loadAttractionsFromLocationToBaseMarkerOptions( currentLatLng, - listOf(place) - )[0].place, place + listOf(place), + )[0].place, + place, ) } @Test fun testLoadAttractionsFromLocationToBaseMarkerOptionsCaseIsNotMonumentPicEmptyPlaceExists() { - place = Place( - "en", - "placeName", - Label.FOREST, - "placeDescription", - currentLatLng, - "placeCategory", - Sitelinks.Builder().build(), - "", - true, - "entityID" - ) + place = + Place( + "en", + "placeName", + Label.FOREST, + "placeDescription", + currentLatLng, + "placeCategory", + Sitelinks.Builder().build(), + "", + true, + "entityID", + ) place.isMonument = false `when`(currentLatLng.latitude).thenReturn(0.0) `when`(currentLatLng.longitude).thenReturn(0.0) assertEquals( loadAttractionsFromLocationToBaseMarkerOptions( currentLatLng, - listOf(place) - )[0].place, place + listOf(place), + )[0].place, + place, ) } @@ -378,7 +402,7 @@ class NearbyControllerTest { Whitebox.setInternalState( NearbyController::class.java, "markerLabelList", - list + list, ) updateMarkerLabelListBookmark(place, false) assertEquals(list[0].isBookmarked, false) @@ -393,7 +417,7 @@ class NearbyControllerTest { Whitebox.setInternalState( NearbyController::class.java, "markerLabelList", - list + list, ) updateMarkerLabelListBookmark(place, true) assertEquals(list[0].isBookmarked, true) @@ -401,4 +425,4 @@ class NearbyControllerTest { } fun any(type: Class): T = Mockito.any(type) -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapterUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapterUnitTests.kt index 961590d1c8..7432cdf45a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapterUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapterUnitTests.kt @@ -16,13 +16,11 @@ import org.mockito.MockitoAnnotations import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode -import java.util.* @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class NearbyFilterSearchRecyclerViewAdapterUnitTests { - private lateinit var context: Context private lateinit var adapter: NearbyFilterSearchRecyclerViewAdapter @@ -101,5 +99,4 @@ class NearbyFilterSearchRecyclerViewAdapterUnitTests { fun testSetRecyclerViewAdapterAllSelected() { adapter.setRecyclerViewAdapterAllSelected() } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyParentFragmentPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyParentFragmentPresenterTest.kt index cb887d7b4c..d112ba5079 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyParentFragmentPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyParentFragmentPresenterTest.kt @@ -1,6 +1,8 @@ package fr.free.nrw.commons.nearby -import com.nhaarman.mockitokotlin2.* +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao import fr.free.nrw.commons.location.LatLng import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType @@ -9,7 +11,6 @@ import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.mockito.ArgumentMatchers import org.mockito.Mock @@ -18,13 +19,12 @@ import org.mockito.Mockito.any import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.verifyNoInteractions import org.mockito.MockitoAnnotations -import java.util.* +import java.util.Collections /** * The unit test class for NearbyParentFragmentPresenter */ class NearbyParentFragmentPresenterTest { - @Mock internal lateinit var nearbyParentFragmentView: NearbyParentFragmentContract.View @@ -54,7 +54,6 @@ class NearbyParentFragmentPresenterTest { MockitoAnnotations.openMocks(this) nearbyPresenter = NearbyParentFragmentPresenter(bookmarkLocationsDao) nearbyPresenter.attachView(nearbyParentFragmentView) - } /** @@ -67,7 +66,7 @@ class NearbyParentFragmentPresenterTest { expectMapAndListUpdate() whenever(nearbyParentFragmentView.lastMapFocus).thenReturn(LatLng(2.0, 1.0, 0.0F)) nearbyPresenter.updateMapAndList(LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED) - verify(nearbyParentFragmentView).disableFABRecenter(); + verify(nearbyParentFragmentView).disableFABRecenter() verify(nearbyParentFragmentView).`setProgressBarVisibility`(true) assertTrue(null == nearbyParentFragmentView.mapCenter) verify(nearbyParentFragmentView).populatePlaces(null) @@ -134,13 +133,14 @@ class NearbyParentFragmentPresenterTest { * Test updateMapAndList method updates parent fragment view with latest location of user * at significant location change */ - @Test + @Test fun testPlacesPopulatedForLatestLocationWhenLocationSignificantlyChanged() { expectMapAndListUpdate() - whenever(nearbyParentFragmentView.mapCenter).thenReturn(LatLng(2.0, 1.0, 0.0F)); + whenever(nearbyParentFragmentView.mapCenter).thenReturn(LatLng(2.0, 1.0, 0.0F)) nearbyPresenter.updateMapAndList(LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED) updateMapSignificantly() } + /** * Test updateMapAndList method updates parent fragment view with latest location of user * at map is updated location change type @@ -148,7 +148,7 @@ class NearbyParentFragmentPresenterTest { @Test fun testPlacesPopulatedForLatestLocationWhenLocationMapUpdated() { expectMapAndListUpdate() - whenever(nearbyParentFragmentView.mapCenter).thenReturn(LatLng(2.0, 1.0, 0.0F)); + whenever(nearbyParentFragmentView.mapCenter).thenReturn(LatLng(2.0, 1.0, 0.0F)) nearbyPresenter.updateMapAndList(LocationChangeType.MAP_UPDATED) updateMapSignificantly() } @@ -167,7 +167,7 @@ class NearbyParentFragmentPresenterTest { fun testPlacesPopulatedForCameraTargetLocationWhenSearchCustomArea() { expectMapAndListUpdate() whenever(nearbyParentFragmentView.getCameraTarget()).thenReturn(LatLng(2.0, 1.0, 0.0F)) - whenever(nearbyParentFragmentView.mapCenter).thenReturn(LatLng(2.0, 1.0, 0.0F)); + whenever(nearbyParentFragmentView.mapCenter).thenReturn(LatLng(2.0, 1.0, 0.0F)) whenever(nearbyParentFragmentView.mapFocus).thenReturn(LatLng(2.0, 1.0, 0.0F)) nearbyPresenter.updateMapAndList(LocationChangeType.SEARCH_CUSTOM_AREA) verify(nearbyParentFragmentView).disableFABRecenter() @@ -183,7 +183,7 @@ class NearbyParentFragmentPresenterTest { fun testUserTrackedWhenCurrentLocationMarkerVisible() { expectMapAndListUpdate() whenever(nearbyParentFragmentView.lastMapFocus).thenReturn(LatLng(2.0, 1.0, 0.0F)) - whenever(nearbyParentFragmentView.mapCenter).thenReturn(null); + whenever(nearbyParentFragmentView.mapCenter).thenReturn(null) nearbyPresenter.updateMapAndList(LocationChangeType.LOCATION_SLIGHTLY_CHANGED) verify(nearbyParentFragmentView).getLastMapFocus() verify(nearbyParentFragmentView).recenterMap(nearbyParentFragmentView.lastMapFocus) @@ -198,7 +198,7 @@ class NearbyParentFragmentPresenterTest { expectMapAndListUpdate() verify(nearbyParentFragmentView).enableFABRecenter() whenever(nearbyParentFragmentView.lastMapFocus).thenReturn(LatLng(2.0, 1.0, 0.0F)) - whenever(nearbyParentFragmentView.mapCenter).thenReturn(null); + whenever(nearbyParentFragmentView.mapCenter).thenReturn(null) nearbyPresenter.updateMapAndList(LocationChangeType.LOCATION_SLIGHTLY_CHANGED) verify(nearbyParentFragmentView).isNetworkConnectionEstablished() verify(nearbyParentFragmentView).getLastMapFocus() @@ -248,8 +248,8 @@ class NearbyParentFragmentPresenterTest { verify(nearbyParentFragmentView).filterMarkersByLabels( ArgumentMatchers.anyList(), ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyBoolean() - ); + ArgumentMatchers.anyBoolean(), + ) verify(nearbyParentFragmentView).setRecyclerViewAdapterAllSelected() verifyNoMoreInteractions(nearbyParentFragmentView) } @@ -263,8 +263,8 @@ class NearbyParentFragmentPresenterTest { verify(nearbyParentFragmentView).filterMarkersByLabels( any(), anyBoolean(), - anyBoolean() - ); + anyBoolean(), + ) verifyNoMoreInteractions(nearbyParentFragmentView) } @@ -294,9 +294,9 @@ class NearbyParentFragmentPresenterTest { */ @Test fun testSearchCloseToCurrentLocationWhenFar() { - whenever(nearbyParentFragmentView.lastMapFocus).thenReturn(LatLng(2.0, 1.0, 0.0F)); + whenever(nearbyParentFragmentView.lastMapFocus).thenReturn(LatLng(2.0, 1.0, 0.0F)) whenever(nearbyParentFragmentView.mapFocus).thenReturn(LatLng(2.0, 1.0, 0.0F)) - //111.19 km real distance, return false if 148306.444306 > currentLocationSearchRadius + // 111.19 km real distance, return false if 148306.444306 > currentLocationSearchRadius NearbyController.currentLocationSearchRadius = 148306.0 val isClose = nearbyPresenter?.searchCloseToCurrentLocation() assertFalse(isClose!!.equals(false)) @@ -308,7 +308,7 @@ class NearbyParentFragmentPresenterTest { @Test fun testSearchCloseToCurrentLocationWhenClose() { whenever(nearbyParentFragmentView.getCameraTarget()).thenReturn(LatLng(2.0, 1.0, 0.0F)) - //111.19 km real distance, return false if 148253.333 > currentLocationSearchRadius + // 111.19 km real distance, return false if 148253.333 > currentLocationSearchRadius NearbyController.currentLocationSearchRadius = 148307.0 val isClose = nearbyPresenter?.searchCloseToCurrentLocation() assertTrue(isClose!!) @@ -361,14 +361,14 @@ class NearbyParentFragmentPresenterTest { @Test fun testMarkerUnselected() { nearbyPresenter.markerUnselected() - verify(nearbyParentFragmentView).hideBottomSheet(); + verify(nearbyParentFragmentView).hideBottomSheet() } @Test fun testOnWikidataEditSuccessful() { nearbyPresenter.onWikidataEditSuccessful() expectMapAndListUpdate() - whenever(nearbyParentFragmentView.mapCenter).thenReturn(LatLng(2.0, 1.0, 0.0F)); + whenever(nearbyParentFragmentView.mapCenter).thenReturn(LatLng(2.0, 1.0, 0.0F)) nearbyPresenter.updateMapAndList(LocationChangeType.MAP_UPDATED) updateMapSignificantly() } @@ -376,8 +376,8 @@ class NearbyParentFragmentPresenterTest { @Test fun testOnLocationChangedSignificantly() { expectMapAndListUpdate() - whenever(nearbyParentFragmentView.mapCenter).thenReturn(LatLng(2.0, 1.0, 0.0F)); - latestLocation=LatLng(2.0, 1.0, 0.0F) + whenever(nearbyParentFragmentView.mapCenter).thenReturn(LatLng(2.0, 1.0, 0.0F)) + latestLocation = LatLng(2.0, 1.0, 0.0F) nearbyPresenter.onLocationChangedSignificantly(latestLocation) updateMapSignificantly() } @@ -461,6 +461,5 @@ class NearbyParentFragmentPresenterTest { Mockito.verify(nearbyParentFragmentView).updateMapMarkers(any()) Mockito.verify(nearbyParentFragmentView).setProgressBarVisibility(false) Mockito.verify(nearbyParentFragmentView).updateListFragment(nearbyPlacesInfo.placeList) - } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyParentFragmentUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyParentFragmentUnitTest.kt index aeef0c1048..6584550b06 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyParentFragmentUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyParentFragmentUnitTest.kt @@ -1,6 +1,5 @@ package fr.free.nrw.commons.nearby -import android.app.AlertDialog import android.content.Context import android.content.res.Configuration import android.net.Uri @@ -24,9 +23,9 @@ import fr.free.nrw.commons.BaseMarker import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao import fr.free.nrw.commons.contributions.MainActivity +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.location.LocationServiceManager import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType @@ -39,7 +38,9 @@ import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.* +import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.osmdroid.util.GeoPoint import org.powermock.reflect.Whitebox @@ -49,14 +50,12 @@ import org.robolectric.Shadows import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import org.robolectric.shadows.ShadowActivity -import org.robolectric.shadows.ShadowAlertDialog import java.lang.reflect.Method @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class NearbyParentFragmentUnitTest { - @Mock private lateinit var mapView: org.osmdroid.views.MapView @@ -165,51 +164,59 @@ class NearbyParentFragmentUnitTest { Whitebox.setInternalState( fragment, "nearbyParentFragmentInstanceReadyCallback", - nearbyParentFragmentInstanceReadyCallback + nearbyParentFragmentInstanceReadyCallback, ) } @Test @Ignore @Throws(Exception::class) fun `Start map without gps test when last location known`() { - val method: Method = NearbyParentFragment::class.java.getDeclaredMethod( - "startMapWithCondition", - String::class.java - ) + val method: Method = + NearbyParentFragment::class.java.getDeclaredMethod( + "startMapWithCondition", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "Without GPS") verify(applicationKvStore, times(1)).getString("LastLocation") verify(presenter, times(1)).onMapReady() - val position = GeoPoint(51.50550, - -0.07520) + val position = + GeoPoint( + 51.50550, + -0.07520, + ) verify(mapView, times(1)) - .controller.animateTo(position) + .controller + .animateTo(position) } @Test @Ignore @Throws(Exception::class) fun `Start map without gps test when last location unknown`() { `when`(applicationKvStore.getString("LastLocation")).thenReturn("23.76,56.876") - val method: Method = NearbyParentFragment::class.java.getDeclaredMethod( - "startMapWithCondition", - String::class.java - ) + val method: Method = + NearbyParentFragment::class.java.getDeclaredMethod( + "startMapWithCondition", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "Without GPS") verify(applicationKvStore, times(2)).getString("LastLocation") verify(presenter, times(1)).onMapReady() - val position = GeoPoint(23.76,56.876) + val position = GeoPoint(23.76, 56.876) verify(mapView, times(1)) - .controller.animateTo(position) + .controller + .animateTo(position) } @Test @Ignore @Throws(Exception::class) fun `Start map without location permission test when last location known`() { - val method: Method = NearbyParentFragment::class.java.getDeclaredMethod( - "startMapWithCondition", - String::class.java - ) + val method: Method = + NearbyParentFragment::class.java.getDeclaredMethod( + "startMapWithCondition", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "Without Permission") verify(applicationKvStore, times(1)).getString("LastLocation") @@ -222,10 +229,11 @@ class NearbyParentFragmentUnitTest { @Throws(Exception::class) fun `Start map without location permission test when last location unknown`() { `when`(applicationKvStore.getString("LastLocation")).thenReturn("23.76,56.876") - val method: Method = NearbyParentFragment::class.java.getDeclaredMethod( - "startMapWithCondition", - String::class.java - ) + val method: Method = + NearbyParentFragment::class.java.getDeclaredMethod( + "startMapWithCondition", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "Without Permission") verify(applicationKvStore, times(2)).getString("LastLocation") @@ -256,20 +264,22 @@ class NearbyParentFragmentUnitTest { @Throws(Exception::class) fun testSetNearbyParentFragmentInstanceReadyCallback() { fragment.setNearbyParentFragmentInstanceReadyCallback( - nearbyParentFragmentInstanceReadyCallback + nearbyParentFragmentInstanceReadyCallback, ) Assert.assertEquals( nearbyParentFragmentInstanceReadyCallback, - nearbyParentFragmentInstanceReadyCallback + nearbyParentFragmentInstanceReadyCallback, ) } @Test @Ignore @Throws(Exception::class) fun testSetUserVisibleHintCaseFalse() { - val method: Method = NearbyParentFragment::class.java.getDeclaredMethod( - "setUserVisibleHint", Boolean::class.java - ) + val method: Method = + NearbyParentFragment::class.java.getDeclaredMethod( + "setUserVisibleHint", + Boolean::class.java, + ) method.isAccessible = true method.invoke(fragment, false) verify(bottomSheetBehavior, times(2)).state = BottomSheetBehavior.STATE_HIDDEN @@ -279,9 +289,11 @@ class NearbyParentFragmentUnitTest { @Throws(Exception::class) fun testSetUserVisibleHintCaseTrue() { Whitebox.setInternalState(fragment, "mState", 4) - val method: Method = NearbyParentFragment::class.java.getDeclaredMethod( - "setUserVisibleHint", Boolean::class.java - ) + val method: Method = + NearbyParentFragment::class.java.getDeclaredMethod( + "setUserVisibleHint", + Boolean::class.java, + ) method.isAccessible = true method.invoke(fragment, true) } @@ -360,5 +372,4 @@ class NearbyParentFragmentUnitTest { val shadowActivity: ShadowActivity = Shadows.shadowOf(activity) Assert.assertEquals(shadowActivity.nextStartedActivityForResult, null) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyPlacesTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyPlacesTest.kt index 13fa2eac38..f83aab4534 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyPlacesTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyPlacesTest.kt @@ -33,7 +33,7 @@ class NearbyPlacesTest { eq(currentLatLong), eq("test"), any(), - eq("test") + eq("test"), ) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationActivityUnitTests.kt index 08d08772ab..0ff92af2ad 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationActivityUnitTests.kt @@ -32,7 +32,6 @@ import java.lang.reflect.Method @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class, shadows = [ShadowActionBar::class]) class NotificationActivityUnitTests { - @Mock private lateinit var activity: NotificationActivity @@ -53,7 +52,6 @@ class NotificationActivityUnitTests { @Before fun setUp() { - MockitoAnnotations.initMocks(this) networkUtils = mock(NetworkUtils::class.java) @@ -77,7 +75,6 @@ class NotificationActivityUnitTests { NotificationActivity::class.java.getDeclaredField("notificationMenuItem") notificationMenuItem.isAccessible = true notificationMenuItem.set(activity, menuItemWithId) - } @Test @@ -147,9 +144,10 @@ class NotificationActivityUnitTests { NotificationActivity::class.java.getDeclaredField("isRead") isRead.isAccessible = true isRead.set(activity, true) - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "setEmptyView" - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "setEmptyView", + ) method.isAccessible = true method.invoke(activity) } @@ -161,9 +159,10 @@ class NotificationActivityUnitTests { NotificationActivity::class.java.getDeclaredField("isRead") isRead.isAccessible = true isRead.set(activity, false) - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "setEmptyView" - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "setEmptyView", + ) method.isAccessible = true method.invoke(activity) } @@ -175,9 +174,10 @@ class NotificationActivityUnitTests { NotificationActivity::class.java.getDeclaredField("isRead") isRead.isAccessible = true isRead.set(activity, true) - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "setMenuItemTitle" - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "setMenuItemTitle", + ) method.isAccessible = true method.invoke(activity) } @@ -189,9 +189,10 @@ class NotificationActivityUnitTests { NotificationActivity::class.java.getDeclaredField("isRead") isRead.isAccessible = true isRead.set(activity, false) - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "setMenuItemTitle" - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "setMenuItemTitle", + ) method.isAccessible = true method.invoke(activity) } @@ -203,9 +204,10 @@ class NotificationActivityUnitTests { NotificationActivity::class.java.getDeclaredField("isRead") isRead.isAccessible = true isRead.set(activity, true) - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "setPageTitle" - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "setPageTitle", + ) method.isAccessible = true method.invoke(activity) } @@ -217,9 +219,10 @@ class NotificationActivityUnitTests { NotificationActivity::class.java.getDeclaredField("isRead") isRead.isAccessible = true isRead.set(activity, false) - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "setPageTitle" - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "setPageTitle", + ) method.isAccessible = true method.invoke(activity) } @@ -227,9 +230,11 @@ class NotificationActivityUnitTests { @Test @Throws(Exception::class) fun testSetItemsListNull() { - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "setItems", List::class.java - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "setItems", + List::class.java, + ) method.isAccessible = true method.invoke(activity, null) } @@ -237,9 +242,11 @@ class NotificationActivityUnitTests { @Test @Throws(Exception::class) fun testSetItemsListNonNull() { - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "setItems", List::class.java - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "setItems", + List::class.java, + ) method.isAccessible = true method.invoke(activity, notificationList) } @@ -247,9 +254,11 @@ class NotificationActivityUnitTests { @Test @Throws(Exception::class) fun testHandleUrlNull() { - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "handleUrl", String::class.java - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "handleUrl", + String::class.java, + ) method.isAccessible = true method.invoke(activity, null) } @@ -257,9 +266,11 @@ class NotificationActivityUnitTests { @Test @Throws(Exception::class) fun testHandleUrlNonNull() { - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "handleUrl", String::class.java - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "handleUrl", + String::class.java, + ) method.isAccessible = true method.invoke(activity, "string") } @@ -271,9 +282,11 @@ class NotificationActivityUnitTests { NotificationActivity::class.java.getDeclaredField("mNotificationWorkerFragment") mNotificationWorkerFragment.isAccessible = true mNotificationWorkerFragment.set(activity, null) - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "addNotifications", Boolean::class.java - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "addNotifications", + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, true) } @@ -285,9 +298,11 @@ class NotificationActivityUnitTests { NotificationActivity::class.java.getDeclaredField("mNotificationWorkerFragment") mNotificationWorkerFragment.isAccessible = true mNotificationWorkerFragment.set(activity, notificationWorkerFragment) - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "addNotifications", Boolean::class.java - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "addNotifications", + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, true) } @@ -299,9 +314,10 @@ class NotificationActivityUnitTests { NotificationActivity::class.java.getDeclaredField("isRead") isRead.isAccessible = true isRead.set(activity, false) - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "initListView" - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "initListView", + ) method.isAccessible = true method.invoke(activity) } @@ -313,9 +329,10 @@ class NotificationActivityUnitTests { NotificationActivity::class.java.getDeclaredField("isRead") isRead.isAccessible = true isRead.set(activity, true) - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "initListView" - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "initListView", + ) method.isAccessible = true method.invoke(activity) } @@ -323,12 +340,12 @@ class NotificationActivityUnitTests { @Test @Throws(Exception::class) fun testRefreshNull() { - val method: Method = NotificationActivity::class.java.getDeclaredMethod( - "refresh", Boolean::class.java - ) + val method: Method = + NotificationActivity::class.java.getDeclaredMethod( + "refresh", + Boolean::class.java, + ) method.isAccessible = true method.invoke(activity, true) } - - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationClientTest.kt index 74ffb31826..e9451cd759 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationClientTest.kt @@ -2,7 +2,12 @@ package fr.free.nrw.commons.notification import androidx.test.ext.junit.runners.AndroidJUnit4 import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.auth.csrf.CsrfTokenClient import fr.free.nrw.commons.notification.models.NotificationType +import fr.free.nrw.commons.wikidata.GsonUtil +import fr.free.nrw.commons.wikidata.model.notifications.Notification +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResult import io.reactivex.Observable import junit.framework.TestCase.assertEquals import org.junit.Before @@ -17,17 +22,11 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode -import fr.free.nrw.commons.auth.csrf.CsrfTokenClient -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResult -import fr.free.nrw.commons.wikidata.GsonUtil -import fr.free.nrw.commons.wikidata.model.notifications.Notification @RunWith(AndroidJUnit4::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class NotificationClientTest { - @Mock private lateinit var service: NotificationInterface @@ -61,7 +60,8 @@ class NotificationClientTest { @Test fun getNotificationTest() { - Mockito.`when`(service.getAllNotifications(anyString(), anyString(), any())) + Mockito + .`when`(service.getAllNotifications(anyString(), anyString(), any())) .thenReturn(Observable.just(mQueryResponse)) Mockito.`when`(mQueryResponse.query()).thenReturn(mQueryResult) Mockito.`when`(mQueryResult.notifications()).thenReturn(mQueryResultNotificationsList) @@ -71,9 +71,9 @@ class NotificationClientTest { primaryUrl = "foo", compactHeader = "header", timestamp = "2024-01-22T10:12:00Z", - notificationId = 1234L - ) - ) + notificationId = 1234L, + ), + ), ) val result = notificationClient.getNotifications(true).test().values() @@ -81,7 +81,7 @@ class NotificationClientTest { verify(service).getAllNotifications( eq("wikidatawiki|commonswiki|enwiki"), eq("read"), - eq(null) + eq(null), ) val notificationList = result.first() @@ -103,7 +103,8 @@ class NotificationClientTest { @Test fun markNotificationAsReadTest() { Mockito.`when`(csrfTokenClient.getTokenBlocking()).thenReturn("test") - Mockito.`when`(service.markRead(anyString(), anyString(), anyString())) + Mockito + .`when`(service.markRead(anyString(), anyString(), anyString())) .thenReturn(Observable.just(mQueryResponse)) Mockito.`when`(mQueryResponse.success()).thenReturn(true) notificationClient.markNotificationAsRead("test") @@ -112,24 +113,33 @@ class NotificationClientTest { @Suppress("SameParameterValue") private fun createWikimediaNotification( - primaryUrl: String, compactHeader: String, timestamp: String, notificationId: Long + primaryUrl: String, + compactHeader: String, + timestamp: String, + notificationId: Long, ) = Notification().apply { setId(notificationId) - setTimestamp(Notification.Timestamp().apply { - setUtciso8601(timestamp) - }) - - contents = Notification.Contents().apply { - setCompactHeader(compactHeader) + setTimestamp( + Notification.Timestamp().apply { + setUtciso8601(timestamp) + }, + ) - links = Notification.Links().apply { - setPrimary( - GsonUtil.getDefaultGson().toJsonTree(Notification.Link().apply { - setUrl(primaryUrl) - }) - ) + contents = + Notification.Contents().apply { + setCompactHeader(compactHeader) + + links = + Notification.Links().apply { + setPrimary( + GsonUtil.getDefaultGson().toJsonTree( + Notification.Link().apply { + setUrl(primaryUrl) + }, + ), + ) + } } - } } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationControllerTest.kt index b329bb7889..dfe4c1cba5 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationControllerTest.kt @@ -6,14 +6,14 @@ import org.junit.Test import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers.eq import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations class NotificationControllerTest { - @Mock private lateinit var notificationClient: NotificationClient + @Mock private lateinit var notification: Notification private lateinit var notificationController: NotificationController @@ -46,4 +46,4 @@ class NotificationControllerTest { notificationController.markAsRead(notification) verify(notificationClient).markNotificationAsRead(eq("test")) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationHelperUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationHelperUnitTests.kt index 6503f0d2e8..49c50ff7fe 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationHelperUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationHelperUnitTests.kt @@ -20,7 +20,6 @@ import java.lang.reflect.Field @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class NotificationHelperUnitTests { - private lateinit var notificationHelper: NotificationHelper private lateinit var context: Context @@ -63,7 +62,7 @@ class NotificationHelperUnitTests { date = "", link = "", iconUrl = "", - notificationId = "" + notificationId = "", ) } @@ -76,8 +75,7 @@ class NotificationHelperUnitTests { date = "", link = "", iconUrl = "", - notificationId = "" + notificationId = "", ) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationWorkerFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationWorkerFragmentUnitTests.kt index 86ad998587..3db0f3bec1 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationWorkerFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/notification/NotificationWorkerFragmentUnitTests.kt @@ -5,7 +5,6 @@ import org.junit.Before import org.junit.Test class NotificationWorkerFragmentUnitTests { - private lateinit var fragment: NotificationWorkerFragment @Before @@ -36,5 +35,4 @@ class NotificationWorkerFragmentUnitTests { fun testGetNotificationList() { fragment.notificationList } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/profile/ProfileActivityTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/profile/ProfileActivityTest.kt index 328c617fc6..2dfeb42719 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/profile/ProfileActivityTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/profile/ProfileActivityTest.kt @@ -5,7 +5,6 @@ import android.graphics.Bitmap import android.os.Looper import android.view.Menu import android.view.MenuItem -import androidx.appcompat.widget.Toolbar import androidx.test.core.app.ApplicationProvider import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication @@ -23,11 +22,9 @@ import org.robolectric.fakes.RoboMenu import org.robolectric.fakes.RoboMenuItem import java.lang.reflect.Method - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class ProfileActivityTest { - @Mock private lateinit var activity: ProfileActivity @@ -95,9 +92,11 @@ class ProfileActivityTest { @Throws(Exception::class) fun testShareScreen() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = ProfileActivity::class.java.getDeclaredMethod( - "shareScreen", Bitmap::class.java - ) + val method: Method = + ProfileActivity::class.java.getDeclaredMethod( + "shareScreen", + Bitmap::class.java, + ) method.isAccessible = true method.invoke(activity, bitmap) } @@ -107,6 +106,7 @@ class ProfileActivityTest { fun testOnSupportNavigateUp() { activity.onSupportNavigateUp() } + @Test fun testToolbarNotNull() { val toolbar = activity.binding.toolbarBinding.toolbar @@ -119,5 +119,4 @@ class ProfileActivityTest { activity.onCreateOptionsMenu(menu) Assert.assertEquals(1, menu.size()) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/AchievementsFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/AchievementsFragmentUnitTests.kt index fee4f5d8d3..4f845962fc 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/AchievementsFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/AchievementsFragmentUnitTests.kt @@ -13,8 +13,8 @@ import androidx.test.core.app.ApplicationProvider import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.auth.SessionManager +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.profile.ProfileActivity import fr.free.nrw.commons.utils.ConfigUtils import org.junit.Assert @@ -34,12 +34,10 @@ import org.robolectric.fakes.RoboMenuItem import org.robolectric.shadows.ShadowToast import java.lang.reflect.Method - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class AchievementsFragmentUnitTests { - private lateinit var fragment: AchievementsFragment private lateinit var context: Context @@ -73,7 +71,7 @@ class AchievementsFragmentUnitTests { context = ApplicationProvider.getApplicationContext() menuItem = RoboMenuItem(context) OkHttpConnectionFactory.CLIENT = createTestClient() - + val activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get() val fragmentManager: FragmentManager = activity.supportFragmentManager val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction() @@ -165,11 +163,12 @@ class AchievementsFragmentUnitTests { @Throws(Exception::class) fun testLaunchAlert() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "launchAlert", - String::class.java, - String::class.java - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "launchAlert", + String::class.java, + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "", "") } @@ -178,10 +177,11 @@ class AchievementsFragmentUnitTests { @Throws(Exception::class) fun testHideProgressBar() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "hideProgressBar", - Achievements::class.java - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "hideProgressBar", + Achievements::class.java, + ) method.isAccessible = true method.invoke(fragment, achievements) } @@ -190,11 +190,12 @@ class AchievementsFragmentUnitTests { @Throws(Exception::class) fun testSetAchievementsUploadCount() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "setAchievementsUploadCount", - Achievements::class.java, - Int::class.java - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "setAchievementsUploadCount", + Achievements::class.java, + Int::class.java, + ) method.isAccessible = true method.invoke(fragment, achievements, 0) } @@ -203,9 +204,10 @@ class AchievementsFragmentUnitTests { @Throws(Exception::class) fun testCheckAccount() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "checkAccount" - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "checkAccount", + ) method.isAccessible = true method.invoke(fragment) } @@ -214,10 +216,11 @@ class AchievementsFragmentUnitTests { @Throws(Exception::class) fun testSetUploadCount() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "setUploadCount", - Achievements::class.java - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "setUploadCount", + Achievements::class.java, + ) method.isAccessible = true method.invoke(fragment, achievements) } @@ -226,9 +229,10 @@ class AchievementsFragmentUnitTests { @Throws(Exception::class) fun testOnError() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "onError" - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "onError", + ) method.isAccessible = true method.invoke(fragment) } @@ -237,9 +241,11 @@ class AchievementsFragmentUnitTests { @Throws(Exception::class) fun testShowSnackBarWithRetryTrue() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "showSnackBarWithRetry", Boolean::class.java - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "showSnackBarWithRetry", + Boolean::class.java, + ) method.isAccessible = true method.invoke(fragment, true) } @@ -248,9 +254,11 @@ class AchievementsFragmentUnitTests { @Throws(Exception::class) fun testShowSnackBarWithRetryFalse() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "showSnackBarWithRetry", Boolean::class.java - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "showSnackBarWithRetry", + Boolean::class.java, + ) method.isAccessible = true method.invoke(fragment, false) } @@ -259,9 +267,10 @@ class AchievementsFragmentUnitTests { @Throws(Exception::class) fun testSetWikidataEditCount() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "setWikidataEditCount" - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "setWikidataEditCount", + ) method.isAccessible = true method.invoke(fragment) } @@ -270,9 +279,10 @@ class AchievementsFragmentUnitTests { @Throws(Exception::class) fun testSetAchievements() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "setAchievements" - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "setAchievements", + ) method.isAccessible = true method.invoke(fragment) } @@ -281,10 +291,11 @@ class AchievementsFragmentUnitTests { @Throws(Exception::class) fun testMenuVisibilityOverrideNotVisible() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "setMenuVisibility", - Boolean::class.java - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "setMenuVisibility", + Boolean::class.java, + ) method.isAccessible = true method.invoke(fragment, false) assertToast() @@ -295,10 +306,11 @@ class AchievementsFragmentUnitTests { fun testMenuVisibilityOverrideVisibleWithContext() { Shadows.shadowOf(Looper.getMainLooper()).idle() Mockito.`when`(parentView.context).thenReturn(context) - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "setMenuVisibility", - Boolean::class.java - ) + val method: Method = + AchievementsFragment::class.java.getDeclaredMethod( + "setMenuVisibility", + Boolean::class.java, + ) method.isAccessible = true method.invoke(fragment, true) assertToast() @@ -308,14 +320,13 @@ class AchievementsFragmentUnitTests { if (ConfigUtils.isBetaFlavour) { Assert.assertEquals( ShadowToast.getTextOfLatestToast().toString(), - context.getString(R.string.achievements_unavailable_beta) + context.getString(R.string.achievements_unavailable_beta), ) } else { Assert.assertEquals( context.getString(R.string.user_not_logged_in), - ShadowToast.getTextOfLatestToast().toString() + ShadowToast.getTextOfLatestToast().toString(), ) } } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/LevelControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/LevelControllerTest.kt index 624150d4ba..bfb8cb4a15 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/LevelControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/LevelControllerTest.kt @@ -11,15 +11,14 @@ import org.mockito.MockitoAnnotations * Test for the Level Controller Class for Achievements */ class LevelControllerTest { - @Mock private lateinit var levelController: LevelController - private val IMAGES_UPLOADED_SAMPLE_VALUE = 0 + private val imagesUploadedSampleValue = 0 - private val UNIQUE_IMAGES_USED_SAMPLE_VALUE = 0 + private val uniqueImagesUsedSampleValue = 0 - private val NON_REVERT_RATE_SAMPLE_VALUE = 0 + private val nonRevertRateSampleValue = 0 private lateinit var levelInfo: LevelController.LevelInfo @@ -30,11 +29,12 @@ class LevelControllerTest { fun setUp() { MockitoAnnotations.openMocks(this) levelController = LevelController() - levelInfo = LevelController.LevelInfo.from( - IMAGES_UPLOADED_SAMPLE_VALUE, - UNIQUE_IMAGES_USED_SAMPLE_VALUE, - NON_REVERT_RATE_SAMPLE_VALUE - ) + levelInfo = + LevelController.LevelInfo.from( + imagesUploadedSampleValue, + uniqueImagesUsedSampleValue, + nonRevertRateSampleValue, + ) } /** @@ -79,4 +79,4 @@ class LevelControllerTest { fun testMinNonRevertPercentage() { assertEquals(levelInfo.minNonRevertPercentage, 85) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizActivityUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizActivityUnitTest.kt index 112be9ae03..a0508ea5ea 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizActivityUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizActivityUnitTest.kt @@ -25,9 +25,8 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class QuizActivityUnitTest { - - private val SAMPLE_ALERT_TITLE_VALUE = "Title" - private val SAMPLE_ALERT_MESSAGE_VALUE = "Message" + private val sampleAlertTitleValue = "Title" + private val sampleAlertMessageValue = "Message" private lateinit var activity: QuizActivity private lateinit var positiveAnswer: Button @@ -45,9 +44,12 @@ class QuizActivityUnitTest { Fresco.initialize(ApplicationProvider.getApplicationContext()) activity = Robolectric.buildActivity(QuizActivity::class.java).create().get() context = mock(Context::class.java) - view = LayoutInflater.from(activity) - .inflate(R.layout.answer_layout, null) as View - Mockito.`when`(context.getString(Mockito.any(Int::class.java))) + view = + LayoutInflater + .from(activity) + .inflate(R.layout.answer_layout, null) as View + Mockito + .`when`(context.getString(Mockito.any(Int::class.java))) .thenReturn("") quizController = QuizController() quizController.initialize(context) @@ -86,7 +88,6 @@ class QuizActivityUnitTest { @Test @Throws(Exception::class) fun testCustomAlert() { - activity.customAlert(SAMPLE_ALERT_TITLE_VALUE, SAMPLE_ALERT_MESSAGE_VALUE) + activity.customAlert(sampleAlertTitleValue, sampleAlertMessageValue) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizCheckerUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizCheckerUnitTest.kt index d566f15c98..0b7498592c 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizCheckerUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizCheckerUnitTest.kt @@ -27,7 +27,6 @@ import java.lang.reflect.Method @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class QuizCheckerUnitTest { - private lateinit var quizChecker: QuizChecker private lateinit var activity: Activity @@ -52,7 +51,8 @@ class QuizCheckerUnitTest { quizChecker = QuizChecker(sessionManager, okHttpJsonApiClient, jsonKvStore) Mockito.`when`(sessionManager.userName).thenReturn("") Mockito.`when`(okHttpJsonApiClient.getUploadCount(any())).thenReturn(Single.just(0)) - Mockito.`when`(okHttpJsonApiClient.getAchievements(any())) + Mockito + .`when`(okHttpJsonApiClient.getAchievements(any())) .thenReturn(Single.just(feedbackResponse)) } @@ -77,10 +77,11 @@ class QuizCheckerUnitTest { @Test @Throws(Exception::class) fun testSetTotalUploadCount() { - val method: Method = QuizChecker::class.java.getDeclaredMethod( - "setTotalUploadCount", - Int::class.java - ) + val method: Method = + QuizChecker::class.java.getDeclaredMethod( + "setTotalUploadCount", + Int::class.java, + ) method.isAccessible = true method.invoke(quizChecker, -1) } @@ -88,10 +89,11 @@ class QuizCheckerUnitTest { @Test @Throws(Exception::class) fun testSetRevertParameter() { - val method: Method = QuizChecker::class.java.getDeclaredMethod( - "setRevertParameter", - Int::class.java - ) + val method: Method = + QuizChecker::class.java.getDeclaredMethod( + "setRevertParameter", + Int::class.java, + ) method.isAccessible = true method.invoke(quizChecker, -1) } @@ -100,10 +102,11 @@ class QuizCheckerUnitTest { @Throws(Exception::class) fun testCalculateRevertParameterAndShowQuiz() { Whitebox.setInternalState(quizChecker, "revertCount", -1) - val method: Method = QuizChecker::class.java.getDeclaredMethod( - "calculateRevertParameterAndShowQuiz", - Activity::class.java - ) + val method: Method = + QuizChecker::class.java.getDeclaredMethod( + "calculateRevertParameterAndShowQuiz", + Activity::class.java, + ) method.isAccessible = true method.invoke(quizChecker, activity) } @@ -115,10 +118,11 @@ class QuizCheckerUnitTest { Whitebox.setInternalState(quizChecker, "isUploadCountFetched", true) Whitebox.setInternalState(quizChecker, "totalUploadCount", 5) Whitebox.setInternalState(quizChecker, "revertCount", 5) - val method: Method = QuizChecker::class.java.getDeclaredMethod( - "calculateRevertParameterAndShowQuiz", - Activity::class.java - ) + val method: Method = + QuizChecker::class.java.getDeclaredMethod( + "calculateRevertParameterAndShowQuiz", + Activity::class.java, + ) method.isAccessible = true method.invoke(quizChecker, activity) } @@ -126,12 +130,12 @@ class QuizCheckerUnitTest { @Test @Throws(Exception::class) fun testStartQuizActivity() { - val method: Method = QuizChecker::class.java.getDeclaredMethod( - "startQuizActivity", - Activity::class.java - ) + val method: Method = + QuizChecker::class.java.getDeclaredMethod( + "startQuizActivity", + Activity::class.java, + ) method.isAccessible = true method.invoke(quizChecker, activity) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizControllerTest.kt index 52180aae98..c0e3c4db23 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizControllerTest.kt @@ -5,13 +5,11 @@ import org.junit.Assert.assertNotNull import org.junit.Before import org.junit.Test import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.any +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations - class QuizControllerTest { - @Mock private lateinit var quizController: QuizController @@ -35,5 +33,4 @@ class QuizControllerTest { fun testGetQuiz() { assertNotNull(quizController.getQuiz()) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizQuestionTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizQuestionTest.kt index c591f277f0..6c6b25420a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizQuestionTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizQuestionTest.kt @@ -14,38 +14,37 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class QuizQuestionTest { - @Mock private lateinit var quizQuestion: QuizQuestion - private val QUESTION_NUM_SAMPLE_VALUE = 1 - private val QUESTION_SAMPLE_VALUE = "Is this picture OK to upload?" - private val QUESTION_URL_SAMPLE_VALUE_ONE = "https://i.imgur.com/0fMYcpM.jpg" - private val QUESTION_URL_SAMPLE_VALUE_TWO = "https://example.com" - private val IS_ANSWER_SAMPLE_VALUE = false - private val ANSWER_MESSAGE_SAMPLE_VALUE = "Continue" + private val questionNumSampleValue = 1 + private val questionSampleValue = "Is this picture OK to upload?" + private val questionUrlSampleValueOne = "https://i.imgur.com/0fMYcpM.jpg" + private val questionUrlSampleValueTwo = "https://example.com" + private val isAnswerSampleValue = false + private val answerMessageSampleValue = "Continue" @Before fun setup() { MockitoAnnotations.openMocks(this) - quizQuestion = QuizQuestion( - QUESTION_NUM_SAMPLE_VALUE, - QUESTION_SAMPLE_VALUE, - QUESTION_URL_SAMPLE_VALUE_ONE, - IS_ANSWER_SAMPLE_VALUE, - ANSWER_MESSAGE_SAMPLE_VALUE - ) + quizQuestion = + QuizQuestion( + questionNumSampleValue, + questionSampleValue, + questionUrlSampleValueOne, + isAnswerSampleValue, + answerMessageSampleValue, + ) } @Test fun testGetUrl() { - assertEquals(quizQuestion.getUrl(), Uri.parse(QUESTION_URL_SAMPLE_VALUE_ONE)) + assertEquals(quizQuestion.getUrl(), Uri.parse(questionUrlSampleValueOne)) } @Test fun testSetUrl() { - quizQuestion.setUrl(QUESTION_URL_SAMPLE_VALUE_TWO) - assertEquals(quizQuestion.getUrl(), Uri.parse(QUESTION_URL_SAMPLE_VALUE_TWO)) + quizQuestion.setUrl(questionUrlSampleValueTwo) + assertEquals(quizQuestion.getUrl(), Uri.parse(questionUrlSampleValueTwo)) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizResultActivityUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizResultActivityUnitTest.kt index f2785777ec..8af4c4e19b 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizResultActivityUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/quiz/QuizResultActivityUnitTest.kt @@ -19,7 +19,6 @@ import org.robolectric.fakes.RoboMenu @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class QuizResultActivityUnitTest { - private lateinit var activity: QuizResultActivity private lateinit var quizResultActivity: QuizResultActivity private lateinit var menu: RoboMenu @@ -84,5 +83,4 @@ class QuizResultActivityUnitTest { fun tesShareScreen() { activity.shareScreen(bitmap) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/quiz/RadioGroupHelperUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/quiz/RadioGroupHelperUnitTest.kt index 0464ca325a..12eebfd698 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/quiz/RadioGroupHelperUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/quiz/RadioGroupHelperUnitTest.kt @@ -8,12 +8,9 @@ import org.junit.Assert import org.junit.Before import org.junit.Test import org.mockito.Mock -import org.mockito.Mockito.`when` -import org.mockito.Mockito.anyInt import org.mockito.MockitoAnnotations class RadioGroupHelperUnitTest { - private lateinit var radioGroupHelper: RadioGroupHelper @Mock @@ -54,5 +51,4 @@ class RadioGroupHelperUnitTest { fun constructor2() { radioGroupHelper = RadioGroupHelper(activity) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapterUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapterUnitTest.kt index f49c83e7f9..6c9a192739 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapterUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapterUnitTest.kt @@ -4,14 +4,14 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.contributions.MainActivity import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import fr.free.nrw.commons.R -import fr.free.nrw.commons.contributions.MainActivity import org.mockito.MockitoAnnotations import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner @@ -24,7 +24,6 @@ import java.lang.reflect.Field @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class RecentLanguagesAdapterUnitTest { - private lateinit var context: Context private lateinit var convertView: View private lateinit var activity: MainActivity @@ -44,10 +43,11 @@ class RecentLanguagesAdapterUnitTest { convertView = LayoutInflater.from(context).inflate(R.layout.row_item_languages_spinner, null) as View - languages = listOf( - Language("English", "en"), - Language("Bengali", "bn") - ) + languages = + listOf( + Language("English", "en"), + Language("Bengali", "bn"), + ) adapter = RecentLanguagesAdapter(context, languages, hashMapOf(1 to "en")) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesContentProviderUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesContentProviderUnitTest.kt index a1f2020551..c6880ad7bd 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesContentProviderUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesContentProviderUnitTest.kt @@ -16,14 +16,13 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class RecentLanguagesContentProviderUnitTest { - private lateinit var contentProvider: RecentLanguagesContentProvider @Mock lateinit var dbOpenHelper: DBOpenHelper @Before - fun setUp(){ + fun setUp() { MockitoAnnotations.openMocks(this) contentProvider = RecentLanguagesContentProvider() Whitebox.setInternalState(contentProvider, "dbOpenHelper", dbOpenHelper) @@ -34,4 +33,4 @@ class RecentLanguagesContentProviderUnitTest { fun testGetType() { contentProvider.getType(mock(Uri::class.java)) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesDaoUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesDaoUnitTest.kt index ef4f066e82..087640a44c 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesDaoUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/recentlanguages/RecentLanguagesDaoUnitTest.kt @@ -6,9 +6,19 @@ import android.database.Cursor import android.database.MatrixCursor import android.database.sqlite.SQLiteDatabase import android.os.RemoteException -import com.nhaarman.mockitokotlin2.* +import com.nhaarman.mockitokotlin2.anyOrNull +import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.inOrder +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.* +import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.COLUMN_CODE +import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.COLUMN_NAME +import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.CREATE_TABLE_STATEMENT +import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.DROP_TABLE_STATEMENT +import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.onCreate +import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.onDelete +import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.onUpdate import org.junit.Assert import org.junit.Before import org.junit.Test @@ -17,18 +27,17 @@ import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoInteractions -import org.mockito.Mockito.verifyNoInteractions import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class RecentLanguagesDaoUnitTest { - - private val columns = arrayOf( - COLUMN_NAME, - COLUMN_CODE - ) + private val columns = + arrayOf( + COLUMN_NAME, + COLUMN_CODE, + ) private val client: ContentProviderClient = mock() private val database: SQLiteDatabase = mock() @@ -79,13 +88,12 @@ class RecentLanguagesDaoUnitTest { val result = testObject.recentLanguages Assert.assertEquals(14, (result.size)) - } @Test(expected = RuntimeException::class) fun getGetRecentLanguagesTranslatesExceptions() { whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow( - RemoteException("") + RemoteException(""), ) testObject.recentLanguages } @@ -123,7 +131,7 @@ class RecentLanguagesDaoUnitTest { @Test(expected = RuntimeException::class) fun findLanguageTranslatesExceptions() { whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow( - RemoteException("") + RemoteException(""), ) testObject.findRecentLanguage(exampleLanguage.languageCode) } @@ -291,11 +299,11 @@ class RecentLanguagesDaoUnitTest { Assert.assertEquals(2, cv.size()) Assert.assertEquals( exampleLanguage.languageName, - cv.getAsString(COLUMN_NAME) + cv.getAsString(COLUMN_NAME), ) Assert.assertEquals( exampleLanguage.languageCode, - cv.getAsString(COLUMN_CODE) + cv.getAsString(COLUMN_CODE), ) } } @@ -306,10 +314,10 @@ class RecentLanguagesDaoUnitTest { testObject.deleteRecentLanguage(exampleLanguage.languageCode) } - private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply { - - for (i in 0 until rowCount) { - addRow(listOf("languageName", "languageCode")) + private fun createCursor(rowCount: Int) = + MatrixCursor(columns, rowCount).apply { + for (i in 0 until rowCount) { + addRow(listOf("languageName", "languageCode")) + } } - } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt index b401d7e813..f2faf769ac 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewActivityTest.kt @@ -20,7 +20,9 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers import org.mockito.Mock -import org.mockito.Mockito.* +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations import org.powermock.reflect.Whitebox import org.robolectric.Robolectric @@ -36,7 +38,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ReviewActivityTest { - private lateinit var activity: ReviewActivity private lateinit var menuItem: MenuItem @@ -79,12 +80,11 @@ class ReviewActivityTest { menuItem = RoboMenuItem(null) menu = RoboMenu(context) - Whitebox.setInternalState(binding, "viewPagerReview", reviewPager); - Whitebox.setInternalState(activity, "hasNonHiddenCategories", hasNonHiddenCategories); - Whitebox.setInternalState(activity, "reviewHelper", reviewHelper); - Whitebox.setInternalState(activity, "reviewImageFragment", reviewImageFragment); - Whitebox.setInternalState(activity, "reviewPagerAdapter", reviewPagerAdapter); - + Whitebox.setInternalState(binding, "viewPagerReview", reviewPager) + Whitebox.setInternalState(activity, "hasNonHiddenCategories", hasNonHiddenCategories) + Whitebox.setInternalState(activity, "reviewHelper", reviewHelper) + Whitebox.setInternalState(activity, "reviewImageFragment", reviewImageFragment) + Whitebox.setInternalState(activity, "reviewPagerAdapter", reviewPagerAdapter) } @Test @@ -103,7 +103,7 @@ class ReviewActivityTest { @Throws(Exception::class) fun testSwipeToNext() { shadowOf(getMainLooper()).idle() - doReturn(1,2).`when`(reviewPager)?.currentItem + doReturn(1, 2).`when`(reviewPager)?.currentItem activity.swipeToNext() } @@ -120,7 +120,7 @@ class ReviewActivityTest { reviewHelper ?.getRandomMedia() ?.test() - ?.assertValue(media); + ?.assertValue(media) activity.swipeToNext() } @@ -135,11 +135,9 @@ class ReviewActivityTest { doNothing().`when`(reviewImageFragment).disableButtons() var findNonHiddenCategory: Method = - ReviewActivity::class.java.getDeclaredMethod("findNonHiddenCategories" - , Media::class.java) + ReviewActivity::class.java.getDeclaredMethod("findNonHiddenCategories", Media::class.java) findNonHiddenCategory.isAccessible = true findNonHiddenCategory.invoke(activity, media) - } @Test @@ -195,5 +193,4 @@ class ReviewActivityTest { fun testOnBackPressed() { activity.onBackPressed() } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt index a8387919a9..059583f8a3 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewControllerTest.kt @@ -14,6 +14,7 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.delete.DeleteHelper +import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage import media import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -30,15 +31,13 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import org.robolectric.shadows.ShadowNotificationManager import org.robolectric.shadows.ShadowToast -import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage import java.lang.reflect.Method -import java.util.* +import java.util.Date @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ReviewControllerTest { - private lateinit var controller: ReviewController private lateinit var context: Context private lateinit var activity: Activity @@ -82,7 +81,7 @@ class ReviewControllerTest { activity, activity.resources.getString(R.string.review_spam_report_question), ReviewController.DeleteReason.SPAM, - reviewCallback + reviewCallback, ) } @@ -95,7 +94,7 @@ class ReviewControllerTest { activity, activity.resources.getString(R.string.review_c_violation_report_question), ReviewController.DeleteReason.COPYRIGHT_VIOLATION, - reviewCallback + reviewCallback, ) } @@ -105,28 +104,33 @@ class ReviewControllerTest { controller.reportWrongCategory(activity, reviewCallback) assertEquals( ShadowToast.getTextOfLatestToast().toString(), - context.getString(R.string.check_category_toast, media.displayTitle) + context.getString(R.string.check_category_toast, media.displayTitle), ) } @Test fun testPublishProgress() { shadowOf(Looper.getMainLooper()).idle() - val method: Method = ReviewController::class.java.getDeclaredMethod( - "publishProgress", Context::class.java, Int::class.java - ) + val method: Method = + ReviewController::class.java.getDeclaredMethod( + "publishProgress", + Context::class.java, + Int::class.java, + ) method.isAccessible = true method.invoke(controller, context, 1) assertNotNull(ShadowNotificationManager().allNotifications) } - @Test fun testShowNotification() { shadowOf(Looper.getMainLooper()).idle() - val method: Method = ReviewController::class.java.getDeclaredMethod( - "showNotification", String::class.java, String::class.java - ) + val method: Method = + ReviewController::class.java.getDeclaredMethod( + "showNotification", + String::class.java, + String::class.java, + ) method.isAccessible = true method.invoke(controller, "", "") assertNotNull(ShadowNotificationManager().allNotifications) @@ -141,26 +145,30 @@ class ReviewControllerTest { assertEquals( ShadowToast.getTextOfLatestToast().toString(), context.getString( - R.string.send_thank_toast, media.displayTitle - ) + R.string.send_thank_toast, + media.displayTitle, + ), ) - val method: Method = ReviewController::class.java.getDeclaredMethod( - "displayThanksToast", Context::class.java, Boolean::class.java - ) + val method: Method = + ReviewController::class.java.getDeclaredMethod( + "displayThanksToast", + Context::class.java, + Boolean::class.java, + ) method.isAccessible = true - method.invoke(controller,context,true) + method.invoke(controller, context, true) assertEquals( ShadowToast.getTextOfLatestToast().toString(), context.getString( - R.string.send_thank_success_message, media.displayTitle - ) + R.string.send_thank_success_message, + media.displayTitle, + ), ) } - @Test fun testSendThanksCaseNull() { shadowOf(Looper.getMainLooper()).idle() @@ -168,8 +176,9 @@ class ReviewControllerTest { assertEquals( ShadowToast.getTextOfLatestToast().toString(), context.getString( - R.string.send_thank_toast, media.displayTitle - ) + R.string.send_thank_toast, + media.displayTitle, + ), ) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewDaoTest.kt index 55623b3cb8..96aacf6f84 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewDaoTest.kt @@ -18,7 +18,7 @@ import org.robolectric.annotation.LooperMode @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ReviewDaoTest { - private lateinit var reviewDao: ReviewDao + private lateinit var reviewDao: ReviewDao private lateinit var database: AppDatabase /** @@ -26,10 +26,11 @@ class ReviewDaoTest { */ @Before fun createDb() { - database = inMemoryDatabaseBuilder( - context = ApplicationProvider.getApplicationContext(), - klass = AppDatabase::class.java - ).allowMainThreadQueries().build() + database = + inMemoryDatabaseBuilder( + context = ApplicationProvider.getApplicationContext(), + klass = AppDatabase::class.java, + ).allowMainThreadQueries().build() reviewDao = database.ReviewDao() } @@ -70,4 +71,4 @@ class ReviewDaoTest { val isInserted = reviewDao.isReviewedAlready(imageId) MatcherAssert.assertThat(isInserted, CoreMatchers.equalTo(false)) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt index 74be27d5b3..283bbf268e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt @@ -13,18 +13,19 @@ import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test -import org.mockito.Mockito.* +import org.mockito.Mockito.any +import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify /** * Test class for ReviewHelper */ class ReviewHelperTest { - private val reviewInterface = mock() private val mediaClient = mock() private val reviewHelper = ReviewHelper(mediaClient, reviewInterface) - private val mwQueryResult = mock() private val mockResponse = mock() @@ -132,16 +133,21 @@ class ReviewHelperTest { assertTrue(result) } - private fun setupMedia(file: String, vararg revision: MwQueryPage.Revision): MwQueryPage = mock().apply { - whenever(title()).thenReturn(file) - if (revision.isNotEmpty()) { - whenever(revisions()).thenReturn(*revision.toMutableList()) + private fun setupMedia( + file: String, + vararg revision: MwQueryPage.Revision, + ): MwQueryPage = + mock().apply { + whenever(title()).thenReturn(file) + if (revision.isNotEmpty()) { + whenever(revisions()).thenReturn(*revision.toMutableList()) + } + + val media = + mock().apply { + whenever(filename).thenReturn(file) + whenever(pageId).thenReturn(file.split(".").first()) + } + whenever(mediaClient.getMedia(file)).thenReturn(Single.just(media)) } - - val media = mock().apply { - whenever(filename).thenReturn(file) - whenever(pageId).thenReturn(file.split(".").first()) - } - whenever(mediaClient.getMedia(file)).thenReturn(Single.just(media)) - } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewImageFragmentTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewImageFragmentTest.kt index 6e08dc665b..bf4bd18784 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewImageFragmentTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewImageFragmentTest.kt @@ -62,7 +62,6 @@ class ReviewImageFragmentTest { @Before fun setUp() { - MockitoAnnotations.openMocks(this) context = ApplicationProvider.getApplicationContext() OkHttpConnectionFactory.CLIENT = createTestClient() @@ -79,8 +78,10 @@ class ReviewImageFragmentTest { fragmentTransaction.add(fragment, null) fragmentTransaction.commit() - view = LayoutInflater.from(activity) - .inflate(R.layout.fragment_review_image, null) as View + view = + LayoutInflater + .from(activity) + .inflate(R.layout.fragment_review_image, null) as View binding = FragmentReviewImageBinding.inflate(LayoutInflater.from(activity)) noButton = view.findViewById(R.id.button_no) @@ -105,7 +106,6 @@ class ReviewImageFragmentTest { assertEquals(noButton.alpha, 0.5f) } - @Test @Throws(Exception::class) fun testOnEnableButton() { @@ -116,7 +116,6 @@ class ReviewImageFragmentTest { assertEquals(noButton.alpha, 1f) } - @Test @Throws(Exception::class) fun testOnUpdateCategoriesQuestion() { diff --git a/app/src/test/kotlin/fr/free/nrw/commons/settings/SettingsActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/settings/SettingsActivityUnitTests.kt index e8a3fe19a8..2caef0566f 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/settings/SettingsActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/settings/SettingsActivityUnitTests.kt @@ -24,7 +24,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class SettingsActivityUnitTests { - private lateinit var activity: SettingsActivity private lateinit var context: Context private lateinit var menuItem: MenuItem @@ -69,12 +68,12 @@ class SettingsActivityUnitTests { @Throws(Exception::class) fun testSetTotalUploadCount() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = SettingsActivity::class.java.getDeclaredMethod( - "onPostCreate", - Bundle::class.java - ) + val method: Method = + SettingsActivity::class.java.getDeclaredMethod( + "onPostCreate", + Bundle::class.java, + ) method.isAccessible = true method.invoke(activity, savedInstanceState) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/settings/SettingsFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/settings/SettingsFragmentUnitTests.kt index c05d810298..6c9ee9d039 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/settings/SettingsFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/settings/SettingsFragmentUnitTests.kt @@ -22,7 +22,6 @@ import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao import fr.free.nrw.commons.settings.SettingsFragment.createLocale import org.junit.Assert import org.junit.Assert.assertEquals -import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -38,12 +37,10 @@ import org.robolectric.annotation.LooperMode import java.lang.reflect.Method import java.util.Locale - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class SettingsFragmentUnitTests { - private lateinit var fragment: SettingsFragment private lateinit var fragmentManager: FragmentManager private lateinit var layoutInflater: LayoutInflater @@ -79,11 +76,17 @@ class SettingsFragmentUnitTests { layoutInflater = LayoutInflater.from(activity) Whitebox.setInternalState(fragment, "recentLanguagesDao", recentLanguagesDao) - Whitebox.setInternalState(fragment, "recentLanguagesTextView", - recentLanguagesTextView) + Whitebox.setInternalState( + fragment, + "recentLanguagesTextView", + recentLanguagesTextView, + ) Whitebox.setInternalState(fragment, "separator", separator) - Whitebox.setInternalState(fragment, "languageHistoryListView", - languageHistoryListView) + Whitebox.setInternalState( + fragment, + "languageHistoryListView", + languageHistoryListView, + ) } @Test @@ -95,9 +98,10 @@ class SettingsFragmentUnitTests { @Test @Throws(Exception::class) fun testRequestExternalStoragePermissions() { - val method: Method = SettingsFragment::class.java.getDeclaredMethod( - "requestExternalStoragePermissions" - ) + val method: Method = + SettingsFragment::class.java.getDeclaredMethod( + "requestExternalStoragePermissions", + ) method.isAccessible = true method.invoke(fragment) } @@ -106,9 +110,10 @@ class SettingsFragmentUnitTests { @Throws(Exception::class) fun testCheckPermissionsAndSendLogs() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = SettingsFragment::class.java.getDeclaredMethod( - "checkPermissionsAndSendLogs" - ) + val method: Method = + SettingsFragment::class.java.getDeclaredMethod( + "checkPermissionsAndSendLogs", + ) method.isAccessible = true method.invoke(fragment) } @@ -117,10 +122,11 @@ class SettingsFragmentUnitTests { @Throws(Exception::class) fun testGetCurrentLanguageCode() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = SettingsFragment::class.java.getDeclaredMethod( - "getCurrentLanguageCode", - String::class.java - ) + val method: Method = + SettingsFragment::class.java.getDeclaredMethod( + "getCurrentLanguageCode", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "") } @@ -129,11 +135,12 @@ class SettingsFragmentUnitTests { @Throws(Exception::class) fun testSaveLanguageValueCase_appUiDefaultLanguagePref() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = SettingsFragment::class.java.getDeclaredMethod( - "saveLanguageValue", - String::class.java, - String::class.java - ) + val method: Method = + SettingsFragment::class.java.getDeclaredMethod( + "saveLanguageValue", + String::class.java, + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "", "appUiDefaultLanguagePref") } @@ -142,12 +149,13 @@ class SettingsFragmentUnitTests { @Throws(Exception::class) fun `Test prepareAppLanguages when recently used languages is empty`() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = SettingsFragment::class.java.getDeclaredMethod( - "prepareAppLanguages", - String::class.java - ) + val method: Method = + SettingsFragment::class.java.getDeclaredMethod( + "prepareAppLanguages", + String::class.java, + ) method.isAccessible = true - method.invoke(fragment, "appUiDefaultLanguagePref") + method.invoke(fragment, "appUiDefaultLanguagePref") verify(recentLanguagesDao, times(1)).recentLanguages } @@ -157,19 +165,22 @@ class SettingsFragmentUnitTests { Shadows.shadowOf(Looper.getMainLooper()).idle() whenever(recentLanguagesDao.recentLanguages) .thenReturn( - mutableListOf(Language("English", "en"), - Language("English", "en"), - Language("English", "en"), - Language("English", "en"), - Language("English", "en"), - Language("English", "en")) + mutableListOf( + Language("English", "en"), + Language("English", "en"), + Language("English", "en"), + Language("English", "en"), + Language("English", "en"), + Language("English", "en"), + ), + ) + val method: Method = + SettingsFragment::class.java.getDeclaredMethod( + "prepareAppLanguages", + String::class.java, ) - val method: Method = SettingsFragment::class.java.getDeclaredMethod( - "prepareAppLanguages", - String::class.java - ) method.isAccessible = true - method.invoke(fragment, "appUiDefaultLanguagePref") + method.invoke(fragment, "appUiDefaultLanguagePref") verify(recentLanguagesDao, times(2)).recentLanguages } @@ -177,11 +188,12 @@ class SettingsFragmentUnitTests { @Throws(Exception::class) fun testSaveLanguageValueCase_descriptionDefaultLanguagePref() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = SettingsFragment::class.java.getDeclaredMethod( - "saveLanguageValue", - String::class.java, - String::class.java - ) + val method: Method = + SettingsFragment::class.java.getDeclaredMethod( + "saveLanguageValue", + String::class.java, + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "", "descriptionDefaultLanguagePref") } @@ -190,9 +202,10 @@ class SettingsFragmentUnitTests { @Throws(Exception::class) fun testHideRecentLanguagesSection() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = SettingsFragment::class.java.getDeclaredMethod( - "hideRecentLanguagesSection" - ) + val method: Method = + SettingsFragment::class.java.getDeclaredMethod( + "hideRecentLanguagesSection", + ) method.isAccessible = true method.invoke(fragment) verify(recentLanguagesTextView, times(1)).visibility = any() @@ -206,18 +219,21 @@ class SettingsFragmentUnitTests { whenever(recentLanguagesDao.findRecentLanguage(any())) .thenReturn(true) whenever(adapterView.adapter) - .thenReturn(RecentLanguagesAdapter(context, - listOf(Language("English", "en")), - hashMapOf() - ) + .thenReturn( + RecentLanguagesAdapter( + context, + listOf(Language("English", "en")), + hashMapOf(), + ), + ) + val method: Method = + SettingsFragment::class.java.getDeclaredMethod( + "onRecentLanguageClicked", + String::class.java, + Dialog::class.java, + AdapterView::class.java, + Int::class.java, ) - val method: Method = SettingsFragment::class.java.getDeclaredMethod( - "onRecentLanguageClicked", - String::class.java, - Dialog::class.java, - AdapterView::class.java, - Int::class.java - ) method.isAccessible = true method.invoke(fragment, "test", Mockito.mock(Dialog::class.java), adapterView, 0) verify(recentLanguagesDao, times(1)).findRecentLanguage(any()) @@ -226,13 +242,14 @@ class SettingsFragmentUnitTests { @Test fun `Test setUpRecentLanguagesSection when list is empty`() { - val method: Method = SettingsFragment::class.java.getDeclaredMethod( - "setUpRecentLanguagesSection", - List::class.java, - HashMap::class.java - ) + val method: Method = + SettingsFragment::class.java.getDeclaredMethod( + "setUpRecentLanguagesSection", + List::class.java, + HashMap::class.java, + ) method.isAccessible = true - method.invoke(fragment, emptyList(), hashMapOf()) + method.invoke(fragment, emptyList(), hashMapOf()) verify(languageHistoryListView, times(1)).visibility = View.GONE verify(separator, times(1)).visibility = View.GONE verify(recentLanguagesTextView, times(1)).visibility = View.GONE @@ -240,20 +257,25 @@ class SettingsFragmentUnitTests { @Test fun `Test setUpRecentLanguagesSection when list is not empty`() { - val method: Method = SettingsFragment::class.java.getDeclaredMethod( - "setUpRecentLanguagesSection", - List::class.java, - HashMap::class.java - ) + val method: Method = + SettingsFragment::class.java.getDeclaredMethod( + "setUpRecentLanguagesSection", + List::class.java, + HashMap::class.java, + ) method.isAccessible = true - method.invoke(fragment, listOf( - Language("Bengali", "bn"), - Language("Bengali", "bn"), - Language("Bengali", "bn"), - Language("Bengali", "bn"), - Language("Bengali", "bn"), - Language("Bengali", "bn") - ), hashMapOf()) + method.invoke( + fragment, + listOf( + Language("Bengali", "bn"), + Language("Bengali", "bn"), + Language("Bengali", "bn"), + Language("Bengali", "bn"), + Language("Bengali", "bn"), + Language("Bengali", "bn"), + ), + hashMapOf(), + ) verify(languageHistoryListView, times(1)).visibility = View.VISIBLE verify(separator, times(1)).visibility = View.VISIBLE verify(recentLanguagesTextView, times(1)).visibility = View.VISIBLE @@ -264,8 +286,8 @@ class SettingsFragmentUnitTests { val locale: Locale = createLocale("en") assertEquals("en", locale.language) - assertEquals("",locale.country) - assertEquals("",locale.variant) + assertEquals("", locale.country) + assertEquals("", locale.variant) } @Test @@ -273,8 +295,8 @@ class SettingsFragmentUnitTests { val locale: Locale = createLocale("zh-CN") assertEquals("zh", locale.language) - assertEquals("CN",locale.country) - assertEquals("",locale.variant) + assertEquals("CN", locale.country) + assertEquals("", locale.variant) } @Test @@ -282,7 +304,7 @@ class SettingsFragmentUnitTests { val locale: Locale = createLocale("pt-BR-variant") assertEquals("pt", locale.language) - assertEquals("BR",locale.country) - assertEquals("variant",locale.variant) + assertEquals("BR", locale.country) + assertEquals("variant", locale.variant) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt index 679a25c8d9..4b321071fe 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt @@ -2,7 +2,11 @@ package fr.free.nrw.commons.upload import androidx.lifecycle.MutableLiveData import categoryItem -import com.nhaarman.mockitokotlin2.* +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions +import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.R import fr.free.nrw.commons.category.CategoryItem import fr.free.nrw.commons.repository.UploadRepository @@ -17,7 +21,6 @@ import org.mockito.Mock import org.mockito.MockitoAnnotations import java.lang.reflect.Method - /** * The class contains unit test cases for CategoriesPresenter */ @@ -41,7 +44,6 @@ class CategoriesPresenterTest { MockitoAnnotations.openMocks(this) testScheduler = TestScheduler() categoriesPresenter = CategoriesPresenter(repository, testScheduler, testScheduler) - } @Test @@ -58,12 +60,14 @@ class CategoriesPresenterTest { .thenReturn(Observable.just(mutableListOf(categoryItem()))) whenever(repository.searchAll("mock", emptyList(), repository.selectedDepictions)) .thenReturn(Observable.just(mutableListOf(categoryItem()))) - val method: Method = CategoriesPresenter::class.java.getDeclaredMethod( - "searchResults", - String::class.java - ) + val method: Method = + CategoriesPresenter::class.java.getDeclaredMethod( + "searchResults", + String::class.java, + ) method.isAccessible = true - method.invoke(categoriesPresenter, "mock") } + method.invoke(categoriesPresenter, "mock") + } /** * unit test case for method CategoriesPresenter.searchForCategories @@ -73,8 +77,11 @@ class CategoriesPresenterTest { categoriesPresenter.onAttachView(view) val liveData = MutableLiveData>() categoriesPresenter.setCategoryList(liveData) - categoriesPresenter.setCategoryListValue(listOf( - categoryItem("selected", "", "", true))) + categoriesPresenter.setCategoryListValue( + listOf( + categoryItem("selected", "", "", true), + ), + ) val nonEmptyCaptionUploadItem = mock() whenever(nonEmptyCaptionUploadItem.uploadMediaDetails) .thenReturn(listOf(UploadMediaDetail(captionText = "nonEmpty"))) @@ -84,22 +91,25 @@ class CategoriesPresenterTest { whenever(repository.uploads).thenReturn( listOf( nonEmptyCaptionUploadItem, - emptyCaptionUploadItem - ) + emptyCaptionUploadItem, + ), ) whenever(repository.searchAll(any(), any(), any())) .thenReturn( Observable.just( listOf( categoryItem("selected"), - categoryItem("doesContainYear") - ) - ) + categoryItem("doesContainYear"), + ), + ), ) whenever(repository.isSpammyCategory("selected")).thenReturn(false) whenever(repository.isSpammyCategory("doesContainYear")).thenReturn(true) - whenever(repository.selectedCategories).thenReturn(listOf( - categoryItem("selected", "", "",true))) + whenever(repository.selectedCategories).thenReturn( + listOf( + categoryItem("selected", "", "", true), + ), + ) categoriesPresenter.searchForCategories("test") testScheduler.triggerActions() verify(view).showProgress(true) @@ -123,10 +133,11 @@ class CategoriesPresenterTest { whenever(repository.selectedCategories).thenReturn(listOf()) categoriesPresenter.searchForCategories(query) testScheduler.triggerActions() - val method: Method = CategoriesPresenter::class.java.getDeclaredMethod( - "searchResults", - String::class.java - ) + val method: Method = + CategoriesPresenter::class.java.getDeclaredMethod( + "searchResults", + String::class.java, + ) method.isAccessible = true method.invoke(categoriesPresenter, query) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/DepictsPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/DepictsPresenterTest.kt index a22b3d9691..748b95ea6d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/DepictsPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/DepictsPresenterTest.kt @@ -22,9 +22,7 @@ import org.mockito.MockitoAnnotations import org.powermock.reflect.Whitebox import java.lang.reflect.Method - class DepictsPresenterTest { - @get:Rule var testRule = InstantTaskExecutorRule() @@ -65,14 +63,15 @@ class DepictsPresenterTest { @Test fun `search results emission returns distinct results + selected items without disambiguations`() { - val searchResults = listOf( - depictedItem(id="nonUnique"), - depictedItem(id="nonUnique"), - depictedItem( - instanceOfs = listOf(WikidataDisambiguationItems.CATEGORY.id), - id = "unique" + val searchResults = + listOf( + depictedItem(id = "nonUnique"), + depictedItem(id = "nonUnique"), + depictedItem( + instanceOfs = listOf(WikidataDisambiguationItems.CATEGORY.id), + id = "unique", + ), ) - ) whenever(repository.searchAllEntities("")).thenReturn(Flowable.just(searchResults)) val selectedItem = depictedItem(id = "selected") whenever(repository.selectedDepictions).thenReturn(listOf(selectedItem)) @@ -82,7 +81,6 @@ class DepictsPresenterTest { verify(view).showError(true) } - @Test fun `empty search results with empty term do not show error`() { whenever(repository.searchAllEntities("")).thenReturn(Flowable.just(emptyList())) @@ -151,10 +149,11 @@ class DepictsPresenterTest { fun `Test searchResults when media is null`() { whenever(repository.searchAllEntities("querystring")) .thenReturn(Flowable.just(listOf(depictedItem()))) - val method: Method = DepictsPresenter::class.java.getDeclaredMethod( - "searchResults", - String::class.java - ) + val method: Method = + DepictsPresenter::class.java.getDeclaredMethod( + "searchResults", + String::class.java, + ) method.isAccessible = true method.invoke(depictsPresenter, "querystring") verify(repository, times(1)).searchAllEntities("querystring") @@ -167,10 +166,11 @@ class DepictsPresenterTest { .thenReturn(Flowable.just(listOf(depictedItem()))) whenever(repository.searchAllEntities("querystring")) .thenReturn(Flowable.just(listOf(depictedItem()))) - val method: Method = DepictsPresenter::class.java.getDeclaredMethod( - "searchResults", - String::class.java - ) + val method: Method = + DepictsPresenter::class.java.getDeclaredMethod( + "searchResults", + String::class.java, + ) method.isAccessible = true method.invoke(depictsPresenter, "querystring") verify(repository, times(1)).searchAllEntities("querystring") @@ -179,19 +179,21 @@ class DepictsPresenterTest { @Test fun testSelectNewDepictions() { Whitebox.setInternalState(depictsPresenter, "media", media) - val method: Method = DepictsPresenter::class.java.getDeclaredMethod( - "selectNewDepictions", - List::class.java - ) + val method: Method = + DepictsPresenter::class.java.getDeclaredMethod( + "selectNewDepictions", + List::class.java, + ) method.isAccessible = true method.invoke(depictsPresenter, listOf(depictedItem())) } @Test fun testClearPreviousSelection() { - val method: Method = DepictsPresenter::class.java.getDeclaredMethod( - "clearPreviousSelection" - ) + val method: Method = + DepictsPresenter::class.java.getDeclaredMethod( + "clearPreviousSelection", + ) method.isAccessible = true method.invoke(depictsPresenter) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/FileMetadataUtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/FileMetadataUtilsTest.kt index 1aa0df1679..385189fefe 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/FileMetadataUtilsTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/FileMetadataUtilsTest.kt @@ -1,22 +1,37 @@ package fr.free.nrw.commons.upload -import androidx.exifinterface.media.ExifInterface.* +import androidx.exifinterface.media.ExifInterface.TAG_ARTIST +import androidx.exifinterface.media.ExifInterface.TAG_BODY_SERIAL_NUMBER +import androidx.exifinterface.media.ExifInterface.TAG_CAMERA_OWNER_NAME +import androidx.exifinterface.media.ExifInterface.TAG_COPYRIGHT +import androidx.exifinterface.media.ExifInterface.TAG_GPS_ALTITUDE +import androidx.exifinterface.media.ExifInterface.TAG_GPS_ALTITUDE_REF +import androidx.exifinterface.media.ExifInterface.TAG_GPS_LATITUDE +import androidx.exifinterface.media.ExifInterface.TAG_GPS_LATITUDE_REF +import androidx.exifinterface.media.ExifInterface.TAG_GPS_LONGITUDE +import androidx.exifinterface.media.ExifInterface.TAG_GPS_LONGITUDE_REF +import androidx.exifinterface.media.ExifInterface.TAG_LENS_MAKE +import androidx.exifinterface.media.ExifInterface.TAG_LENS_MODEL +import androidx.exifinterface.media.ExifInterface.TAG_LENS_SERIAL_NUMBER +import androidx.exifinterface.media.ExifInterface.TAG_LENS_SPECIFICATION +import androidx.exifinterface.media.ExifInterface.TAG_MAKE +import androidx.exifinterface.media.ExifInterface.TAG_MODEL +import androidx.exifinterface.media.ExifInterface.TAG_SOFTWARE import org.junit.Assert.assertTrue import org.junit.Test -import java.util.* +import java.util.Arrays /** * Test cases for FileMetadataUtils */ class FileMetadataUtilsTest { - /** * Test method to verify EXIF tags for "Author" */ @Test fun getTagsFromPrefAuthor() { val author = FileMetadataUtils.getTagsFromPref("Author") - val authorRef = arrayOf(TAG_ARTIST, TAG_CAMERA_OWNER_NAME); + val authorRef = arrayOf(TAG_ARTIST, TAG_CAMERA_OWNER_NAME) assertTrue(Arrays.deepEquals(author, authorRef)) } @@ -27,9 +42,15 @@ class FileMetadataUtilsTest { @Test fun getTagsFromPrefLocation() { val author = FileMetadataUtils.getTagsFromPref("Location") - val authorRef = arrayOf(TAG_GPS_LATITUDE, TAG_GPS_LATITUDE_REF, - TAG_GPS_LONGITUDE, TAG_GPS_LONGITUDE_REF, - TAG_GPS_ALTITUDE, TAG_GPS_ALTITUDE_REF) + val authorRef = + arrayOf( + TAG_GPS_LATITUDE, + TAG_GPS_LATITUDE_REF, + TAG_GPS_LONGITUDE, + TAG_GPS_LONGITUDE_REF, + TAG_GPS_ALTITUDE, + TAG_GPS_ALTITUDE_REF, + ) assertTrue(Arrays.deepEquals(author, authorRef)) } @@ -88,4 +109,4 @@ class FileMetadataUtilsTest { assertTrue(Arrays.deepEquals(author, authorRef)) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt index 5d3988826d..1ef103272e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt @@ -19,16 +19,20 @@ class GpsCategoryModelTest { @Test fun `setCategoriesFromLocation emits the new value`() { - val expectedList = listOf( - CategoryItem("category", "", "", false)) - gpsCategoryModel.categoriesFromLocation.test() + val expectedList = + listOf( + CategoryItem("category", "", "", false), + ) + gpsCategoryModel.categoriesFromLocation + .test() .also { gpsCategoryModel.setCategoriesFromLocation(expectedList) } .assertValues(emptyList(), expectedList) } @Test fun `clear emits an empty value`() { - gpsCategoryModel.categoriesFromLocation.test() + gpsCategoryModel.categoriesFromLocation + .test() .also { gpsCategoryModel.clear() } .assertValues(emptyList(), emptyList()) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/ImageProcessingServiceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/ImageProcessingServiceTest.kt index cf720c6a47..30f553e722 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/ImageProcessingServiceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/ImageProcessingServiceTest.kt @@ -13,21 +13,28 @@ import org.junit.Test import org.mockito.ArgumentMatchers import org.mockito.InjectMocks import org.mockito.Mock -import org.mockito.Mockito.* +import org.mockito.Mockito.any +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import java.io.FileInputStream class ImageProcessingServiceTest { @Mock internal var fileUtilsWrapper: FileUtilsWrapper? = null + @Mock internal var imageUtilsWrapper: ImageUtilsWrapper? = null + @Mock - internal var readFBMD: ReadFBMD?=null + internal var readFBMD: ReadFBMD? = null + @Mock - internal var readEXIF: EXIFReader?=null + internal var readEXIF: EXIFReader? = null + @Mock internal var mediaClient: MediaClient? = null + @Mock internal var location: LatLng? = null @@ -60,31 +67,31 @@ class ImageProcessingServiceTest { `when`(uploadItem.fileName).thenReturn("File:jpg") `when`(fileUtilsWrapper!!.getFileInputStream(ArgumentMatchers.anyString())) - .thenReturn(mock(FileInputStream::class.java)) + .thenReturn(mock(FileInputStream::class.java)) `when`(fileUtilsWrapper!!.getSHA1(any(FileInputStream::class.java))) - .thenReturn("fileSha") + .thenReturn("fileSha") `when`(fileUtilsWrapper!!.getGeolocationOfFile(ArgumentMatchers.anyString(), any(LatLng::class.java))) - .thenReturn("latLng") + .thenReturn("latLng") `when`(imageUtilsWrapper?.checkIfImageIsTooDark(ArgumentMatchers.anyString())) - .thenReturn(Single.just(ImageUtils.IMAGE_OK)) + .thenReturn(Single.just(ImageUtils.IMAGE_OK)) `when`(imageUtilsWrapper!!.checkImageGeolocationIsDifferent(ArgumentMatchers.anyString(), any(LatLng::class.java))) - .thenReturn(Single.just(ImageUtils.IMAGE_OK)) + .thenReturn(Single.just(ImageUtils.IMAGE_OK)) `when`(fileUtilsWrapper!!.getFileInputStream(ArgumentMatchers.anyString())) - .thenReturn(mock(FileInputStream::class.java)) + .thenReturn(mock(FileInputStream::class.java)) `when`(fileUtilsWrapper!!.getSHA1(any(FileInputStream::class.java))) - .thenReturn("fileSha") + .thenReturn("fileSha") `when`(mediaClient!!.checkFileExistsUsingSha(ArgumentMatchers.anyString())) - .thenReturn(Single.just(false)) + .thenReturn(Single.just(false)) `when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString())) - .thenReturn(Single.just(false)) + .thenReturn(Single.just(false)) `when`(readFBMD?.processMetadata(ArgumentMatchers.any())) - .thenReturn(Single.just(ImageUtils.IMAGE_OK)) + .thenReturn(Single.just(ImageUtils.IMAGE_OK)) `when`(readEXIF?.processMetadata(ArgumentMatchers.anyString())) - .thenReturn(Single.just(ImageUtils.IMAGE_OK)) + .thenReturn(Single.just(ImageUtils.IMAGE_OK)) } @Test @@ -97,7 +104,7 @@ class ImageProcessingServiceTest { @Test fun validateImageForDuplicateImage() { `when`(mediaClient!!.checkFileExistsUsingSha(ArgumentMatchers.anyString())) - .thenReturn(Single.just(true)) + .thenReturn(Single.just(true)) val validateImage = imageProcessingService!!.validateImage(uploadItem, location) assertEquals(ImageUtils.IMAGE_DUPLICATE, validateImage.blockingGet()) } @@ -111,7 +118,7 @@ class ImageProcessingServiceTest { @Test fun validateImageForDarkImage() { `when`(imageUtilsWrapper?.checkIfImageIsTooDark(ArgumentMatchers.anyString())) - .thenReturn(Single.just(ImageUtils.IMAGE_DARK)) + .thenReturn(Single.just(ImageUtils.IMAGE_DARK)) val validateImage = imageProcessingService!!.validateImage(uploadItem, location) assertEquals(ImageUtils.IMAGE_DARK, validateImage.blockingGet()) } @@ -119,7 +126,7 @@ class ImageProcessingServiceTest { @Test fun validateImageForWrongGeoLocation() { `when`(imageUtilsWrapper!!.checkImageGeolocationIsDifferent(ArgumentMatchers.anyString(), any(LatLng::class.java))) - .thenReturn(Single.just(ImageUtils.IMAGE_GEOLOCATION_DIFFERENT)) + .thenReturn(Single.just(ImageUtils.IMAGE_GEOLOCATION_DIFFERENT)) val validateImage = imageProcessingService!!.validateImage(uploadItem, location) assertEquals(ImageUtils.IMAGE_GEOLOCATION_DIFFERENT, validateImage.blockingGet()) } @@ -127,7 +134,7 @@ class ImageProcessingServiceTest { @Test fun validateImageForFileNameExistsWithCheckTitleOn() { `when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString())) - .thenReturn(Single.just(true)) + .thenReturn(Single.just(true)) val validateImage = imageProcessingService!!.validateCaption(uploadItem) assertEquals(ImageUtils.FILE_NAME_EXISTS, validateImage.blockingGet()) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/LanguagesAdapterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/LanguagesAdapterTest.kt index 3ba3f8dc0b..801d4e9005 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/LanguagesAdapterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/LanguagesAdapterTest.kt @@ -8,6 +8,7 @@ import androidx.core.os.ConfigurationCompat import androidx.test.core.app.ApplicationProvider import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.language.AppLanguageLookUpTable import org.junit.Assert import org.junit.Before import org.junit.Test @@ -19,18 +20,18 @@ import org.mockito.MockitoAnnotations import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode -import fr.free.nrw.commons.language.AppLanguageLookUpTable -import java.util.* +import java.util.Collections +import java.util.Locale @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class LanguagesAdapterTest { - private lateinit var context: Context @Mock private lateinit var selectedLanguages: HashMap + @Mock private lateinit var parent: ViewGroup @@ -48,8 +49,10 @@ class LanguagesAdapterTest { MockitoAnnotations.openMocks(this) context = ApplicationProvider.getApplicationContext() language = AppLanguageLookUpTable(context) - convertView = LayoutInflater.from(context) - .inflate(R.layout.row_item_languages_spinner, null) as View + convertView = + LayoutInflater + .from(context) + .inflate(R.layout.row_item_languages_spinner, null) as View languageNamesList = language.localizedNames languageCodesList = language.codes @@ -79,10 +82,14 @@ class LanguagesAdapterTest { @Test fun testGetIndexOfUserDefaultLocale() { languagesAdapter = LanguagesAdapter(context, selectedLanguages) - Assertions.assertEquals(ConfigurationCompat.getLocales(context.resources.configuration)[0]?.let { - languageCodesList.indexOf( - it.language) - }, languagesAdapter.getIndexOfUserDefaultLocale(context)) + Assertions.assertEquals( + ConfigurationCompat.getLocales(context.resources.configuration)[0]?.let { + languageCodesList.indexOf( + it.language, + ) + }, + languagesAdapter.getIndexOfUserDefaultLocale(context), + ) } @Test @@ -108,21 +115,27 @@ class LanguagesAdapterTest { val constraint = "spa" languagesAdapter.filter.filter(constraint) val length: Int = languageNamesList.size - val defaultlanguagecode = ConfigurationCompat.getLocales(context.resources.configuration)[0]?.let { - languageCodesList.indexOf( - it.language) - } + val defaultlanguagecode = + ConfigurationCompat.getLocales(context.resources.configuration)[0]?.let { + languageCodesList.indexOf( + it.language, + ) + } var i = 0 var s = 0 while (i < length) { val key: String = language.codes[i] val value: String = language.localizedNames[i] - if(value.contains(constraint, true) || Locale(key).getDisplayName( - Locale(language.codes[defaultlanguagecode!!])).contains(constraint, true)) + if (value.contains(constraint, true) || + Locale(key) + .getDisplayName( + Locale(language.codes[defaultlanguagecode!!]), + ).contains(constraint, true) + ) { s++ + } i++ } Assertions.assertEquals(s, languagesAdapter.count) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt index d71593caaf..68b1c95cb3 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt @@ -17,9 +17,7 @@ import org.mockito.MockedStatic import org.mockito.Mockito import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import org.powermock.api.mockito.PowerMockito import org.powermock.core.classloader.annotations.PrepareForTest -import org.powermock.modules.junit4.PowerMockRunner import org.robolectric.RobolectricTestRunner /** @@ -60,7 +58,6 @@ class MediaLicensePresenterTest { mockedUtil.close() } - /** * unit test case for method MediaLicensePresenter.getLicense */ diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt index b406dd09ee..d15fe06e5f 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt @@ -9,8 +9,8 @@ import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.contributions.ContributionController +import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.filepicker.UploadableFile import fr.free.nrw.commons.upload.categories.UploadCategoriesFragment import fr.free.nrw.commons.upload.license.MediaLicenseFragment @@ -28,12 +28,10 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import java.lang.reflect.Method - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class UploadActivityUnitTests { - private lateinit var activity: UploadActivity private lateinit var context: Context @@ -83,9 +81,10 @@ class UploadActivityUnitTests { @Test @Throws(Exception::class) fun testOnResume() { - val method: Method = UploadActivity::class.java.getDeclaredMethod( - "onResume" - ) + val method: Method = + UploadActivity::class.java.getDeclaredMethod( + "onResume", + ) method.isAccessible = true method.invoke(activity) } @@ -93,9 +92,10 @@ class UploadActivityUnitTests { @Test @Throws(Exception::class) fun testOnStop() { - val method: Method = UploadActivity::class.java.getDeclaredMethod( - "onStop" - ) + val method: Method = + UploadActivity::class.java.getDeclaredMethod( + "onStop", + ) method.isAccessible = true method.invoke(activity) } @@ -170,12 +170,13 @@ class UploadActivityUnitTests { @Test @Throws(Exception::class) fun testOnActivityResult() { - val method: Method = UploadActivity::class.java.getDeclaredMethod( - "onActivityResult", - Int::class.java, - Int::class.java, - Intent::class.java - ) + val method: Method = + UploadActivity::class.java.getDeclaredMethod( + "onActivityResult", + Int::class.java, + Int::class.java, + Intent::class.java, + ) method.isAccessible = true method.invoke(activity, CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS, 0, Intent()) } @@ -183,9 +184,10 @@ class UploadActivityUnitTests { @Test @Throws(Exception::class) fun testReceiveSharedItems() { - val method: Method = UploadActivity::class.java.getDeclaredMethod( - "receiveSharedItems" - ) + val method: Method = + UploadActivity::class.java.getDeclaredMethod( + "receiveSharedItems", + ) method.isAccessible = true method.invoke(activity) } @@ -193,9 +195,10 @@ class UploadActivityUnitTests { @Test @Throws(Exception::class) fun testReceiveExternalSharedItems() { - val method: Method = UploadActivity::class.java.getDeclaredMethod( - "receiveExternalSharedItems" - ) + val method: Method = + UploadActivity::class.java.getDeclaredMethod( + "receiveExternalSharedItems", + ) method.isAccessible = true method.invoke(activity) } @@ -203,9 +206,10 @@ class UploadActivityUnitTests { @Test @Throws(Exception::class) fun testReceiveInternalSharedItems() { - val method: Method = UploadActivity::class.java.getDeclaredMethod( - "receiveInternalSharedItems" - ) + val method: Method = + UploadActivity::class.java.getDeclaredMethod( + "receiveInternalSharedItems", + ) method.isAccessible = true method.invoke(activity) } @@ -225,9 +229,10 @@ class UploadActivityUnitTests { @Test @Throws(Exception::class) fun testHandleNullMedia() { - val method: Method = UploadActivity::class.java.getDeclaredMethod( - "handleNullMedia" - ) + val method: Method = + UploadActivity::class.java.getDeclaredMethod( + "handleNullMedia", + ) method.isAccessible = true method.invoke(activity) } @@ -256,16 +261,19 @@ class UploadActivityUnitTests { Whitebox.setInternalState( activity, "mediaLicenseFragment", - mock(MediaLicenseFragment::class.java) + mock(MediaLicenseFragment::class.java), ) Whitebox.setInternalState( - activity, "uploadCategoriesFragment", mock( - UploadCategoriesFragment::class.java - ) - ) - val method: Method = UploadActivity::class.java.getDeclaredMethod( - "onDestroy" + activity, + "uploadCategoriesFragment", + mock( + UploadCategoriesFragment::class.java, + ), ) + val method: Method = + UploadActivity::class.java.getDeclaredMethod( + "onDestroy", + ) method.isAccessible = true method.invoke(activity) } @@ -273,11 +281,11 @@ class UploadActivityUnitTests { @Test @Throws(Exception::class) fun testOnBackPressed() { - val method: Method = UploadActivity::class.java.getDeclaredMethod( - "onBackPressed" - ) + val method: Method = + UploadActivity::class.java.getDeclaredMethod( + "onBackPressed", + ) method.isAccessible = true method.invoke(activity) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadClientTest.kt index 4757d52583..97aac88fe6 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadClientTest.kt @@ -9,7 +9,6 @@ import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.times -import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.CommonsApplication.DEFAULT_EDIT_SUMMARY import fr.free.nrw.commons.auth.csrf.CsrfTokenClient @@ -22,24 +21,17 @@ import fr.free.nrw.commons.wikidata.mwapi.MwServiceError import io.reactivex.Observable import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertSame -import kotlinx.coroutines.runBlocking import okhttp3.MediaType.Companion.toMediaType import okhttp3.MultipartBody import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody import okio.Buffer -import org.junit.Assert import org.junit.Before -import org.junit.Ignore import org.junit.Test -import org.junit.jupiter.api.assertThrows -import org.junit.platform.commons.annotation.Testable import java.io.File import java.util.Date - class UploadClientTest { - private val contribution = mock() private val uploadResult = mock() private val uploadInterface = mock() @@ -49,15 +41,16 @@ class UploadClientTest { private val gson = mock() private val contributionDao = mock { } private val timeProvider = mock() - private val uploadClient = UploadClient( - uploadInterface, - csrfTokenClient, - pageContentsCreator, - fileUtilsWrapper, - gson, - timeProvider, - contributionDao - ) + private val uploadClient = + UploadClient( + uploadInterface, + csrfTokenClient, + pageContentsCreator, + fileUtilsWrapper, + gson, + timeProvider, + contributionDao, + ) private val expectedChunkSize = 512 * 1024 private val testToken = "test-token" @@ -86,8 +79,8 @@ class UploadClientTest { createdContent, DEFAULT_EDIT_SUMMARY, filename, - filekey - ) + filekey, + ), ).thenReturn(Observable.just(uploadJson)) val result = uploadClient.uploadFileFromStash(contribution, filename, filekey).test() @@ -110,8 +103,8 @@ class UploadClientTest { createdContent, DEFAULT_EDIT_SUMMARY, filename, - filekey - ) + filekey, + ), ).thenReturn(Observable.just(uploadJson)) val result = uploadClient.uploadFileFromStash(contribution, filename, filekey).test() @@ -129,10 +122,9 @@ class UploadClientTest { createdContent, DEFAULT_EDIT_SUMMARY, filename, - filekey - ) - ) - .thenReturn(Observable.error(exception)) + filekey, + ), + ).thenReturn(Observable.error(exception)) val result = uploadClient.uploadFileFromStash(contribution, filename, filekey).test() @@ -156,9 +148,13 @@ class UploadClientTest { whenever( uploadInterface.uploadFileToStash( - filenameCaptor.capture(), totalFileSizeCaptor.capture(), offsetCaptor.capture(), - fileKeyCaptor.capture(), tokenCaptor.capture(), fileCaptor.capture() - ) + filenameCaptor.capture(), + totalFileSizeCaptor.capture(), + offsetCaptor.capture(), + fileKeyCaptor.capture(), + tokenCaptor.capture(), + fileCaptor.capture(), + ), ).thenReturn(Observable.just(uploadResponse)) val result = @@ -211,7 +207,7 @@ class UploadClientTest { whenever(contribution.localUriPath).thenReturn(tempFile) whenever(fileUtilsWrapper.getMimeType(anyOrNull())).thenReturn("image/png") whenever(fileUtilsWrapper.getFileChunks(anyOrNull(), eq(expectedChunkSize))).thenReturn(emptyList()) - val result = uploadClient.uploadFileToStash(filename, contribution, mock() ).test() + val result = uploadClient.uploadFileToStash(filename, contribution, mock()).test() result.assertNoErrors() assertEquals(StashUploadState.FAILED, result.values()[0].state) } @@ -229,8 +225,8 @@ class UploadClientTest { whenever( fileUtilsWrapper.getFileChunks( anyOrNull(), - eq(expectedChunkSize) - ) + eq(expectedChunkSize), + ), ).thenReturn(listOf(mockFile)) whenever( uploadInterface.uploadFileToStash( @@ -239,8 +235,8 @@ class UploadClientTest { any(), any(), any(), - any() - ) + any(), + ), ).thenReturn(Observable.just(uploadResponse)) val result = uploadClient.uploadFileToStash(filename, contribution, mock()).test() @@ -272,15 +268,19 @@ class UploadClientTest { whenever( fileUtilsWrapper.getFileChunks( anyOrNull(), - eq(expectedChunkSize) - ) + eq(expectedChunkSize), + ), ).thenReturn(listOf(mockFile)) whenever( uploadInterface.uploadFileToStash( - anyOrNull(), anyOrNull(), anyOrNull(), - anyOrNull(), anyOrNull(), anyOrNull() - ) + anyOrNull(), + anyOrNull(), + anyOrNull(), + anyOrNull(), + anyOrNull(), + anyOrNull(), + ), ).thenReturn(Observable.just(uploadResponse)) val result = uploadClient.uploadFileToStash(filename, contribution, mock()).test() @@ -290,13 +290,11 @@ class UploadClientTest { assertEquals(filekey, result.values()[0].fileKey) } - - private fun KArgumentCaptor.asString(): String = - firstValue.asString() + private fun KArgumentCaptor.asString(): String = firstValue.asString() private fun RequestBody.asString(): String { val b = Buffer() writeTo(b) return b.readUtf8() } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailAdapterUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailAdapterUnitTest.kt index c1547df779..db5b20f7e6 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailAdapterUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailAdapterUnitTest.kt @@ -16,7 +16,6 @@ import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.recentlanguages.Language import fr.free.nrw.commons.recentlanguages.RecentLanguagesAdapter import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao -import fr.free.nrw.commons.settings.SettingsFragment import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment import org.junit.Assert import org.junit.Before @@ -38,7 +37,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class UploadMediaDetailAdapterUnitTest { - private lateinit var adapter: UploadMediaDetailAdapter private lateinit var context: Context private lateinit var viewHolder: UploadMediaDetailAdapter.ViewHolder @@ -58,7 +56,7 @@ class UploadMediaDetailAdapterUnitTest { private lateinit var textView: TextView @Mock - private lateinit var fragment : UploadMediaDetailFragment + private lateinit var fragment: UploadMediaDetailFragment @Mock private lateinit var view: View @@ -75,7 +73,7 @@ class UploadMediaDetailAdapterUnitTest { uploadMediaDetails = mutableListOf(uploadMediaDetail, uploadMediaDetail) activity = Robolectric.buildActivity(UploadActivity::class.java).get() fragment = mock(UploadMediaDetailFragment::class.java) - adapter = UploadMediaDetailAdapter(fragment,"", recentLanguagesDao) + adapter = UploadMediaDetailAdapter(fragment, "", recentLanguagesDao) context = ApplicationProvider.getApplicationContext() Whitebox.setInternalState(adapter, "uploadMediaDetails", uploadMediaDetails) Whitebox.setInternalState(adapter, "eventListener", eventListener) @@ -184,9 +182,10 @@ class UploadMediaDetailAdapterUnitTest { @Test fun testHideRecentLanguagesSection() { - val method: Method = UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod( - "hideRecentLanguagesSection" - ) + val method: Method = + UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod( + "hideRecentLanguagesSection", + ) method.isAccessible = true method.invoke(viewHolder) verify(listView, times(1)).visibility = View.GONE @@ -196,10 +195,11 @@ class UploadMediaDetailAdapterUnitTest { @Test fun `Test setUpRecentLanguagesSection when list is empty`() { - val method: Method = UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod( - "setUpRecentLanguagesSection", - List::class.java - ) + val method: Method = + UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod( + "setUpRecentLanguagesSection", + List::class.java, + ) method.isAccessible = true method.invoke(viewHolder, emptyList()) verify(listView, times(1)).visibility = View.GONE @@ -209,19 +209,23 @@ class UploadMediaDetailAdapterUnitTest { @Test fun `Test setUpRecentLanguagesSection when list is not empty`() { - val method: Method = UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod( - "setUpRecentLanguagesSection", - List::class.java - ) + val method: Method = + UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod( + "setUpRecentLanguagesSection", + List::class.java, + ) method.isAccessible = true - method.invoke(viewHolder, listOf( - Language("Bengali", "bn"), - Language("Bengali", "bn"), - Language("Bengali", "bn"), - Language("Bengali", "bn"), - Language("Bengali", "bn"), - Language("Bengali", "bn") - )) + method.invoke( + viewHolder, + listOf( + Language("Bengali", "bn"), + Language("Bengali", "bn"), + Language("Bengali", "bn"), + Language("Bengali", "bn"), + Language("Bengali", "bn"), + Language("Bengali", "bn"), + ), + ) verify(listView, times(1)).visibility = View.VISIBLE verify(view, times(1)).visibility = View.VISIBLE verify(textView, times(1)).visibility = View.VISIBLE @@ -234,21 +238,28 @@ class UploadMediaDetailAdapterUnitTest { .thenReturn(true) whenever(adapterView.adapter) .thenReturn( - RecentLanguagesAdapter(context, - listOf(Language("English", "en")), - hashMapOf() - ) + RecentLanguagesAdapter( + context, + listOf(Language("English", "en")), + hashMapOf(), + ), + ) + val method: Method = + UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod( + "onRecentLanguageClicked", + Dialog::class.java, + AdapterView::class.java, + Int::class.java, + UploadMediaDetail::class.java, ) - val method: Method = UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod( - "onRecentLanguageClicked", - Dialog::class.java, - AdapterView::class.java, - Int::class.java, - UploadMediaDetail::class.java - ) method.isAccessible = true - method.invoke(viewHolder, Mockito.mock(Dialog::class.java), adapterView, 0, - Mockito.mock(UploadMediaDetail::class.java)) + method.invoke( + viewHolder, + Mockito.mock(Dialog::class.java), + adapterView, + 0, + Mockito.mock(UploadMediaDetail::class.java), + ) verify(recentLanguagesDao, times(1)).findRecentLanguage(any()) verify(adapterView, times(3)).adapter } @@ -265,13 +276,13 @@ class UploadMediaDetailAdapterUnitTest { Assert.assertEquals(expected2, viewHolder.removeLeadingAndTrailingWhitespace(test2)) // No whitespace - val test3 = "No trailing space"; - val expected3 = "No trailing space"; + val test3 = "No trailing space" + val expected3 = "No trailing space" Assert.assertEquals(expected3, viewHolder.removeLeadingAndTrailingWhitespace(test3)) // blank string val test4 = " \r \t " - val expected4 = ""; + val expected4 = "" Assert.assertEquals(expected4, viewHolder.removeLeadingAndTrailingWhitespace(test4)) } @@ -293,10 +304,10 @@ class UploadMediaDetailAdapterUnitTest { fun testCaptionJapaneseCharacters() { val test1 = "テスト テスト" val expected1 = "テスト テスト" - Assert.assertEquals(expected1, viewHolder.convertIdeographicSpaceToLatinSpace(test1)); + Assert.assertEquals(expected1, viewHolder.convertIdeographicSpaceToLatinSpace(test1)) val test2 = " \r \t テスト \r \t " val expected2 = "テスト" Assert.assertEquals(expected2, viewHolder.removeLeadingAndTrailingWhitespace(test2)) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailInputFilterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailInputFilterTest.kt index 90c5e60423..efd1c832b0 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailInputFilterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaDetailInputFilterTest.kt @@ -13,7 +13,6 @@ import org.robolectric.annotation.LooperMode @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class UploadMediaDetailInputFilterTest { - @Test fun testFilterGeneric() { val filter = UploadMediaDetailInputFilter() @@ -32,7 +31,7 @@ class UploadMediaDetailInputFilterTest { val builder = SpannableStringBuilder("") builder.filters = arrayOf(UploadMediaDetailInputFilter()) - //All unusual space characters + // All unusual space characters val tests = intArrayOf(0x00A0, 0x1680, 0x180E, 0x2000, 0x2005, 0x200B, 0x2028, 0x2029, 0x202F, 0x205F) for (test: Int in tests) { builder.insert(0, String(Character.toChars(test))) @@ -46,7 +45,7 @@ class UploadMediaDetailInputFilterTest { val builder = SpannableStringBuilder("") builder.filters = arrayOf(UploadMediaDetailInputFilter()) - //Sample of BiDI override characters + // Sample of BiDI override characters val tests = intArrayOf(0x202A, 0x202B, 0x202C, 0x202D, 0x202E) for (test: Int in tests) { builder.insert(0, String(Character.toChars(test))) @@ -60,7 +59,7 @@ class UploadMediaDetailInputFilterTest { val builder = SpannableStringBuilder("") builder.filters = arrayOf(UploadMediaDetailInputFilter()) - //Sample of control characters + // Sample of control characters val tests = intArrayOf(0x00, 0x08, 0x10, 0x18, 0x1F, 0x7F, 0x3A) for (test: Int in tests) { builder.insert(0, String(Character.toChars(test))) @@ -94,7 +93,7 @@ class UploadMediaDetailInputFilterTest { val builder = SpannableStringBuilder("") builder.filters = arrayOf(UploadMediaDetailInputFilter()) - //Sample of surrogate and special characters + // Sample of surrogate and special characters val tests = intArrayOf(0xE000, 0xE63F, 0xEC7E, 0xF2BD, 0xF8FF, 0xFFF0, 0xFFF4, 0xFFFC, 0xFFFF) for (test: Int in tests) { builder.insert(0, String(Character.toChars(test))) @@ -108,7 +107,7 @@ class UploadMediaDetailInputFilterTest { val builder = SpannableStringBuilder("") builder.filters = arrayOf(UploadMediaDetailInputFilter()) - //Sample of characters over 5 hex places not in the Han set + // Sample of characters over 5 hex places not in the Han set val testsExclude = intArrayOf(0x1FFFF, 0x44444, 0xFFFFF) for (test: Int in testsExclude) { builder.insert(0, String(Character.toChars(test))) @@ -116,7 +115,7 @@ class UploadMediaDetailInputFilterTest { builder.clear() } - //Sample of characters over 5 hex places in the Han set + // Sample of characters over 5 hex places in the Han set val testsInclude = intArrayOf(0x20000, 0x2B740, 0x2F800) val expected = SpannableStringBuilder("") for (test: Int in testsInclude) { @@ -125,4 +124,4 @@ class UploadMediaDetailInputFilterTest { } Assert.assertEquals(builder.toString(), expected.toString()) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaPresenterTest.kt index d2b6a49480..bf27280aae 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadMediaPresenterTest.kt @@ -10,7 +10,9 @@ import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.repository.UploadRepository import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailsContract import fr.free.nrw.commons.upload.mediaDetails.UploadMediaPresenter -import fr.free.nrw.commons.utils.ImageUtils.* +import fr.free.nrw.commons.utils.ImageUtils.EMPTY_CAPTION +import fr.free.nrw.commons.utils.ImageUtils.FILE_NAME_EXISTS +import fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK import io.github.coordinates2country.Coordinates2Country import io.reactivex.Observable import io.reactivex.Single @@ -20,13 +22,17 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.* +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.Mock +import org.mockito.MockedStatic +import org.mockito.Mockito import org.mockito.Mockito.mockStatic import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations import org.powermock.core.classloader.annotations.PrepareForTest import org.robolectric.RobolectricTestRunner -import java.util.* - +import java.util.Collections /** * The class contains unit test cases for UploadMediaPresenter @@ -82,9 +88,13 @@ class UploadMediaPresenterTest { testObservableUploadItem = Observable.just(uploadItem) testSingleImageResult = Single.just(1) testScheduler = TestScheduler() - uploadMediaPresenter = UploadMediaPresenter( - repository, jsonKvStore, testScheduler, testScheduler - ) + uploadMediaPresenter = + UploadMediaPresenter( + repository, + jsonKvStore, + testScheduler, + testScheduler, + ) uploadMediaPresenter.onAttachView(view) mockedCountry = mockStatic(Coordinates2Country::class.java) } @@ -94,7 +104,6 @@ class UploadMediaPresenterTest { mockedCountry.close() } - /** * unit test for method UploadMediaPresenter.receiveImage */ @@ -105,15 +114,15 @@ class UploadMediaPresenterTest { ArgumentMatchers.any(UploadableFile::class.java), ArgumentMatchers.any(Place::class.java), ArgumentMatchers.any(UploadMediaPresenter::class.java), - ArgumentMatchers.any(LatLng::class.java) - ) + ArgumentMatchers.any(LatLng::class.java), + ), ).thenReturn(testObservableUploadItem) uploadMediaPresenter.receiveImage(uploadableFile, place, location) verify(view).showProgress(true) testScheduler.triggerActions() verify(view).onImageProcessed( ArgumentMatchers.any(UploadItem::class.java), - ArgumentMatchers.any(Place::class.java) + ArgumentMatchers.any(Place::class.java), ) } @@ -157,7 +166,7 @@ class UploadMediaPresenterTest { */ @Test fun emptyFileNameTest() { - uploadMediaPresenter.handleCaptionResult(EMPTY_CAPTION, uploadItem); + uploadMediaPresenter.handleCaptionResult(EMPTY_CAPTION, uploadItem) verify(view).showMessage(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()) } @@ -237,7 +246,6 @@ class UploadMediaPresenterTest { @Test fun setCorrectCountryCodeForReceivedImage() { - val germanyAsPlace = Place(null, null, null, null, LatLng(50.1, 10.2, 1.0f), null, null, null, true, null) germanyAsPlace.isMonument = true @@ -245,8 +253,8 @@ class UploadMediaPresenterTest { whenever( Coordinates2Country.country( ArgumentMatchers.eq(germanyAsPlace.getLocation().latitude), - ArgumentMatchers.eq(germanyAsPlace.getLocation().longitude) - ) + ArgumentMatchers.eq(germanyAsPlace.getLocation().longitude), + ), ).thenReturn("Germany") val item: Observable = @@ -257,8 +265,8 @@ class UploadMediaPresenterTest { ArgumentMatchers.any(UploadableFile::class.java), ArgumentMatchers.any(Place::class.java), ArgumentMatchers.any(UploadMediaPresenter::class.java), - ArgumentMatchers.any(LatLng::class.java) - ) + ArgumentMatchers.any(LatLng::class.java), + ), ).thenReturn(item) uploadMediaPresenter.receiveImage(uploadableFile, germanyAsPlace, location) @@ -268,9 +276,9 @@ class UploadMediaPresenterTest { val captor: ArgumentCaptor = ArgumentCaptor.forClass(UploadItem::class.java) verify(view).onImageProcessed( captor.capture(), - ArgumentMatchers.any(Place::class.java) + ArgumentMatchers.any(Place::class.java), ) - assertEquals("Exptected contry code", "de", captor.value.countryCode); + assertEquals("Exptected contry code", "de", captor.value.countryCode) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelUnitTest.kt index c782917317..fb1f07e338 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelUnitTest.kt @@ -12,41 +12,43 @@ import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations class UploadModelUnitTest { - private lateinit var uploadModel: UploadModel @Before fun setUp() { MockitoAnnotations.openMocks(this) - uploadModel = UploadModel( - listOf(), - mock(JsonKvStore::class.java), - mapOf(), - mock(Context::class.java), - mock(SessionManager::class.java), - mock(FileProcessor::class.java), - mock(ImageProcessingService::class.java) - ) + uploadModel = + UploadModel( + listOf(), + mock(JsonKvStore::class.java), + mapOf(), + mock(Context::class.java), + mock(SessionManager::class.java), + mock(FileProcessor::class.java), + mock(ImageProcessingService::class.java), + ) } @Ignore @Test - fun `Test onDepictItemClicked when DepictedItem is selected`(){ + fun `Test onDepictItemClicked when DepictedItem is selected`() { uploadModel.onDepictItemClicked( DepictedItem( - "Test", + "Test", "Test", "test", listOf(), listOf(), true, - "depictionId" - ), media(filename = "File:Example.jpg")) + "depictionId", + ), + media(filename = "File:Example.jpg"), + ) } @Ignore @Test - fun `Test onDepictItemClicked when DepictedItem is not selected`(){ + fun `Test onDepictItemClicked when DepictedItem is not selected`() { uploadModel.onDepictItemClicked( DepictedItem( "Test", @@ -55,14 +57,15 @@ class UploadModelUnitTest { listOf(), listOf(), false, - "depictionId" - ), media(filename = "File:Example.jpg") + "depictionId", + ), + media(filename = "File:Example.jpg"), ) } @Ignore @Test - fun `Test onDepictItemClicked when DepictedItem is not selected and not included in media`(){ + fun `Test onDepictItemClicked when DepictedItem is not selected and not included in media`() { uploadModel.onDepictItemClicked( DepictedItem( "Test", @@ -71,14 +74,15 @@ class UploadModelUnitTest { listOf(), listOf(), false, - "id" - ), media(filename = "File:Example.jpg") + "id", + ), + media(filename = "File:Example.jpg"), ) } @Ignore @Test - fun `Test onDepictItemClicked when media is null and DepictedItem is not selected`(){ + fun `Test onDepictItemClicked when media is null and DepictedItem is not selected`() { uploadModel.onDepictItemClicked( DepictedItem( "Test", @@ -87,13 +91,15 @@ class UploadModelUnitTest { listOf(), listOf(), false, - "id" - ), null) + "id", + ), + null, + ) } @Ignore @Test - fun `Test onDepictItemClicked when media is not null and DepictedItem is selected`(){ + fun `Test onDepictItemClicked when media is not null and DepictedItem is selected`() { uploadModel.onDepictItemClicked( DepictedItem( "Test", @@ -102,13 +108,15 @@ class UploadModelUnitTest { listOf(), listOf(), true, - "id" - ), media(filename = "File:Example.jpg")) + "id", + ), + media(filename = "File:Example.jpg"), + ) } @Ignore @Test - fun `Test onDepictItemClicked when media is null and DepictedItem is selected`(){ + fun `Test onDepictItemClicked when media is null and DepictedItem is selected`() { uploadModel.onDepictItemClicked( DepictedItem( "Test", @@ -117,19 +125,21 @@ class UploadModelUnitTest { listOf(), listOf(), true, - "id" - ), null) + "id", + ), + null, + ) } @Ignore @Test - fun testGetSelectedExistingDepictions(){ + fun testGetSelectedExistingDepictions() { uploadModel.selectedExistingDepictions } @Ignore @Test - fun testSetSelectedExistingDepictions(){ + fun testSetSelectedExistingDepictions() { uploadModel.selectedExistingDepictions = listOf("") } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt index ac6fb3f9d9..bbaaaec1cf 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt @@ -6,7 +6,6 @@ import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.filepicker.UploadableFile import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.repository.UploadRepository -import fr.free.nrw.commons.upload.ImageCoordinates import io.reactivex.Observable import org.junit.Before import org.junit.Ignore @@ -14,21 +13,20 @@ import org.junit.Test import org.mockito.ArgumentMatchers import org.mockito.InjectMocks import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.times +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import java.util.* - /** * The clas contains unit test cases for UploadPresenter */ class UploadPresenterTest { - @Mock internal lateinit var repository: UploadRepository + @Mock internal lateinit var view: UploadContract.View + @Mock lateinit var contribution: Contribution @@ -43,6 +41,7 @@ class UploadPresenterTest { @Mock private lateinit var imageCoords: ImageCoordinates + @Mock private lateinit var uploadItem: UploadItem @@ -90,16 +89,16 @@ class UploadPresenterTest { // test 1 - insufficient count `when`( - defaultKvStore.getInt(UploadPresenter.COUNTER_OF_CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES, 0)) - .thenReturn(UploadPresenter.CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES_REMINDER_THRESHOLD - 1) + defaultKvStore.getInt(UploadPresenter.COUNTER_OF_CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES, 0), + ).thenReturn(UploadPresenter.CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES_REMINDER_THRESHOLD - 1) uploadPresenter.handleSubmit() // no alert dialog expected as insufficient consecutive count verify(view, times(0)).showAlertDialog(ArgumentMatchers.anyInt(), ArgumentMatchers.any()) // test 2 - sufficient count `when`( - defaultKvStore.getInt(UploadPresenter.COUNTER_OF_CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES, 0)) - .thenReturn(UploadPresenter.CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES_REMINDER_THRESHOLD) + defaultKvStore.getInt(UploadPresenter.COUNTER_OF_CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES, 0), + ).thenReturn(UploadPresenter.CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES_REMINDER_THRESHOLD) uploadPresenter.handleSubmit() // alert dialog expected as consecutive count is at threshold verify(view).showAlertDialog(ArgumentMatchers.anyInt(), ArgumentMatchers.any()) @@ -109,8 +108,8 @@ class UploadPresenterTest { @Test fun handleSubmitImagesWithLocationWithConsecutiveNoLocationUploads() { `when`( - defaultKvStore.getInt(UploadPresenter.COUNTER_OF_CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES, 0)) - .thenReturn(UploadPresenter.CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES_REMINDER_THRESHOLD) + defaultKvStore.getInt(UploadPresenter.COUNTER_OF_CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES, 0), + ).thenReturn(UploadPresenter.CONSECUTIVE_UPLOADS_WITHOUT_COORDINATES_REMINDER_THRESHOLD) `when`(imageCoords.imageCoordsExists).thenReturn(true) `when`(uploadItem.getGpsCoords()).thenReturn(imageCoords) `when`(repository.uploads).thenReturn(uploadableItems) @@ -128,8 +127,9 @@ class UploadPresenterTest { defaultKvStore .getBoolean( CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED, - false - )).thenReturn(true) + false, + ), + ).thenReturn(true) `when`(view.isLoggedIn).thenReturn(true) uploadPresenter.handleSubmit() verify(view).isLoggedIn @@ -148,10 +148,9 @@ class UploadPresenterTest { uploadPresenter.handleSubmit() verify(view).isLoggedIn verify(view).askUserToLogIn() - } - private fun deletePictureBaseTest(){ + private fun deletePictureBaseTest() { uploadableFiles.clear() } @@ -160,7 +159,7 @@ class UploadPresenterTest { */ @Ignore @Test - fun hideTopCardWhenReachedTheLastFile(){ + fun hideTopCardWhenReachedTheLastFile() { deletePictureBaseTest() uploadableFiles.add(uploadableFile) uploadPresenter.deletePictureAtIndex(0) @@ -172,12 +171,13 @@ class UploadPresenterTest { */ @Ignore @Test - fun testDeleteWhenSingleUpload(){ + fun testDeleteWhenSingleUpload() { deletePictureBaseTest() uploadableFiles.add(uploadableFile) uploadPresenter.deletePictureAtIndex(0) verify(repository).deletePicture(ArgumentMatchers.anyString()) - verify(view).showMessage(ArgumentMatchers.anyInt())//As there is only one while which we are asking for deletion, upload should be cancelled and this flow should be triggered + // As there is only one while which we are asking for deletion, upload should be cancelled and this flow should be triggered + verify(view).showMessage(ArgumentMatchers.anyInt()) verify(view).finish() } @@ -186,7 +186,7 @@ class UploadPresenterTest { */ @Ignore @Test - fun testDeleteWhenMultipleFilesUpload(){ + fun testDeleteWhenMultipleFilesUpload() { deletePictureBaseTest() uploadableFiles.add(uploadableFile) uploadableFiles.add(anotherUploadableFile) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadRepositoryUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadRepositoryUnitTest.kt index 75c5117d34..54d35494a3 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadRepositoryUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadRepositoryUnitTest.kt @@ -17,17 +17,15 @@ import fr.free.nrw.commons.upload.structure.depictions.DepictedItem import io.reactivex.Completable import io.reactivex.Single import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.junit.jupiter.api.Assertions.assertEquals import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import java.lang.reflect.Method class UploadRepositoryUnitTest { - private lateinit var repository: UploadRepository @Mock @@ -84,14 +82,15 @@ class UploadRepositoryUnitTest { @Before fun setUp() { MockitoAnnotations.openMocks(this) - repository = UploadRepository( - uploadModel, - uploadController, - categoriesModel, - nearbyPlaces, - depictModel, - contributionDao - ) + repository = + UploadRepository( + uploadModel, + uploadController, + categoriesModel, + nearbyPlaces, + depictModel, + contributionDao, + ) `when`(contributionDao.save(contribution)).thenReturn(completable) } @@ -104,7 +103,7 @@ class UploadRepositoryUnitTest { fun testPrepareMedia() { assertEquals( repository.prepareMedia(contribution), - uploadController.prepareMedia(contribution) + uploadController.prepareMedia(contribution), ) } @@ -112,7 +111,7 @@ class UploadRepositoryUnitTest { fun testSaveContribution() { assertEquals( repository.saveContribution(contribution), - contributionDao.save(contribution).blockingAwait() + contributionDao.save(contribution).blockingAwait(), ) } @@ -138,7 +137,7 @@ class UploadRepositoryUnitTest { fun testSearchAll() { assertEquals( repository.searchAll("", listOf(), listOf()), - categoriesModel.searchAll("", listOf(), listOf()) + categoriesModel.searchAll("", listOf(), listOf()), ) } @@ -157,7 +156,8 @@ class UploadRepositoryUnitTest { @Test fun testContainsYear() { assertEquals( - repository.isSpammyCategory(""), categoriesModel.isSpammyCategory("") + repository.isSpammyCategory(""), + categoriesModel.isSpammyCategory(""), ) } @@ -180,7 +180,7 @@ class UploadRepositoryUnitTest { fun testPreProcessImage() { assertEquals( repository.preProcessImage(uploadableFile, place, similarImageInterface, location), - uploadModel.preProcessImage(uploadableFile, place, similarImageInterface, location) + uploadModel.preProcessImage(uploadableFile, place, similarImageInterface, location), ) } @@ -188,7 +188,7 @@ class UploadRepositoryUnitTest { fun testGetImageQuality() { assertEquals( repository.getImageQuality(uploadItem, location), - uploadModel.getImageQuality(uploadItem, location) + uploadModel.getImageQuality(uploadItem, location), ) } @@ -196,7 +196,7 @@ class UploadRepositoryUnitTest { fun testGetCaptionQuality() { assertEquals( repository.getCaptionQuality(uploadItem), - uploadModel.getCaptionQuality(uploadItem) + uploadModel.getCaptionQuality(uploadItem), ) } @@ -210,7 +210,7 @@ class UploadRepositoryUnitTest { `when`(uploadModel.items).thenReturn(listOf(uploadItem)) assertEquals( repository.getUploadItem(0), - uploadModel.items[0] + uploadModel.items[0], ) } @@ -228,7 +228,7 @@ class UploadRepositoryUnitTest { fun testSetSelectedExistingDepictions() { assertEquals( repository.setSelectedExistingDepictions(listOf("")), - uploadModel.setSelectedExistingDepictions(listOf("")) + uploadModel.setSelectedExistingDepictions(listOf("")), ) } @@ -236,7 +236,7 @@ class UploadRepositoryUnitTest { fun testOnDepictItemClicked() { assertEquals( repository.onDepictItemClicked(depictedItem, mock()), - uploadModel.onDepictItemClicked(depictedItem, mock()) + uploadModel.onDepictItemClicked(depictedItem, mock()), ) } @@ -254,7 +254,7 @@ class UploadRepositoryUnitTest { fun testSearchAllEntities() { assertEquals( repository.searchAllEntities(""), - depictModel.searchAllEntities("", repository) + depictModel.searchAllEntities("", repository), ) } @@ -265,7 +265,7 @@ class UploadRepositoryUnitTest { `when`(place.wikiDataEntityId).thenReturn("1") assertEquals( repository.placeDepictions, - depictModel.getPlaceDepictions(listOf("1")) + depictModel.getPlaceDepictions(listOf("1")), ) } @@ -274,13 +274,16 @@ class UploadRepositoryUnitTest { `when`( nearbyPlaces.getFromWikidataQuery( LatLng(0.0, 0.0, 0.0f), - java.util.Locale.getDefault().language, 0.1, - null - ) + java.util.Locale + .getDefault() + .language, + 0.1, + null, + ), ).thenReturn(listOf(place)) assertEquals( repository.checkNearbyPlaces(0.0, 0.0), - place + place, ) } @@ -288,7 +291,7 @@ class UploadRepositoryUnitTest { fun testCheckNearbyPlacesWithoutExceptionCaseNull() { assertEquals( repository.checkNearbyPlaces(0.0, 0.0), - null + null, ) } @@ -297,13 +300,16 @@ class UploadRepositoryUnitTest { `when`( nearbyPlaces.getFromWikidataQuery( LatLng(0.0, 0.0, 0.0f), - java.util.Locale.getDefault().language, 0.1, - null - ) + java.util.Locale + .getDefault() + .language, + 0.1, + null, + ), ).thenThrow(Exception()) assertEquals( repository.checkNearbyPlaces(0.0, 0.0), - null + null, ) } @@ -311,7 +317,7 @@ class UploadRepositoryUnitTest { fun testUseSimilarPictureCoordinates() { assertEquals( repository.useSimilarPictureCoordinates(imageCoordinates, 0), - uploadModel.useSimilarPictureCoordinates(imageCoordinates, 0) + uploadModel.useSimilarPictureCoordinates(imageCoordinates, 0), ) } @@ -321,7 +327,7 @@ class UploadRepositoryUnitTest { `when`(uploadItem.isWLMUpload).thenReturn(true) assertEquals( repository.isWMLSupportedForThisPlace, - true + true, ) } @@ -329,30 +335,33 @@ class UploadRepositoryUnitTest { fun testGetDepictions() { `when`(depictModel.getDepictions("Q12")) .thenReturn(Single.just(listOf(mock(DepictedItem::class.java)))) - val method: Method = UploadRepository::class.java.getDeclaredMethod( - "getDepictions", - List::class.java - ) + val method: Method = + UploadRepository::class.java.getDeclaredMethod( + "getDepictions", + List::class.java, + ) method.isAccessible = true method.invoke(repository, listOf("Q12")) } @Test fun testJoinIDs() { - val method: Method = UploadRepository::class.java.getDeclaredMethod( - "joinQIDs", - List::class.java - ) + val method: Method = + UploadRepository::class.java.getDeclaredMethod( + "joinQIDs", + List::class.java, + ) method.isAccessible = true method.invoke(repository, listOf("Q12", "Q23")) } @Test fun `test joinIDs when depictIDs is null`() { - val method: Method = UploadRepository::class.java.getDeclaredMethod( - "joinQIDs", - List::class.java - ) + val method: Method = + UploadRepository::class.java.getDeclaredMethod( + "joinQIDs", + List::class.java, + ) method.isAccessible = true method.invoke(repository, null) } @@ -361,7 +370,7 @@ class UploadRepositoryUnitTest { fun testGetSelectedExistingCategories() { assertEquals( repository.selectedExistingCategories, - categoriesModel.getSelectedExistingCategories() + categoriesModel.getSelectedExistingCategories(), ) } @@ -369,7 +378,7 @@ class UploadRepositoryUnitTest { fun testSetSelectedExistingCategories() { assertEquals( repository.setSelectedExistingCategories(listOf("Test")), - categoriesModel.setSelectedExistingCategories(mutableListOf("Test")) + categoriesModel.setSelectedExistingCategories(mutableListOf("Test")), ) } @@ -377,7 +386,7 @@ class UploadRepositoryUnitTest { fun testGetCategories() { assertEquals( repository.getCategories(listOf("Test")), - categoriesModel.getCategoriesByName(mutableListOf("Test")) + categoriesModel.getCategoriesByName(mutableListOf("Test")), ) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/categories/UploadCategoriesFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/categories/UploadCategoriesFragmentUnitTests.kt index ff7eb6625f..ee2d54ee7c 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/categories/UploadCategoriesFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/categories/UploadCategoriesFragmentUnitTests.kt @@ -1,30 +1,18 @@ package fr.free.nrw.commons.upload.categories -import android.app.ProgressDialog import android.content.Context import android.os.Looper -import android.text.Editable -import android.util.Log import android.view.LayoutInflater -import android.view.View -import android.widget.Button -import android.widget.ImageView -import android.widget.ProgressBar -import android.widget.TextView import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction -import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ApplicationProvider -import com.google.android.material.textfield.TextInputLayout import com.nhaarman.mockitokotlin2.times -import com.nhaarman.mockitokotlin2.verify import fr.free.nrw.commons.Media import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.databinding.UploadCategoriesFragmentBinding -import fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText import fr.free.nrw.commons.upload.UploadActivity import fr.free.nrw.commons.upload.UploadBaseFragment import io.reactivex.disposables.Disposable @@ -33,7 +21,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.powermock.reflect.Whitebox import org.robolectric.Robolectric @@ -47,7 +34,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class UploadCategoriesFragmentUnitTests { - private lateinit var fragment: UploadCategoriesFragment private lateinit var context: Context private lateinit var fragmentManager: FragmentManager @@ -68,8 +54,7 @@ class UploadCategoriesFragmentUnitTests { @Mock private lateinit var media: Media - private lateinit var binding : UploadCategoriesFragmentBinding - + private lateinit var binding: UploadCategoriesFragmentBinding @Before fun setUp() { @@ -102,15 +87,16 @@ class UploadCategoriesFragmentUnitTests { @Throws(Exception::class) fun testOnCreateView() { Shadows.shadowOf(Looper.getMainLooper()).idle() - fragment.onCreateView(layoutInflater,null, null) + fragment.onCreateView(layoutInflater, null, null) } @Test @Throws(Exception::class) fun testInitMethod() { - val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod( - "init" - ) + val method: Method = + UploadCategoriesFragment::class.java.getDeclaredMethod( + "init", + ) Shadows.shadowOf(Looper.getMainLooper()).idle() method.isAccessible = true method.invoke(fragment) @@ -120,9 +106,10 @@ class UploadCategoriesFragmentUnitTests { @Throws(Exception::class) fun `Test init when media is non null`() { Whitebox.setInternalState(fragment, "media", media) - val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod( - "init" - ) + val method: Method = + UploadCategoriesFragment::class.java.getDeclaredMethod( + "init", + ) method.isAccessible = true method.invoke(fragment) } @@ -130,9 +117,10 @@ class UploadCategoriesFragmentUnitTests { @Test @Throws(Exception::class) fun testFragmentOnBecameVisible() { - val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod( - "onBecameVisible" - ) + val method: Method = + UploadCategoriesFragment::class.java.getDeclaredMethod( + "onBecameVisible", + ) method.isAccessible = true method.invoke(fragment) } @@ -261,9 +249,10 @@ class UploadCategoriesFragmentUnitTests { @Throws(Exception::class) fun testOnBecameVisible() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod( - "onBecameVisible" - ) + val method: Method = + UploadCategoriesFragment::class.java.getDeclaredMethod( + "onBecameVisible", + ) method.isAccessible = true method.invoke(fragment) } @@ -272,9 +261,10 @@ class UploadCategoriesFragmentUnitTests { @Throws(Exception::class) fun testAddTextChangeListenerToEtSearch() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod( - "addTextChangeListenerToEtSearch" - ) + val method: Method = + UploadCategoriesFragment::class.java.getDeclaredMethod( + "addTextChangeListenerToEtSearch", + ) method.isAccessible = true method.invoke(fragment) } @@ -283,10 +273,11 @@ class UploadCategoriesFragmentUnitTests { @Throws(Exception::class) fun testSearchForCategory() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod( - "searchForCategory", - String::class.java - ) + val method: Method = + UploadCategoriesFragment::class.java.getDeclaredMethod( + "searchForCategory", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "") } @@ -295,9 +286,10 @@ class UploadCategoriesFragmentUnitTests { @Throws(Exception::class) fun testInitRecyclerView() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod( - "initRecyclerView" - ) + val method: Method = + UploadCategoriesFragment::class.java.getDeclaredMethod( + "initRecyclerView", + ) method.isAccessible = true method.invoke(fragment) } @@ -306,9 +298,10 @@ class UploadCategoriesFragmentUnitTests { @Throws(Exception::class) fun testInit() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod( - "init" - ) + val method: Method = + UploadCategoriesFragment::class.java.getDeclaredMethod( + "init", + ) method.isAccessible = true method.invoke(fragment) } @@ -318,9 +311,10 @@ class UploadCategoriesFragmentUnitTests { fun `Test init when media is not null`() { Shadows.shadowOf(Looper.getMainLooper()).idle() Whitebox.setInternalState(fragment, "media", media) - val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod( - "init" - ) + val method: Method = + UploadCategoriesFragment::class.java.getDeclaredMethod( + "init", + ) method.isAccessible = true method.invoke(fragment) } @@ -329,11 +323,11 @@ class UploadCategoriesFragmentUnitTests { @Throws(Exception::class) fun `Test init when callback is null`() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = UploadCategoriesFragment::class.java.getDeclaredMethod( - "init" - ) + val method: Method = + UploadCategoriesFragment::class.java.getDeclaredMethod( + "init", + ) method.isAccessible = true method.invoke(fragment) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/depicts/DepictEditHelperUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/depicts/DepictEditHelperUnitTest.kt index 47c2236069..d90a288cab 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/depicts/DepictEditHelperUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/depicts/DepictEditHelperUnitTest.kt @@ -28,7 +28,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class DepictEditHelperUnitTest { - private lateinit var context: Context private lateinit var helper: DepictEditHelper @@ -68,7 +67,7 @@ class DepictEditHelperUnitTest { helper.makeDepictionEdit(context, media, listOf("Q12")) Mockito.verify(viewUtilWrapper, Mockito.times(1)).showShortToast( context, - context.getString(R.string.depictions_edit_helper_make_edit_toast) + context.getString(R.string.depictions_edit_helper_make_edit_toast), ) } @@ -76,34 +75,34 @@ class DepictEditHelperUnitTest { @Throws(Exception::class) fun testShowCoordinatesEditNotificationCaseTrue() { whenever(media.depictionIds).thenReturn(listOf("id", "id2")) - val method: Method = DepictEditHelper::class.java.getDeclaredMethod( - "showDepictionEditNotification", - Context::class.java, - Media::class.java, - Boolean::class.java - ) + val method: Method = + DepictEditHelper::class.java.getDeclaredMethod( + "showDepictionEditNotification", + Context::class.java, + Media::class.java, + Boolean::class.java, + ) method.isAccessible = true Assertions.assertEquals( method.invoke(helper, context, media, true), - true + true, ) } @Test @Throws(Exception::class) fun testShowCoordinatesEditNotificationCaseFalse() { - val method: Method = DepictEditHelper::class.java.getDeclaredMethod( - "showDepictionEditNotification", - Context::class.java, - Media::class.java, - Boolean::class.java - ) + val method: Method = + DepictEditHelper::class.java.getDeclaredMethod( + "showDepictionEditNotification", + Context::class.java, + Media::class.java, + Boolean::class.java, + ) method.isAccessible = true Assertions.assertEquals( method.invoke(helper, context, media, false), - false + false, ) } - - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/depicts/DepictsFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/depicts/DepictsFragmentUnitTests.kt index 54f1edc686..84b878aeef 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/depicts/DepictsFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/depicts/DepictsFragmentUnitTests.kt @@ -7,9 +7,7 @@ import android.view.LayoutInflater import android.view.View import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction -import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ApplicationProvider -import com.google.android.material.textfield.TextInputLayout import com.nhaarman.mockitokotlin2.whenever import depictedItem import fr.free.nrw.commons.Media @@ -18,7 +16,6 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.kvstore.JsonKvStore -import fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText import fr.free.nrw.commons.upload.UploadActivity import fr.free.nrw.commons.upload.UploadBaseFragment import io.reactivex.disposables.Disposable @@ -39,7 +36,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class DepictsFragmentUnitTests { - private lateinit var fragment: DepictsFragment private lateinit var fragmentManager: FragmentManager private lateinit var layoutInflater: LayoutInflater @@ -82,9 +78,10 @@ class DepictsFragmentUnitTests { layoutInflater = LayoutInflater.from(activity) - view = LayoutInflater.from(activity) - .inflate(R.layout.upload_depicts_fragment, null) as View - + view = + LayoutInflater + .from(activity) + .inflate(R.layout.upload_depicts_fragment, null) as View Whitebox.setInternalState(fragment, "callback", callback) Whitebox.setInternalState(fragment, "subscribe", disposable) @@ -106,9 +103,10 @@ class DepictsFragmentUnitTests { @Test @Throws(Exception::class) fun testInit() { - val method: Method = DepictsFragment::class.java.getDeclaredMethod( - "init" - ) + val method: Method = + DepictsFragment::class.java.getDeclaredMethod( + "init", + ) method.isAccessible = true method.invoke(fragment) } @@ -117,9 +115,10 @@ class DepictsFragmentUnitTests { @Throws(Exception::class) fun `Test init when media is not null`() { Whitebox.setInternalState(fragment, "media", media) - val method: Method = DepictsFragment::class.java.getDeclaredMethod( - "init" - ) + val method: Method = + DepictsFragment::class.java.getDeclaredMethod( + "init", + ) method.isAccessible = true method.invoke(fragment) } @@ -127,9 +126,10 @@ class DepictsFragmentUnitTests { @Test @Throws(Exception::class) fun testOnBecameVisible() { - val method: Method = DepictsFragment::class.java.getDeclaredMethod( - "onBecameVisible" - ) + val method: Method = + DepictsFragment::class.java.getDeclaredMethod( + "onBecameVisible", + ) method.isAccessible = true method.invoke(fragment) } @@ -211,10 +211,11 @@ class DepictsFragmentUnitTests { @Test @Throws(Exception::class) fun testSearchForDepictions() { - val method: Method = DepictsFragment::class.java.getDeclaredMethod( - "searchForDepictions", - String::class.java - ) + val method: Method = + DepictsFragment::class.java.getDeclaredMethod( + "searchForDepictions", + String::class.java, + ) method.isAccessible = true method.invoke(fragment, "") } @@ -234,9 +235,10 @@ class DepictsFragmentUnitTests { @Test @Throws(Exception::class) fun testInitRecyclerView() { - val method: Method = DepictsFragment::class.java.getDeclaredMethod( - "initRecyclerView" - ) + val method: Method = + DepictsFragment::class.java.getDeclaredMethod( + "initRecyclerView", + ) method.isAccessible = true method.invoke(fragment) } @@ -245,9 +247,10 @@ class DepictsFragmentUnitTests { @Throws(Exception::class) fun `Test initRecyclerView when media is not null`() { Whitebox.setInternalState(fragment, "media", media) - val method: Method = DepictsFragment::class.java.getDeclaredMethod( - "initRecyclerView" - ) + val method: Method = + DepictsFragment::class.java.getDeclaredMethod( + "initRecyclerView", + ) method.isAccessible = true method.invoke(fragment) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt index c81d0548db..ed76b4519f 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt @@ -57,7 +57,6 @@ import java.lang.reflect.Method @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class UploadMediaDetailFragmentUnitTest { - private lateinit var fragment: UploadMediaDetailFragment private lateinit var fragmentManager: FragmentManager private lateinit var context: Context @@ -73,8 +72,8 @@ class UploadMediaDetailFragmentUnitTest { private lateinit var btnCopyToSubsequentMedia: AppCompatButton private lateinit var photoViewBackgroundImage: PhotoView private lateinit var locationStatusLl: LinearLayout - private lateinit var locationImageView : ImageView - private lateinit var locationTextView : TextView + private lateinit var locationImageView: ImageView + private lateinit var locationTextView: TextView private lateinit var llContainerMediaDetail: LinearLayout private lateinit var ibExpandCollapse: AppCompatImageButton @@ -120,8 +119,10 @@ class UploadMediaDetailFragmentUnitTest { activity = Robolectric.buildActivity(UploadActivity::class.java).create().get() layoutInflater = LayoutInflater.from(activity) - view = LayoutInflater.from(activity) - .inflate(R.layout.fragment_upload_media_detail_fragment, null) as View + view = + LayoutInflater + .from(activity) + .inflate(R.layout.fragment_upload_media_detail_fragment, null) as View fragment = UploadMediaDetailFragment() fragmentManager = activity.supportFragmentManager @@ -190,9 +191,10 @@ class UploadMediaDetailFragmentUnitTest { fun testInit() { Shadows.shadowOf(Looper.getMainLooper()).idle() Whitebox.setInternalState(fragment, "presenter", presenter) - val method: Method = UploadMediaDetailFragment::class.java.getDeclaredMethod( - "init" - ) + val method: Method = + UploadMediaDetailFragment::class.java.getDeclaredMethod( + "init", + ) method.isAccessible = true method.invoke(fragment) } @@ -204,9 +206,10 @@ class UploadMediaDetailFragmentUnitTest { Whitebox.setInternalState(fragment, "presenter", presenter) `when`(callback.getIndexInViewFlipper(fragment)).thenReturn(1) `when`(callback.totalNumberOfSteps).thenReturn(5) - val method: Method = UploadMediaDetailFragment::class.java.getDeclaredMethod( - "init" - ) + val method: Method = + UploadMediaDetailFragment::class.java.getDeclaredMethod( + "init", + ) method.isAccessible = true method.invoke(fragment) } @@ -215,9 +218,12 @@ class UploadMediaDetailFragmentUnitTest { @Throws(Exception::class) fun testShowInfoAlert() { Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = UploadMediaDetailFragment::class.java.getDeclaredMethod( - "showInfoAlert", Int::class.java, Int::class.java - ) + val method: Method = + UploadMediaDetailFragment::class.java.getDeclaredMethod( + "showInfoAlert", + Int::class.java, + Int::class.java, + ) method.isAccessible = true method.invoke(fragment, R.string.media_detail_step_title, R.string.media_details_tooltip) } @@ -283,9 +289,10 @@ class UploadMediaDetailFragmentUnitTest { Whitebox.setInternalState(fragment, "showNearbyFound", true) Whitebox.setInternalState(fragment, "nearbyPlace", place) Whitebox.setInternalState(fragment, "uploadItem", uploadItem) - val method: Method = UploadMediaDetailFragment::class.java.getDeclaredMethod( - "onBecameVisible" - ) + val method: Method = + UploadMediaDetailFragment::class.java.getDeclaredMethod( + "onBecameVisible", + ) method.isAccessible = true method.invoke(fragment) } @@ -321,7 +328,6 @@ class UploadMediaDetailFragmentUnitTest { fragment.showDuplicatePicturePopup(uploadItem) } - @Test @Throws(Exception::class) fun testShowConnectionErrorPopupForCaptionCheck() { @@ -374,7 +380,7 @@ class UploadMediaDetailFragmentUnitTest { Whitebox.setInternalState(cameraPosition, "latitude", latLng.latitude) Whitebox.setInternalState(cameraPosition, "longitude", latLng.longitude) Whitebox.setInternalState(fragment, "editableUploadItem", uploadItem) - Whitebox.setInternalState(fragment,"isMissingLocationDialog",true) + Whitebox.setInternalState(fragment, "isMissingLocationDialog", true) Whitebox.setInternalState(fragment, "presenter", presenter) `when`(LocationPicker.getCameraPosition(intent)).thenReturn(cameraPosition) @@ -433,20 +439,20 @@ class UploadMediaDetailFragmentUnitTest { @Throws(Exception::class) fun testDisplayAddLocationDialog() { Shadows.shadowOf(Looper.getMainLooper()).idle() - runnable = Runnable { } + runnable = Runnable { } fragment.displayAddLocationDialog(runnable) } @Test @Throws(Exception::class) - fun testRememberedZoomLevelOnNull(){ + fun testRememberedZoomLevelOnNull() { shadowOf(Looper.getMainLooper()).idle() Whitebox.setInternalState(fragment, "defaultKvStore", defaultKvStore) `when`(uploadItem.gpsCoords).thenReturn(null) `when`(defaultKvStore.getString(LAST_ZOOM)).thenReturn("13.0") fragment.showExternalMap(uploadItem) - Mockito.verify(uploadItem,Mockito.times(1)).gpsCoords - Mockito.verify(defaultKvStore,Mockito.times(2)).getString(LAST_ZOOM) + Mockito.verify(uploadItem, Mockito.times(1)).gpsCoords + Mockito.verify(defaultKvStore, Mockito.times(2)).getString(LAST_ZOOM) val shadowActivity: ShadowActivity = shadowOf(activity) val startedIntent = shadowActivity.nextStartedActivity val shadowIntent: ShadowIntent = shadowOf(startedIntent) @@ -455,7 +461,7 @@ class UploadMediaDetailFragmentUnitTest { @Test @Throws(Exception::class) - fun testRememberedZoomLevelOnNotNull(){ + fun testRememberedZoomLevelOnNotNull() { shadowOf(Looper.getMainLooper()).idle() `when`(uploadItem.gpsCoords).thenReturn(imageCoordinates) `when`(imageCoordinates.decLatitude).thenReturn(8.0) @@ -463,11 +469,10 @@ class UploadMediaDetailFragmentUnitTest { `when`(imageCoordinates.zoomLevel).thenReturn(14.0) `when`(defaultKvStore.getString(LAST_ZOOM)).thenReturn(null) fragment.showExternalMap(uploadItem) - Mockito.verify(uploadItem.gpsCoords,Mockito.times(1)).zoomLevel + Mockito.verify(uploadItem.gpsCoords, Mockito.times(1)).zoomLevel val shadowActivity: ShadowActivity = shadowOf(activity) val startedIntent = shadowActivity.nextStartedActivity val shadowIntent: ShadowIntent = shadowOf(startedIntent) Assert.assertEquals(shadowIntent.intentClass, LocationPickerActivity::class.java) } - } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/structure/depictions/DepictedItemTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/structure/depictions/DepictedItemTest.kt index ed89e3583d..892d501fd2 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/structure/depictions/DepictedItemTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/structure/depictions/DepictedItemTest.kt @@ -6,7 +6,6 @@ import entity import entityId import fr.free.nrw.commons.wikidata.WikidataProperties import org.junit.Assert -import org.junit.Ignore import org.junit.Test import place import snak @@ -15,7 +14,6 @@ import valueString import wikiBaseEntityValue class DepictedItemTest { - @Test fun `name and description get user language label`() { val depictedItem = @@ -53,12 +51,13 @@ class DepictedItemTest { Assert.assertEquals( DepictedItem( entity( - statements = mapOf( - WikidataProperties.IMAGE.propertyName to listOf(statement(snak(dataValue = mock()))) - ) - ) + statements = + mapOf( + WikidataProperties.IMAGE.propertyName to listOf(statement(snak(dataValue = mock()))), + ), + ), ).imageUrl, - null + null, ) } @@ -67,14 +66,17 @@ class DepictedItemTest { Assert.assertEquals( DepictedItem( entity( - statements = mapOf( - WikidataProperties.IMAGE.propertyName to listOf( - statement(snak(dataValue = valueString("prefix: example_"))) - ) - ) - ) + statements = + mapOf( + WikidataProperties.IMAGE.propertyName to + listOf( + statement(snak(dataValue = valueString("prefix: example_"))), + ), + ), + ), ).imageUrl, - "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b7/_example_/70px-_example_") + "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b7/_example_/70px-_example_", + ) } @Test @@ -82,16 +84,19 @@ class DepictedItemTest { Assert.assertEquals( DepictedItem( entity( - statements = mapOf( - WikidataProperties.INSTANCE_OF.propertyName to listOf( - statement(snak(dataValue = valueString("prefix: example_"))), - statement(snak(dataValue = entityId(wikiBaseEntityValue(id = "1")))), - statement(snak(dataValue = entityId(wikiBaseEntityValue(id = "2")))) - ) - ) - ) + statements = + mapOf( + WikidataProperties.INSTANCE_OF.propertyName to + listOf( + statement(snak(dataValue = valueString("prefix: example_"))), + statement(snak(dataValue = entityId(wikiBaseEntityValue(id = "1")))), + statement(snak(dataValue = entityId(wikiBaseEntityValue(id = "2")))), + ), + ), + ), ).instanceOfs, - listOf("1", "2")) + listOf("1", "2"), + ) } @Test @@ -104,15 +109,18 @@ class DepictedItemTest { Assert.assertEquals( DepictedItem( entity( - statements = mapOf( - WikidataProperties.COMMONS_CATEGORY.propertyName to listOf( - statement(snak(dataValue = valueString("1"))), - statement(snak(dataValue = valueString("2"))) - ) - ) - ) + statements = + mapOf( + WikidataProperties.COMMONS_CATEGORY.propertyName to + listOf( + statement(snak(dataValue = valueString("1"))), + statement(snak(dataValue = valueString("2"))), + ), + ), + ), ).commonsCategories.map { it.name }, - listOf("1", "2")) + listOf("1", "2"), + ) } @Test @@ -137,7 +145,6 @@ class DepictedItemTest { Assert.assertEquals(depictedItem.description, "2") } - @Test fun `same object is Equal`() { val depictedItem = depictedItem() @@ -152,24 +159,26 @@ class DepictedItemTest { @Test fun `if names are equal is Equal`() { Assert.assertEquals( - depictedItem(name="a", id = "0") == depictedItem(name="a", id = "1"), - true) + depictedItem(name = "a", id = "0") == depictedItem(name = "a", id = "1"), + true, + ) } @Test fun `if names are not equal is not Equal`() { Assert.assertEquals( - depictedItem(name="a") == depictedItem(name="b"), - false) + depictedItem(name = "a") == depictedItem(name = "b"), + false, + ) } @Test fun `hashCode returns same values for objects with same name`() { - Assert.assertEquals(depictedItem(name="a").hashCode(), depictedItem(name="a").hashCode()) + Assert.assertEquals(depictedItem(name = "a").hashCode(), depictedItem(name = "a").hashCode()) } - + @Test fun `hashCode returns different values for objects with different name`() { - Assert.assertNotEquals(depictedItem(name="a").hashCode(), depictedItem(name="b").hashCode()) + Assert.assertNotEquals(depictedItem(name = "a").hashCode(), depictedItem(name = "b").hashCode()) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/CommonsDateUtilTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/CommonsDateUtilTest.kt index 52acbf5cd9..40f848a487 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/CommonsDateUtilTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/CommonsDateUtilTest.kt @@ -4,16 +4,17 @@ import org.junit.Assert import org.junit.Test class CommonsDateUtilTest { - @Test fun `Iso8601DateFormatTimestamp parses legal date`() { - val iso8601DateFormatTimestamp = CommonsDateUtil - .getIso8601DateFormatTimestamp() - val parsedDate = iso8601DateFormatTimestamp - .parse("2020-04-07T14:21:57Z") + val iso8601DateFormatTimestamp = + CommonsDateUtil + .getIso8601DateFormatTimestamp() + val parsedDate = + iso8601DateFormatTimestamp + .parse("2020-04-07T14:21:57Z") Assert.assertEquals( "2020-04-07T14:21:57Z", - iso8601DateFormatTimestamp.format(parsedDate) + iso8601DateFormatTimestamp.format(parsedDate), ) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/FileUtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/FileUtilsTest.kt index d186899788..45385f4412 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/FileUtilsTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/FileUtilsTest.kt @@ -4,9 +4,8 @@ import com.nhaarman.mockitokotlin2.mock import fr.free.nrw.commons.upload.FileUtils import fr.free.nrw.commons.upload.FileUtilsWrapper import org.junit.Assert.assertEquals -import org.junit.Ignore import org.junit.Test -import java.io.* +import java.io.File class FileUtilsTest { @Test @@ -24,23 +23,25 @@ class FileUtilsTest { val fileUtilsWrapper = FileUtilsWrapper(mock()) assertEquals( - "907d14fb3af2b0d4f18c2d46abe8aedce17367bd", - fileUtilsWrapper.getSHA1("Hello, World".byteInputStream()) + "907d14fb3af2b0d4f18c2d46abe8aedce17367bd", + fileUtilsWrapper.getSHA1("Hello, World".byteInputStream()), ) assertEquals( - "8b971da6347bd126872ea2f4f8d394e70c74073a", - fileUtilsWrapper.getSHA1("apps-android-commons".byteInputStream()) + "8b971da6347bd126872ea2f4f8d394e70c74073a", + fileUtilsWrapper.getSHA1("apps-android-commons".byteInputStream()), ) assertEquals( - "e9d30f5a3a82792b9d79c258366bd53207ceaeb3", - fileUtilsWrapper.getSHA1("domdomegg was here".byteInputStream()) + "e9d30f5a3a82792b9d79c258366bd53207ceaeb3", + fileUtilsWrapper.getSHA1("domdomegg was here".byteInputStream()), ) assertEquals( - "96e733a3e59261c0621ba99be5bd10bb21abe53e", - fileUtilsWrapper.getSHA1("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=".byteInputStream()) + "96e733a3e59261c0621ba99be5bd10bb21abe53e", + fileUtilsWrapper.getSHA1( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=".byteInputStream(), + ), ) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/ImageUtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/ImageUtilsTest.kt index 08d95d60d7..da7ca208ee 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/ImageUtilsTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/ImageUtilsTest.kt @@ -5,39 +5,29 @@ import android.content.Context import android.graphics.Bitmap import android.net.Uri import androidx.test.core.app.ApplicationProvider -import androidx.work.Data -import androidx.work.ListenableWorker import androidx.work.WorkManager -import androidx.work.testing.TestListenableWorkerBuilder import androidx.work.testing.WorkManagerTestInitHelper import fr.free.nrw.commons.TestCommonsApplication -import fr.free.nrw.commons.contributions.SetWallpaperWorker import fr.free.nrw.commons.location.LatLng import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient import io.reactivex.disposables.CompositeDisposable import org.junit.Assert import org.junit.Before import org.junit.Test -import org.junit.jupiter.api.Assertions.assertTrue import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.mock -import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import java.io.File -import java.lang.reflect.Field import java.lang.reflect.Method - - @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) class ImageUtilsTest { - private lateinit var context: Context @Mock @@ -65,30 +55,23 @@ class ImageUtilsTest { @Test fun testCheckIfImageIsTooDarkCaseException() { - Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("") - , ImageUtils.IMAGE_OK) + Assert.assertEquals(ImageUtils.checkIfImageIsTooDark(""), ImageUtils.IMAGE_OK) } // Refer: testCheckIfImageIsTooDarkCaseException() @Test fun testCheckIfProperImageIsTooDark() { - Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/ok1.jpg") - , ImageUtils.IMAGE_OK) - Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/ok2.jpg") - , ImageUtils.IMAGE_OK) - Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/ok3.jpg") - , ImageUtils.IMAGE_OK) - Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/ok4.jpg") - , ImageUtils.IMAGE_OK) + Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/ok1.jpg"), ImageUtils.IMAGE_OK) + Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/ok2.jpg"), ImageUtils.IMAGE_OK) + Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/ok3.jpg"), ImageUtils.IMAGE_OK) + Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/ok4.jpg"), ImageUtils.IMAGE_OK) } // Refer: testCheckIfImageIsTooDarkCaseException() @Test fun testCheckIfDarkImageIsTooDark() { - Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/dark1.jpg") - , ImageUtils.IMAGE_DARK) - Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/dark2.jpg") - , ImageUtils.IMAGE_DARK) + Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/dark1.jpg"), ImageUtils.IMAGE_DARK) + Assert.assertEquals(ImageUtils.checkIfImageIsTooDark("src/test/resources/ImageTest/dark2.jpg"), ImageUtils.IMAGE_DARK) } @Test @@ -100,11 +83,12 @@ class ImageUtilsTest { @Test fun testSetWallpaper() { val mockImageUtils = mock(ImageUtils::class.java) - val method: Method = ImageUtils::class.java.getDeclaredMethod( - "enqueueSetWallpaperWork", - Context::class.java, - Uri::class.java - ) + val method: Method = + ImageUtils::class.java.getDeclaredMethod( + "enqueueSetWallpaperWork", + Context::class.java, + Uri::class.java, + ) method.isAccessible = true method.invoke(mockImageUtils, context, imageUri) @@ -113,10 +97,11 @@ class ImageUtilsTest { @Test fun testShowSettingAvatarProgressBar() { val mockImageUtils = mock(ImageUtils::class.java) - val method: Method = ImageUtils::class.java.getDeclaredMethod( - "showSettingAvatarProgressBar", - Context::class.java - ) + val method: Method = + ImageUtils::class.java.getDeclaredMethod( + "showSettingAvatarProgressBar", + Context::class.java, + ) method.isAccessible = true method.invoke(mockImageUtils, context) } @@ -163,7 +148,7 @@ class ImageUtilsTest { "", "", okHttpJsonApiClient, - compositeDisposable + compositeDisposable, ) } @@ -172,8 +157,9 @@ class ImageUtilsTest { Assert.assertEquals( ImageUtils.checkImageGeolocationIsDifferent( "test", - null - ), false + null, + ), + false, ) } @@ -182,18 +168,20 @@ class ImageUtilsTest { Assert.assertEquals( ImageUtils.checkImageGeolocationIsDifferent( "0.0|0.0", - LatLng(0.0, 0.0, 0f) - ), false + LatLng(0.0, 0.0, 0f), + ), + false, ) } @Test fun testCheckIfImageIsDark() { val mockImageUtils = mock(ImageUtils::class.java) - val method: Method = ImageUtils::class.java.getDeclaredMethod( - "checkIfImageIsDark", - Bitmap::class.java - ) + val method: Method = + ImageUtils::class.java.getDeclaredMethod( + "checkIfImageIsDark", + Bitmap::class.java, + ) method.isAccessible = true method.invoke(mockImageUtils, null) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/LengthUtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/LengthUtilsTest.kt index 23ea105ea8..ce2f6eacbe 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/LengthUtilsTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/LengthUtilsTest.kt @@ -131,17 +131,28 @@ class LengthUtilsTest { // Test assertion helper functions - private fun assertFormattedDistanceBetween(expected: String, pointA: LatLng, pointB: LatLng) = - assertEquals(expected, LengthUtils.formatDistanceBetween(pointA, pointB)) - - private fun assertFormattedDistance(expected: String, distance: Int) = - assertEquals(expected, LengthUtils.formatDistance(distance)) - - private fun assertDistanceBetween(expected: Double, pointA: LatLng, pointB: LatLng) = - // Acceptable error: 1cm - assertEquals(expected, LengthUtils.computeDistanceBetween(pointA, pointB), 0.01) - - private fun assertBearing(expected: Double, pointA: LatLng, pointB: LatLng) = - // Acceptable error: 1 degree - assertEquals(expected, LengthUtils.computeBearing(pointA,pointB), 1.0) -} \ No newline at end of file + private fun assertFormattedDistanceBetween( + expected: String, + pointA: LatLng, + pointB: LatLng, + ) = assertEquals(expected, LengthUtils.formatDistanceBetween(pointA, pointB)) + + private fun assertFormattedDistance( + expected: String, + distance: Int, + ) = assertEquals(expected, LengthUtils.formatDistance(distance)) + + private fun assertDistanceBetween( + expected: Double, + pointA: LatLng, + pointB: LatLng, + ) = // Acceptable error: 1cm + assertEquals(expected, LengthUtils.computeDistanceBetween(pointA, pointB), 0.01) + + private fun assertBearing( + expected: Double, + pointA: LatLng, + pointB: LatLng, + ) = // Acceptable error: 1 degree + assertEquals(expected, LengthUtils.computeBearing(pointA, pointB), 1.0) +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/LocationUtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/LocationUtilsTest.kt index 316abd3c9e..1b3980a0b0 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/LocationUtilsTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/LocationUtilsTest.kt @@ -1,6 +1,5 @@ package fr.free.nrw.commons.utils -import fr.free.nrw.commons.location.LatLng import org.junit.Test import org.junit.jupiter.api.Assertions.assertEquals @@ -8,7 +7,6 @@ import org.junit.jupiter.api.Assertions.assertEquals * Test class for location utils */ class LocationUtilsTest { - @Test fun testCalculateDistance() { val lat1 = 37.7749 @@ -22,5 +20,4 @@ class LocationUtilsTest { assertEquals(expectedDistance, actualDistance, 0.2) // Tolerance = 0.2 km } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/PagedListMock.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/PagedListMock.kt index 39f8f88995..633ee39641 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/PagedListMock.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/PagedListMock.kt @@ -1,6 +1,5 @@ package fr.free.nrw.commons.utils - import android.database.Cursor import androidx.lifecycle.LiveData import androidx.paging.DataSource @@ -14,15 +13,17 @@ import com.nhaarman.mockitokotlin2.whenever import org.mockito.Mockito.mock fun List.asPagedList(config: PagedList.Config? = null): LiveData> { - val defaultConfig = PagedList.Config.Builder() - .setEnablePlaceholders(false) - .setPageSize(size) - .setMaxSize(size + 2) - .setPrefetchDistance(1) - .build() + val defaultConfig = + PagedList.Config + .Builder() + .setEnablePlaceholders(false) + .setPageSize(size) + .setMaxSize(size + 2) + .setPrefetchDistance(1) + .build() return LivePagedListBuilder( createMockDataSourceFactory(this), - config ?: defaultConfig + config ?: defaultConfig, ).build() } @@ -38,39 +39,48 @@ fun createMockDataSourceFactory(itemList: List): DataSource.Factory(private val itemList: List) : - LimitOffsetDataSource(mockDb(), mockQuery(), false, "") { - override fun convertRows(cursor: Cursor): MutableList { - return itemList.toMutableList() - } +class MockLimitDataSource( + private val itemList: List, +) : LimitOffsetDataSource(mockDb(), mockQuery(), false, "") { + override fun convertRows(cursor: Cursor): MutableList = itemList.toMutableList() + override fun countItems(): Int = itemList.count() + override fun isInvalid(): Boolean = false - override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback) { - } - override fun loadRange(startPosition: Int, loadCount: Int): MutableList { - return itemList.subList(startPosition, startPosition + loadCount).toMutableList() + override fun loadRange( + params: LoadRangeParams, + callback: LoadRangeCallback, + ) { } - override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback) { + override fun loadRange( + startPosition: Int, + loadCount: Int, + ): MutableList = itemList.subList(startPosition, startPosition + loadCount).toMutableList() + + override fun loadInitial( + params: LoadInitialParams, + callback: LoadInitialCallback, + ) { callback.onResult(itemList, 0) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/StringSortingUtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/StringSortingUtilsTest.kt index 7bdee0c2e9..37a7906ba0 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/StringSortingUtilsTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/StringSortingUtilsTest.kt @@ -7,21 +7,24 @@ import org.junit.Test import java.util.Collections.sort class StringSortingUtilsTest { - @Test fun testSortingNumbersBySimilarity() { - val actualList = listOf( - CategoryItem("1234567", "", "", false), - CategoryItem("4567", "", "", false), - CategoryItem("12345", "", "", false), - CategoryItem("123", "", "", false), - CategoryItem("1234", "", "", false)) - val expectedList = listOf( - CategoryItem("1234", "", "", false), - CategoryItem("12345", "", "", false), - CategoryItem("123", "", "", false), - CategoryItem("1234567", "", "", false), - CategoryItem("4567", "", "", false)) + val actualList = + listOf( + CategoryItem("1234567", "", "", false), + CategoryItem("4567", "", "", false), + CategoryItem("12345", "", "", false), + CategoryItem("123", "", "", false), + CategoryItem("1234", "", "", false), + ) + val expectedList = + listOf( + CategoryItem("1234", "", "", false), + CategoryItem("12345", "", "", false), + CategoryItem("123", "", "", false), + CategoryItem("1234567", "", "", false), + CategoryItem("4567", "", "", false), + ) sort(actualList, sortBySimilarity("1234")) @@ -30,24 +33,26 @@ class StringSortingUtilsTest { @Test fun testSortingTextBySimilarity() { - val actualList = listOf( - CategoryItem("The quick brown fox", "", "", false), - CategoryItem("quick brown fox", "", "", false), - CategoryItem("The", "", "", false), - CategoryItem("The quick ", "", "", false), - CategoryItem("The fox", "", "", false), - CategoryItem("brown fox", "", "", false), - CategoryItem("fox", "", "", false) - ) - val expectedList = listOf( - CategoryItem("The", "", "", false), - CategoryItem("The fox", "", "", false), - CategoryItem("The quick ", "", "", false), - CategoryItem("The quick brown fox", "", "", false), - CategoryItem("quick brown fox", "", "", false), - CategoryItem("brown fox", "", "", false), - CategoryItem("fox", "", "", false) - ) + val actualList = + listOf( + CategoryItem("The quick brown fox", "", "", false), + CategoryItem("quick brown fox", "", "", false), + CategoryItem("The", "", "", false), + CategoryItem("The quick ", "", "", false), + CategoryItem("The fox", "", "", false), + CategoryItem("brown fox", "", "", false), + CategoryItem("fox", "", "", false), + ) + val expectedList = + listOf( + CategoryItem("The", "", "", false), + CategoryItem("The fox", "", "", false), + CategoryItem("The quick ", "", "", false), + CategoryItem("The quick brown fox", "", "", false), + CategoryItem("quick brown fox", "", "", false), + CategoryItem("brown fox", "", "", false), + CategoryItem("fox", "", "", false), + ) sort(actualList, sortBySimilarity("The")) @@ -56,20 +61,22 @@ class StringSortingUtilsTest { @Test fun testSortingSymbolsBySimilarity() { - val actualList = listOf( - CategoryItem("$$$$$", "", "", false), - CategoryItem("****", "", "", false), - CategoryItem("**$*", "", "", false), - CategoryItem("*$*$", "", "", false), - CategoryItem(".*$", "", "", false) - ) - val expectedList = listOf( - CategoryItem("**$*", "", "", false), - CategoryItem("*$*$", "", "", false), - CategoryItem(".*$", "", "", false), - CategoryItem("****", "", "", false), - CategoryItem("$$$$$", "", "", false) - ) + val actualList = + listOf( + CategoryItem("$$$$$", "", "", false), + CategoryItem("****", "", "", false), + CategoryItem("**$*", "", "", false), + CategoryItem("*$*$", "", "", false), + CategoryItem(".*$", "", "", false), + ) + val expectedList = + listOf( + CategoryItem("**$*", "", "", false), + CategoryItem("*$*$", "", "", false), + CategoryItem(".*$", "", "", false), + CategoryItem("****", "", "", false), + CategoryItem("$$$$$", "", "", false), + ) sort(actualList, sortBySimilarity("**$")) @@ -79,27 +86,29 @@ class StringSortingUtilsTest { @Test fun testSortingMixedStringsBySimilarity() { // Sample from Category:2018 Android phones - val actualList = listOf( - CategoryItem("ASUS ZenFone 5 (2018)", "", "", false), - CategoryItem("Google Pixel 3", "", "", false), - CategoryItem("HTC U12", "", "", false), - CategoryItem("Huawei P20", "", "", false), - CategoryItem("LG G7 ThinQ", "", "", false), - CategoryItem("Samsung Galaxy A8 (2018)", "", "", false), - CategoryItem("Samsung Galaxy S9", "", "", false), + val actualList = + listOf( + CategoryItem("ASUS ZenFone 5 (2018)", "", "", false), + CategoryItem("Google Pixel 3", "", "", false), + CategoryItem("HTC U12", "", "", false), + CategoryItem("Huawei P20", "", "", false), + CategoryItem("LG G7 ThinQ", "", "", false), + CategoryItem("Samsung Galaxy A8 (2018)", "", "", false), + CategoryItem("Samsung Galaxy S9", "", "", false), // One with more complicated symbols - CategoryItem("MadeUpPhone 2018.$£#你好", "", "", false) - ) - val expectedList = listOf( - CategoryItem("Samsung Galaxy S9", "", "", false), - CategoryItem("ASUS ZenFone 5 (2018)", "", "", false), - CategoryItem("Samsung Galaxy A8 (2018)", "", "", false), - CategoryItem("Google Pixel 3", "", "", false), - CategoryItem("HTC U12", "", "", false), - CategoryItem("Huawei P20", "", "", false), - CategoryItem("LG G7 ThinQ", "", "", false), - CategoryItem("MadeUpPhone 2018.$£#你好", "", "", false) - ) + CategoryItem("MadeUpPhone 2018.$£#你好", "", "", false), + ) + val expectedList = + listOf( + CategoryItem("Samsung Galaxy S9", "", "", false), + CategoryItem("ASUS ZenFone 5 (2018)", "", "", false), + CategoryItem("Samsung Galaxy A8 (2018)", "", "", false), + CategoryItem("Google Pixel 3", "", "", false), + CategoryItem("HTC U12", "", "", false), + CategoryItem("Huawei P20", "", "", false), + CategoryItem("LG G7 ThinQ", "", "", false), + CategoryItem("MadeUpPhone 2018.$£#你好", "", "", false), + ) sort(actualList, sortBySimilarity("S9")) @@ -108,31 +117,33 @@ class StringSortingUtilsTest { @Test fun testSortingWithEmptyStrings() { - val actualList = listOf( - CategoryItem("brown fox", "", "", false), - CategoryItem("", "", "", false), - CategoryItem("quick brown fox", "", "", false), - CategoryItem("the", "", "", false), - CategoryItem("", "", "", false), - CategoryItem("the fox", "", "", false), - CategoryItem("fox", "", "", false), - CategoryItem("", "", "", false), - CategoryItem("", "", "", false) - ) - val expectedList = listOf( - CategoryItem("the fox", "", "", false), - CategoryItem("brown fox", "", "", false), - CategoryItem("the", "", "", false), - CategoryItem("fox", "", "", false), - CategoryItem("quick brown fox", "", "", false), - CategoryItem("", "", "", false), - CategoryItem("", "", "", false), - CategoryItem("", "", "", false), - CategoryItem("", "", "", false) - ) + val actualList = + listOf( + CategoryItem("brown fox", "", "", false), + CategoryItem("", "", "", false), + CategoryItem("quick brown fox", "", "", false), + CategoryItem("the", "", "", false), + CategoryItem("", "", "", false), + CategoryItem("the fox", "", "", false), + CategoryItem("fox", "", "", false), + CategoryItem("", "", "", false), + CategoryItem("", "", "", false), + ) + val expectedList = + listOf( + CategoryItem("the fox", "", "", false), + CategoryItem("brown fox", "", "", false), + CategoryItem("the", "", "", false), + CategoryItem("fox", "", "", false), + CategoryItem("quick brown fox", "", "", false), + CategoryItem("", "", "", false), + CategoryItem("", "", "", false), + CategoryItem("", "", "", false), + CategoryItem("", "", "", false), + ) sort(actualList, sortBySimilarity("the fox")) assertEquals(expectedList, actualList) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt index 8f0dc8a80a..24e9b233aa 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt @@ -5,7 +5,6 @@ import org.junit.Assert.assertEquals import org.junit.Test class UtilsFixExtensionTest { - @Test fun jpegResultsInJpg() { assertEquals("SampleFile.jpg", fixExtension("SampleFile.jpeg", "jpeg")) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/widget/HeightLimitedRecyclerViewUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/widget/HeightLimitedRecyclerViewUnitTests.kt index adc94605e2..115848e55a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/widget/HeightLimitedRecyclerViewUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/widget/HeightLimitedRecyclerViewUnitTests.kt @@ -15,12 +15,10 @@ import java.lang.reflect.Method @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class HeightLimitedRecyclerViewUnitTests { - private lateinit var activityController: ActivityController private lateinit var activity: Activity private lateinit var recyclerView: HeightLimitedRecyclerView - @Before fun setUp() { activityController = Robolectric.buildActivity(Activity::class.java) @@ -40,13 +38,13 @@ class HeightLimitedRecyclerViewUnitTests { @Test @Throws(Exception::class) fun testOnMeasure() { - val method: Method = HeightLimitedRecyclerView::class.java.getDeclaredMethod( - "onMeasure", - Int::class.java, - Int::class.java - ) + val method: Method = + HeightLimitedRecyclerView::class.java.getDeclaredMethod( + "onMeasure", + Int::class.java, + Int::class.java, + ) method.isAccessible = true method.invoke(recyclerView, 0, 0) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/widget/PicOfDayAppWidgetUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/widget/PicOfDayAppWidgetUnitTests.kt index 1547c313b3..623bd2affe 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/widget/PicOfDayAppWidgetUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/widget/PicOfDayAppWidgetUnitTests.kt @@ -28,7 +28,6 @@ import java.lang.reflect.Method @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class PicOfDayAppWidgetUnitTests { - private lateinit var widget: PicOfDayAppWidget private lateinit var context: Context @@ -83,14 +82,15 @@ class PicOfDayAppWidgetUnitTests { @Test @Throws(Exception::class) fun testLoadImageFromUrl() { - val method: Method = PicOfDayAppWidget::class.java.getDeclaredMethod( - "loadImageFromUrl", - String::class.java, - Context::class.java, - RemoteViews::class.java, - AppWidgetManager::class.java, - Int::class.java - ) + val method: Method = + PicOfDayAppWidget::class.java.getDeclaredMethod( + "loadImageFromUrl", + String::class.java, + Context::class.java, + RemoteViews::class.java, + AppWidgetManager::class.java, + Int::class.java, + ) method.isAccessible = true method.invoke(widget, "", context, views, appWidgetManager, 1) } @@ -99,15 +99,15 @@ class PicOfDayAppWidgetUnitTests { @Throws(Exception::class) fun testLoadPictureOfTheDay() { `when`(mediaClient.getPictureOfTheDay()).thenReturn(Single.just(Media())) - val method: Method = PicOfDayAppWidget::class.java.getDeclaredMethod( - "loadPictureOfTheDay", - Context::class.java, - RemoteViews::class.java, - AppWidgetManager::class.java, - Int::class.java - ) + val method: Method = + PicOfDayAppWidget::class.java.getDeclaredMethod( + "loadPictureOfTheDay", + Context::class.java, + RemoteViews::class.java, + AppWidgetManager::class.java, + Int::class.java, + ) method.isAccessible = true method.invoke(widget, context, views, appWidgetManager, 1) } - -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikiBaseClientUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikiBaseClientUnitTest.kt index 6aa97ee6f4..a2f3d10fbf 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikiBaseClientUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikiBaseClientUnitTest.kt @@ -13,12 +13,10 @@ import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertSame import junit.framework.TestCase.assertTrue import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.mockito.Mockito.mock class WikiBaseClientUnitTest { - private val csrfTokenClient: CsrfTokenClient = mock() private val wikiBaseInterface: WikiBaseInterface = mock() private val wikiBaseClient = WikiBaseClient(wikiBaseInterface, csrfTokenClient) @@ -74,12 +72,17 @@ class WikiBaseClientUnitTest { @Test fun addLabelstoWikidata() { val mwPostResponse = mock() - whenever(wikiBaseInterface.addLabelstoWikidata( - "M123", "test", "en", "caption" - )).thenReturn(Observable.just(mwPostResponse)) + whenever( + wikiBaseInterface.addLabelstoWikidata( + "M123", + "test", + "en", + "caption", + ), + ).thenReturn(Observable.just(mwPostResponse)) val result = wikiBaseClient.addLabelsToWikidata(123L, "en", "caption").blockingFirst() assertSame(mwPostResponse, result) } -} \ No newline at end of file +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataClientTest.kt index 038fe30845..3223011447 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataClientTest.kt @@ -3,7 +3,10 @@ package fr.free.nrw.commons.wikidata import com.google.gson.Gson import com.nhaarman.mockitokotlin2.whenever import fr.free.nrw.commons.wikidata.model.PageInfo +import fr.free.nrw.commons.wikidata.model.StatementPartial import fr.free.nrw.commons.wikidata.model.WbCreateClaimResponse +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse +import fr.free.nrw.commons.wikidata.mwapi.MwQueryResult import io.reactivex.Observable import org.junit.Before import org.junit.Test @@ -11,16 +14,11 @@ import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyString import org.mockito.InjectMocks import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse -import fr.free.nrw.commons.wikidata.mwapi.MwQueryResult -import fr.free.nrw.commons.wikidata.model.Statement_partial -import org.junit.Ignore class WikidataClientTest { - @Mock internal var wikidataInterface: WikidataInterface? = null @@ -50,10 +48,13 @@ class WikidataClientTest { whenever(response.pageinfo).thenReturn(pageInfo) `when`(wikidataInterface!!.postSetClaim(anyString(), anyString(), anyString())) .thenReturn(Observable.just(response)) - whenever(gson!!.toJson(any(Statement_partial::class.java))).thenReturn("claim") - val request = mock(Statement_partial::class.java) - - val claim = wikidataClient!!.setClaim(request, "test").test() - .assertValue(1L) + whenever(gson!!.toJson(any(StatementPartial::class.java))).thenReturn("claim") + val request = mock(StatementPartial::class.java) + + val claim = + wikidataClient!! + .setClaim(request, "test") + .test() + .assertValue(1L) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt index 3530574350..c22195c7c6 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt @@ -55,11 +55,17 @@ class WikidataEditServiceTest { fun testUpdateDepictsProperty() { val fileEntityId = "12345" - whenever(wikibaseClient.getClaimIdsByProperty("M" + fileEntityId, - WikidataProperties.DEPICTS.propertyName)) - .thenReturn(Observable.just(emptyList())) - whenever(wikibaseClient.postDeleteClaims("M" + fileEntityId, - gson.toJson(Mockito.mock(RemoveClaim::class.java))) + whenever( + wikibaseClient.getClaimIdsByProperty( + "M" + fileEntityId, + WikidataProperties.DEPICTS.propertyName, + ), + ).thenReturn(Observable.just(emptyList())) + whenever( + wikibaseClient.postDeleteClaims( + "M" + fileEntityId, + gson.toJson(Mockito.mock(RemoveClaim::class.java)), + ), ).thenReturn(Observable.just(true)) wikidataEditService.updateDepictsProperty(fileEntityId, listOf()) @@ -78,7 +84,7 @@ class WikidataEditServiceTest { wikidataEditService.createClaim( wikidataPlace, uploadResult.filename, - hashMapOf() + hashMapOf(), ) } }