From c6f4649fcf1e95f84f1e58c4e8a569871af821de Mon Sep 17 00:00:00 2001 From: Nicolas Raoul Date: Wed, 18 Aug 2021 21:11:35 +0900 Subject: [PATCH 0001/1165] GSoC 2021 custom picture selector (#4534) * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * [GSoC] Image Selection (#4457) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Image selection added * Forwarded activity result to upload wizard * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Image selection added * Forwarded activity result to upload wizard * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * fixed merge errors * Documented the remaining function Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> * [GSoC] Show uploaded images differently. (#4464) * uploaded images shown differently * Loaded images before query * Handled exceptions, Made ImageLoader injectable, Document and clean code * [GSoC] Added Uploaded status table in room database. (#4476) * added Uploaded status table in room database * Added unique property, minor refractoring * Database intigrated * Database integrated * Handled result null exception * Exceptions handled and refractored * Introduced constants * moved to sealed class * No database insert on network error * queried original image * documented the code * Updated uploaded status on upload success * Image Helper test (#4485) * [GSoC] Adapter Tests (#4488) * Added FolderAdapterTest * Image Adapter Test * [GSoC] Master rebase. (#4505) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Fixes #3694 Pre-select places as depictions (#4452) * WikidataEditService: stop automatically adding WikidataPlace as a depiction When the user initiates the upload process from Nearby and also manually adds the place as a depiction, the depiction is added twice. Since this behavior is invisible to the user, it is being removed in preparation for auto-selecting the place as a depiction on the DepictsFragment screen. * DepictsFragment: auto-select place as a depiction Pass the Place reference from UploadActivity to DepictsFragment and select the corresponding DepictedItem. Using the place id, retrieve the corresponding Entity to create and select a DepictedItem. * UploadRepository: use Place from UploadItem to obtain a DepictedItem Instead of passing a Place object from UploadActivity to DepictsFragment and then passing the Place object up the chain to obtain and select a DepictedItem, retrieve the Place object directly within UploadRepository * DepictsFragment: select Place depiction when fragment becomes visible * UploadDepictsAdapter: make adapter aware of selection state Update selection state when recycled list items are automatically selected, preventing automatically selected items from appearing as unselected until they are forced to re-bind (i.e. after scrolling) * DepictsFragment: pre-select place depictions for all UploadItems If several images are selected and set to different places, pre-select all place depictions to reinforce the intended upload workflow philosophy (i.e. all images in a set are intended to be from/of the same place). See discussion in commons-app/apps-android-commons#3694 * DepictsFragment: scroll to the top every time list is updated * Typo fixes (#4461) * Fixed typo on class documentation of TextUtils * corrected comma placement in documentation * Fixed typos in comments * fix-issue-4424 (#4445) Co-authored-by: Pratham2305 * fix edit categories ui (#4414) Co-authored-by: Pratham2305 * Fix doom version issue (#4463) * Update db version * DBOpenHelper version update * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict * Fixes #4437 - Changed indentation on files with 2 spaces to 4 spaces (#4462) * Edited Project.xml to make indent size 4 * Changed files with 2 space indentation to use 4 space indentation * Edited Project.xml to make indent size 4 * changed files with 2 space indent to 4 space indent * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: neslihanturan * Use more understandable strings (#4470) * Fix #3792 Missing Column Issue (#4468) * Fix Missing Column Issue * Fix tests * Add UploadCategoriesFragment Unit Tests (#4473) * Panorama (#4467) * panoramic images fixed * made requested changes * Minor refactoring Co-authored-by: Aditya Srivastava * Localisation updates from https://translatewiki.net. * Main activity title is sometimes "Contributions", sometimes "Commons" (#4472) Fixes #4438 Replace == with equals() in onRestoreInstanceState * Localisation updates from https://translatewiki.net. * caption and description copyable (#4481) * Removed next button in quiz (#4382) * issues resolved * modification done * warning fixed * issues resolved * Button added * don't know function added * Button added * modification done * modification done * Localisation updates from https://translatewiki.net. * Added option to show and modify location while uploading (#4475) * initial commit * Everything done * minor modification * minor modification * Issues fixed * minor modifications * issue fixed * Issues fixed * Tutorial removed from log out state (#4479) * tutorial removed from log out state * Issue removed * Update changelog.md * Versioning for v3.0.2 * Fix #4482 (#4484) * Fix crash when image resolution is very high (#4483) * Localisation updates from https://translatewiki.net. * Add Contributions Fragment Unit Tests (#4490) * Fix Tests Errors (#4491) * Add UploadMediaDetailFragment Unit Tests (#4492) * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * [GSoC] Image Selection (#4457) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Image selection added * Forwarded activity result to upload wizard * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Image selection added * Forwarded activity result to upload wizard * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * fixed merge errors * Documented the remaining function Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> * [GSoC] Show uploaded images differently. (#4464) * uploaded images shown differently * Loaded images before query * Handled exceptions, Made ImageLoader injectable, Document and clean code * [GSoC] Added Uploaded status table in room database. (#4476) * added Uploaded status table in room database * Added unique property, minor refractoring * Database intigrated * Database integrated * Handled result null exception * Exceptions handled and refractored * Introduced constants * moved to sealed class * No database insert on network error * queried original image * documented the code * Updated uploaded status on upload success * Image Helper test (#4485) * [GSoC] Adapter Tests (#4488) * Added FolderAdapterTest * Image Adapter Test * merge fix * rebase fix Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> Co-authored-by: Brigham Byerly <6891883+byerlyb20@users.noreply.github.com> Co-authored-by: Jamie Brown Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul Co-authored-by: Ashar * [GSoC] Custom Selector Tests (#4494) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Fixes #3694 Pre-select places as depictions (#4452) * WikidataEditService: stop automatically adding WikidataPlace as a depiction When the user initiates the upload process from Nearby and also manually adds the place as a depiction, the depiction is added twice. Since this behavior is invisible to the user, it is being removed in preparation for auto-selecting the place as a depiction on the DepictsFragment screen. * DepictsFragment: auto-select place as a depiction Pass the Place reference from UploadActivity to DepictsFragment and select the corresponding DepictedItem. Using the place id, retrieve the corresponding Entity to create and select a DepictedItem. * UploadRepository: use Place from UploadItem to obtain a DepictedItem Instead of passing a Place object from UploadActivity to DepictsFragment and then passing the Place object up the chain to obtain and select a DepictedItem, retrieve the Place object directly within UploadRepository * DepictsFragment: select Place depiction when fragment becomes visible * UploadDepictsAdapter: make adapter aware of selection state Update selection state when recycled list items are automatically selected, preventing automatically selected items from appearing as unselected until they are forced to re-bind (i.e. after scrolling) * DepictsFragment: pre-select place depictions for all UploadItems If several images are selected and set to different places, pre-select all place depictions to reinforce the intended upload workflow philosophy (i.e. all images in a set are intended to be from/of the same place). See discussion in commons-app/apps-android-commons#3694 * DepictsFragment: scroll to the top every time list is updated * Typo fixes (#4461) * Fixed typo on class documentation of TextUtils * corrected comma placement in documentation * Fixed typos in comments * fix-issue-4424 (#4445) Co-authored-by: Pratham2305 * fix edit categories ui (#4414) Co-authored-by: Pratham2305 * Fix doom version issue (#4463) * Update db version * DBOpenHelper version update * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict * Fixes #4437 - Changed indentation on files with 2 spaces to 4 spaces (#4462) * Edited Project.xml to make indent size 4 * Changed files with 2 space indentation to use 4 space indentation * Edited Project.xml to make indent size 4 * changed files with 2 space indent to 4 space indent * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: neslihanturan * Use more understandable strings (#4470) * Fix #3792 Missing Column Issue (#4468) * Fix Missing Column Issue * Fix tests * Add UploadCategoriesFragment Unit Tests (#4473) * Panorama (#4467) * panoramic images fixed * made requested changes * Minor refactoring Co-authored-by: Aditya Srivastava * Localisation updates from https://translatewiki.net. * Main activity title is sometimes "Contributions", sometimes "Commons" (#4472) Fixes #4438 Replace == with equals() in onRestoreInstanceState * Localisation updates from https://translatewiki.net. * caption and description copyable (#4481) * Removed next button in quiz (#4382) * issues resolved * modification done * warning fixed * issues resolved * Button added * don't know function added * Button added * modification done * modification done * Localisation updates from https://translatewiki.net. * Added option to show and modify location while uploading (#4475) * initial commit * Everything done * minor modification * minor modification * Issues fixed * minor modifications * issue fixed * Issues fixed * Tutorial removed from log out state (#4479) * tutorial removed from log out state * Issue removed * Update changelog.md * Versioning for v3.0.2 * Fix #4482 (#4484) * Fix crash when image resolution is very high (#4483) * Localisation updates from https://translatewiki.net. * Add Contributions Fragment Unit Tests (#4490) * Fix Tests Errors (#4491) * Add UploadMediaDetailFragment Unit Tests (#4492) * Localisation updates from https://translatewiki.net. * Folder Fragment test * Folder Fragment test done * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * [GSoC] Image Selection (#4457) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Image selection added * Forwarded activity result to upload wizard * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Image selection added * Forwarded activity result to upload wizard * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * fixed merge errors * Documented the remaining function Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> * [GSoC] Show uploaded images differently. (#4464) * uploaded images shown differently * Loaded images before query * Handled exceptions, Made ImageLoader injectable, Document and clean code * [GSoC] Added Uploaded status table in room database. (#4476) * added Uploaded status table in room database * Added unique property, minor refractoring * Database intigrated * Database integrated * Handled result null exception * Exceptions handled and refractored * Introduced constants * moved to sealed class * No database insert on network error * queried original image * documented the code * Updated uploaded status on upload success * Image Helper test (#4485) * [GSoC] Adapter Tests (#4488) * Added FolderAdapterTest * Image Adapter Test * Folder Fragment test * Folder Fragment test done * Fragment test complete * Added Custom Selector View Model Test * ImageFileLoaderTest * Update strings.xml * Custom Selector Activiy test * Image Loader Test Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> Co-authored-by: Brigham Byerly <6891883+byerlyb20@users.noreply.github.com> Co-authored-by: Jamie Brown Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul Co-authored-by: Ashar * Image Loader Improvements (#4516) * ImageLoader Test Updated (#4517) * [GSoC] Improvement and bug Fixes (#4522) * Improvement and bug Fixes * fixed ellipsize * Saving selector state (#4526) * [GSoC] Saved Image Fragment Scroll State (#4528) * Saved Image Fragment Scroll State * Fix delete image * Fixed Delete bug * Changed custom selector icon * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * [GSoC] Image Selection (#4457) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Image selection added * Forwarded activity result to upload wizard * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Image selection added * Forwarded activity result to upload wizard * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * fixed merge errors * Documented the remaining function Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> * [GSoC] Show uploaded images differently. (#4464) * uploaded images shown differently * Loaded images before query * Handled exceptions, Made ImageLoader injectable, Document and clean code * [GSoC] Added Uploaded status table in room database. (#4476) * added Uploaded status table in room database * Added unique property, minor refractoring * Database intigrated * Database integrated * Handled result null exception * Exceptions handled and refractored * Introduced constants * moved to sealed class * No database insert on network error * queried original image * documented the code * Updated uploaded status on upload success * Image Helper test (#4485) * [GSoC] Adapter Tests (#4488) * Added FolderAdapterTest * Image Adapter Test * [GSoC] Master rebase. (#4505) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Fixes #3694 Pre-select places as depictions (#4452) * WikidataEditService: stop automatically adding WikidataPlace as a depiction When the user initiates the upload process from Nearby and also manually adds the place as a depiction, the depiction is added twice. Since this behavior is invisible to the user, it is being removed in preparation for auto-selecting the place as a depiction on the DepictsFragment screen. * DepictsFragment: auto-select place as a depiction Pass the Place reference from UploadActivity to DepictsFragment and select the corresponding DepictedItem. Using the place id, retrieve the corresponding Entity to create and select a DepictedItem. * UploadRepository: use Place from UploadItem to obtain a DepictedItem Instead of passing a Place object from UploadActivity to DepictsFragment and then passing the Place object up the chain to obtain and select a DepictedItem, retrieve the Place object directly within UploadRepository * DepictsFragment: select Place depiction when fragment becomes visible * UploadDepictsAdapter: make adapter aware of selection state Update selection state when recycled list items are automatically selected, preventing automatically selected items from appearing as unselected until they are forced to re-bind (i.e. after scrolling) * DepictsFragment: pre-select place depictions for all UploadItems If several images are selected and set to different places, pre-select all place depictions to reinforce the intended upload workflow philosophy (i.e. all images in a set are intended to be from/of the same place). See discussion in commons-app/apps-android-commons#3694 * DepictsFragment: scroll to the top every time list is updated * Typo fixes (#4461) * Fixed typo on class documentation of TextUtils * corrected comma placement in documentation * Fixed typos in comments * fix-issue-4424 (#4445) Co-authored-by: Pratham2305 * fix edit categories ui (#4414) Co-authored-by: Pratham2305 * Fix doom version issue (#4463) * Update db version * DBOpenHelper version update * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict * Fixes #4437 - Changed indentation on files with 2 spaces to 4 spaces (#4462) * Edited Project.xml to make indent size 4 * Changed files with 2 space indentation to use 4 space indentation * Edited Project.xml to make indent size 4 * changed files with 2 space indent to 4 space indent * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: neslihanturan * Use more understandable strings (#4470) * Fix #3792 Missing Column Issue (#4468) * Fix Missing Column Issue * Fix tests * Add UploadCategoriesFragment Unit Tests (#4473) * Panorama (#4467) * panoramic images fixed * made requested changes * Minor refactoring Co-authored-by: Aditya Srivastava * Localisation updates from https://translatewiki.net. * Main activity title is sometimes "Contributions", sometimes "Commons" (#4472) Fixes #4438 Replace == with equals() in onRestoreInstanceState * Localisation updates from https://translatewiki.net. * caption and description copyable (#4481) * Removed next button in quiz (#4382) * issues resolved * modification done * warning fixed * issues resolved * Button added * don't know function added * Button added * modification done * modification done * Localisation updates from https://translatewiki.net. * Added option to show and modify location while uploading (#4475) * initial commit * Everything done * minor modification * minor modification * Issues fixed * minor modifications * issue fixed * Issues fixed * Tutorial removed from log out state (#4479) * tutorial removed from log out state * Issue removed * Update changelog.md * Versioning for v3.0.2 * Fix #4482 (#4484) * Fix crash when image resolution is very high (#4483) * Localisation updates from https://translatewiki.net. * Add Contributions Fragment Unit Tests (#4490) * Fix Tests Errors (#4491) * Add UploadMediaDetailFragment Unit Tests (#4492) * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * [GSoC] Image Selection (#4457) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Image selection added * Forwarded activity result to upload wizard * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Image selection added * Forwarded activity result to upload wizard * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * fixed merge errors * Documented the remaining function Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> * [GSoC] Show uploaded images differently. (#4464) * uploaded images shown differently * Loaded images before query * Handled exceptions, Made ImageLoader injectable, Document and clean code * [GSoC] Added Uploaded status table in room database. (#4476) * added Uploaded status table in room database * Added unique property, minor refractoring * Database intigrated * Database integrated * Handled result null exception * Exceptions handled and refractored * Introduced constants * moved to sealed class * No database insert on network error * queried original image * documented the code * Updated uploaded status on upload success * Image Helper test (#4485) * [GSoC] Adapter Tests (#4488) * Added FolderAdapterTest * Image Adapter Test * merge fix * rebase fix Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> Co-authored-by: Brigham Byerly <6891883+byerlyb20@users.noreply.github.com> Co-authored-by: Jamie Brown Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul Co-authored-by: Ashar * [GSoC] Custom Selector Tests (#4494) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Fixes #3694 Pre-select places as depictions (#4452) * WikidataEditService: stop automatically adding WikidataPlace as a depiction When the user initiates the upload process from Nearby and also manually adds the place as a depiction, the depiction is added twice. Since this behavior is invisible to the user, it is being removed in preparation for auto-selecting the place as a depiction on the DepictsFragment screen. * DepictsFragment: auto-select place as a depiction Pass the Place reference from UploadActivity to DepictsFragment and select the corresponding DepictedItem. Using the place id, retrieve the corresponding Entity to create and select a DepictedItem. * UploadRepository: use Place from UploadItem to obtain a DepictedItem Instead of passing a Place object from UploadActivity to DepictsFragment and then passing the Place object up the chain to obtain and select a DepictedItem, retrieve the Place object directly within UploadRepository * DepictsFragment: select Place depiction when fragment becomes visible * UploadDepictsAdapter: make adapter aware of selection state Update selection state when recycled list items are automatically selected, preventing automatically selected items from appearing as unselected until they are forced to re-bind (i.e. after scrolling) * DepictsFragment: pre-select place depictions for all UploadItems If several images are selected and set to different places, pre-select all place depictions to reinforce the intended upload workflow philosophy (i.e. all images in a set are intended to be from/of the same place). See discussion in commons-app/apps-android-commons#3694 * DepictsFragment: scroll to the top every time list is updated * Typo fixes (#4461) * Fixed typo on class documentation of TextUtils * corrected comma placement in documentation * Fixed typos in comments * fix-issue-4424 (#4445) Co-authored-by: Pratham2305 * fix edit categories ui (#4414) Co-authored-by: Pratham2305 * Fix doom version issue (#4463) * Update db version * DBOpenHelper version update * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict * Fixes #4437 - Changed indentation on files with 2 spaces to 4 spaces (#4462) * Edited Project.xml to make indent size 4 * Changed files with 2 space indentation to use 4 space indentation * Edited Project.xml to make indent size 4 * changed files with 2 space indent to 4 space indent * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: neslihanturan * Use more understandable strings (#4470) * Fix #3792 Missing Column Issue (#4468) * Fix Missing Column Issue * Fix tests * Add UploadCategoriesFragment Unit Tests (#4473) * Panorama (#4467) * panoramic images fixed * made requested changes * Minor refactoring Co-authored-by: Aditya Srivastava * Localisation updates from https://translatewiki.net. * Main activity title is sometimes "Contributions", sometimes "Commons" (#4472) Fixes #4438 Replace == with equals() in onRestoreInstanceState * Localisation updates from https://translatewiki.net. * caption and description copyable (#4481) * Removed next button in quiz (#4382) * issues resolved * modification done * warning fixed * issues resolved * Button added * don't know function added * Button added * modification done * modification done * Localisation updates from https://translatewiki.net. * Added option to show and modify location while uploading (#4475) * initial commit * Everything done * minor modification * minor modification * Issues fixed * minor modifications * issue fixed * Issues fixed * Tutorial removed from log out state (#4479) * tutorial removed from log out state * Issue removed * Update changelog.md * Versioning for v3.0.2 * Fix #4482 (#4484) * Fix crash when image resolution is very high (#4483) * Localisation updates from https://translatewiki.net. * Add Contributions Fragment Unit Tests (#4490) * Fix Tests Errors (#4491) * Add UploadMediaDetailFragment Unit Tests (#4492) * Localisation updates from https://translatewiki.net. * Folder Fragment test * Folder Fragment test done * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * [GSoC] Image Selection (#4457) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Image selection added * Forwarded activity result to upload wizard * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Image selection added * Forwarded activity result to upload wizard * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * fixed merge errors * Documented the remaining function Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> * [GSoC] Show uploaded images differently. (#4464) * uploaded images shown differently * Loaded images before query * Handled exceptions, Made ImageLoader injectable, Document and clean code * [GSoC] Added Uploaded status table in room database. (#4476) * added Uploaded status table in room database * Added unique property, minor refractoring * Database intigrated * Database integrated * Handled result null exception * Exceptions handled and refractored * Introduced constants * moved to sealed class * No database insert on network error * queried original image * documented the code * Updated uploaded status on upload success * Image Helper test (#4485) * [GSoC] Adapter Tests (#4488) * Added FolderAdapterTest * Image Adapter Test * Folder Fragment test * Folder Fragment test done * Fragment test complete * Added Custom Selector View Model Test * ImageFileLoaderTest * Update strings.xml * Custom Selector Activiy test * Image Loader Test Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> Co-authored-by: Brigham Byerly <6891883+byerlyb20@users.noreply.github.com> Co-authored-by: Jamie Brown Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul Co-authored-by: Ashar * Image Loader Improvements (#4516) * ImageLoader Test Updated (#4517) * [GSoC] Improvement and bug Fixes (#4522) * Improvement and bug Fixes * fixed ellipsize * Saving selector state (#4526) * [GSoC] Saved Image Fragment Scroll State (#4528) * Saved Image Fragment Scroll State * Fix delete image * Fixed Delete bug * Changed custom selector icon * rebase fix * Tests updated (#4538) * orientation fixed (#4540) * refractoring (#4541) * refractoring (#4545) * [GSoC] Welcome Dialog (#4546) * Welcome Dialog * Condition Fix * Orientation, back button Fix * [GSoC] Image preview (#4559) * Image preview * refractor * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * [GSoC] Image Selection (#4457) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Image selection added * Forwarded activity result to upload wizard * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Image selection added * Forwarded activity result to upload wizard * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * fixed merge errors * Documented the remaining function Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> * [GSoC] Show uploaded images differently. (#4464) * uploaded images shown differently * Loaded images before query * Handled exceptions, Made ImageLoader injectable, Document and clean code * [GSoC] Added Uploaded status table in room database. (#4476) * added Uploaded status table in room database * Added unique property, minor refractoring * Database intigrated * Database integrated * Handled result null exception * Exceptions handled and refractored * Introduced constants * moved to sealed class * No database insert on network error * queried original image * documented the code * Updated uploaded status on upload success * Image Helper test (#4485) * [GSoC] Adapter Tests (#4488) * Added FolderAdapterTest * Image Adapter Test * [GSoC] Master rebase. (#4505) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Fixes #3694 Pre-select places as depictions (#4452) * WikidataEditService: stop automatically adding WikidataPlace as a depiction When the user initiates the upload process from Nearby and also manually adds the place as a depiction, the depiction is added twice. Since this behavior is invisible to the user, it is being removed in preparation for auto-selecting the place as a depiction on the DepictsFragment screen. * DepictsFragment: auto-select place as a depiction Pass the Place reference from UploadActivity to DepictsFragment and select the corresponding DepictedItem. Using the place id, retrieve the corresponding Entity to create and select a DepictedItem. * UploadRepository: use Place from UploadItem to obtain a DepictedItem Instead of passing a Place object from UploadActivity to DepictsFragment and then passing the Place object up the chain to obtain and select a DepictedItem, retrieve the Place object directly within UploadRepository * DepictsFragment: select Place depiction when fragment becomes visible * UploadDepictsAdapter: make adapter aware of selection state Update selection state when recycled list items are automatically selected, preventing automatically selected items from appearing as unselected until they are forced to re-bind (i.e. after scrolling) * DepictsFragment: pre-select place depictions for all UploadItems If several images are selected and set to different places, pre-select all place depictions to reinforce the intended upload workflow philosophy (i.e. all images in a set are intended to be from/of the same place). See discussion in commons-app/apps-android-commons#3694 * DepictsFragment: scroll to the top every time list is updated * Typo fixes (#4461) * Fixed typo on class documentation of TextUtils * corrected comma placement in documentation * Fixed typos in comments * fix-issue-4424 (#4445) Co-authored-by: Pratham2305 * fix edit categories ui (#4414) Co-authored-by: Pratham2305 * Fix doom version issue (#4463) * Update db version * DBOpenHelper version update * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict * Fixes #4437 - Changed indentation on files with 2 spaces to 4 spaces (#4462) * Edited Project.xml to make indent size 4 * Changed files with 2 space indentation to use 4 space indentation * Edited Project.xml to make indent size 4 * changed files with 2 space indent to 4 space indent * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: neslihanturan * Use more understandable strings (#4470) * Fix #3792 Missing Column Issue (#4468) * Fix Missing Column Issue * Fix tests * Add UploadCategoriesFragment Unit Tests (#4473) * Panorama (#4467) * panoramic images fixed * made requested changes * Minor refactoring Co-authored-by: Aditya Srivastava * Localisation updates from https://translatewiki.net. * Main activity title is sometimes "Contributions", sometimes "Commons" (#4472) Fixes #4438 Replace == with equals() in onRestoreInstanceState * Localisation updates from https://translatewiki.net. * caption and description copyable (#4481) * Removed next button in quiz (#4382) * issues resolved * modification done * warning fixed * issues resolved * Button added * don't know function added * Button added * modification done * modification done * Localisation updates from https://translatewiki.net. * Added option to show and modify location while uploading (#4475) * initial commit * Everything done * minor modification * minor modification * Issues fixed * minor modifications * issue fixed * Issues fixed * Tutorial removed from log out state (#4479) * tutorial removed from log out state * Issue removed * Update changelog.md * Versioning for v3.0.2 * Fix #4482 (#4484) * Fix crash when image resolution is very high (#4483) * Localisation updates from https://translatewiki.net. * Add Contributions Fragment Unit Tests (#4490) * Fix Tests Errors (#4491) * Add UploadMediaDetailFragment Unit Tests (#4492) * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * [GSoC] Image Selection (#4457) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Image selection added * Forwarded activity result to upload wizard * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Image selection added * Forwarded activity result to upload wizard * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * fixed merge errors * Documented the remaining function Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> * [GSoC] Show uploaded images differently. (#4464) * uploaded images shown differently * Loaded images before query * Handled exceptions, Made ImageLoader injectable, Document and clean code * [GSoC] Added Uploaded status table in room database. (#4476) * added Uploaded status table in room database * Added unique property, minor refractoring * Database intigrated * Database integrated * Handled result null exception * Exceptions handled and refractored * Introduced constants * moved to sealed class * No database insert on network error * queried original image * documented the code * Updated uploaded status on upload success * Image Helper test (#4485) * [GSoC] Adapter Tests (#4488) * Added FolderAdapterTest * Image Adapter Test * merge fix * rebase fix Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> Co-authored-by: Brigham Byerly <6891883+byerlyb20@users.noreply.github.com> Co-authored-by: Jamie Brown Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul Co-authored-by: Ashar * [GSoC] Custom Selector Tests (#4494) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Fixes #3694 Pre-select places as depictions (#4452) * WikidataEditService: stop automatically adding WikidataPlace as a depiction When the user initiates the upload process from Nearby and also manually adds the place as a depiction, the depiction is added twice. Since this behavior is invisible to the user, it is being removed in preparation for auto-selecting the place as a depiction on the DepictsFragment screen. * DepictsFragment: auto-select place as a depiction Pass the Place reference from UploadActivity to DepictsFragment and select the corresponding DepictedItem. Using the place id, retrieve the corresponding Entity to create and select a DepictedItem. * UploadRepository: use Place from UploadItem to obtain a DepictedItem Instead of passing a Place object from UploadActivity to DepictsFragment and then passing the Place object up the chain to obtain and select a DepictedItem, retrieve the Place object directly within UploadRepository * DepictsFragment: select Place depiction when fragment becomes visible * UploadDepictsAdapter: make adapter aware of selection state Update selection state when recycled list items are automatically selected, preventing automatically selected items from appearing as unselected until they are forced to re-bind (i.e. after scrolling) * DepictsFragment: pre-select place depictions for all UploadItems If several images are selected and set to different places, pre-select all place depictions to reinforce the intended upload workflow philosophy (i.e. all images in a set are intended to be from/of the same place). See discussion in commons-app/apps-android-commons#3694 * DepictsFragment: scroll to the top every time list is updated * Typo fixes (#4461) * Fixed typo on class documentation of TextUtils * corrected comma placement in documentation * Fixed typos in comments * fix-issue-4424 (#4445) Co-authored-by: Pratham2305 * fix edit categories ui (#4414) Co-authored-by: Pratham2305 * Fix doom version issue (#4463) * Update db version * DBOpenHelper version update * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict * Fixes #4437 - Changed indentation on files with 2 spaces to 4 spaces (#4462) * Edited Project.xml to make indent size 4 * Changed files with 2 space indentation to use 4 space indentation * Edited Project.xml to make indent size 4 * changed files with 2 space indent to 4 space indent * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: neslihanturan * Use more understandable strings (#4470) * Fix #3792 Missing Column Issue (#4468) * Fix Missing Column Issue * Fix tests * Add UploadCategoriesFragment Unit Tests (#4473) * Panorama (#4467) * panoramic images fixed * made requested changes * Minor refactoring Co-authored-by: Aditya Srivastava * Localisation updates from https://translatewiki.net. * Main activity title is sometimes "Contributions", sometimes "Commons" (#4472) Fixes #4438 Replace == with equals() in onRestoreInstanceState * Localisation updates from https://translatewiki.net. * caption and description copyable (#4481) * Removed next button in quiz (#4382) * issues resolved * modification done * warning fixed * issues resolved * Button added * don't know function added * Button added * modification done * modification done * Localisation updates from https://translatewiki.net. * Added option to show and modify location while uploading (#4475) * initial commit * Everything done * minor modification * minor modification * Issues fixed * minor modifications * issue fixed * Issues fixed * Tutorial removed from log out state (#4479) * tutorial removed from log out state * Issue removed * Update changelog.md * Versioning for v3.0.2 * Fix #4482 (#4484) * Fix crash when image resolution is very high (#4483) * Localisation updates from https://translatewiki.net. * Add Contributions Fragment Unit Tests (#4490) * Fix Tests Errors (#4491) * Add UploadMediaDetailFragment Unit Tests (#4492) * Localisation updates from https://translatewiki.net. * Folder Fragment test * Folder Fragment test done * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * [GSoC] Image Selection (#4457) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Image selection added * Forwarded activity result to upload wizard * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Image selection added * Forwarded activity result to upload wizard * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * fixed merge errors * Documented the remaining function Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> * [GSoC] Show uploaded images differently. (#4464) * uploaded images shown differently * Loaded images before query * Handled exceptions, Made ImageLoader injectable, Document and clean code * [GSoC] Added Uploaded status table in room database. (#4476) * added Uploaded status table in room database * Added unique property, minor refractoring * Database intigrated * Database integrated * Handled result null exception * Exceptions handled and refractored * Introduced constants * moved to sealed class * No database insert on network error * queried original image * documented the code * Updated uploaded status on upload success * Image Helper test (#4485) * [GSoC] Adapter Tests (#4488) * Added FolderAdapterTest * Image Adapter Test * Folder Fragment test * Folder Fragment test done * Fragment test complete * Added Custom Selector View Model Test * ImageFileLoaderTest * Update strings.xml * Custom Selector Activiy test * Image Loader Test Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> Co-authored-by: Brigham Byerly <6891883+byerlyb20@users.noreply.github.com> Co-authored-by: Jamie Brown Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul Co-authored-by: Ashar * Image Loader Improvements (#4516) * ImageLoader Test Updated (#4517) * [GSoC] Improvement and bug Fixes (#4522) * Improvement and bug Fixes * fixed ellipsize * Saving selector state (#4526) * [GSoC] Saved Image Fragment Scroll State (#4528) * Saved Image Fragment Scroll State * Fix delete image * Fixed Delete bug * Changed custom selector icon * rebase fix * [GSoC] Master rebase. (#4505) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Fixes #3694 Pre-select places as depictions (#4452) * WikidataEditService: stop automatically adding WikidataPlace as a depiction When the user initiates the upload process from Nearby and also manually adds the place as a depiction, the depiction is added twice. Since this behavior is invisible to the user, it is being removed in preparation for auto-selecting the place as a depiction on the DepictsFragment screen. * DepictsFragment: auto-select place as a depiction Pass the Place reference from UploadActivity to DepictsFragment and select the corresponding DepictedItem. Using the place id, retrieve the corresponding Entity to create and select a DepictedItem. * UploadRepository: use Place from UploadItem to obtain a DepictedItem Instead of passing a Place object from UploadActivity to DepictsFragment and then passing the Place object up the chain to obtain and select a DepictedItem, retrieve the Place object directly within UploadRepository * DepictsFragment: select Place depiction when fragment becomes visible * UploadDepictsAdapter: make adapter aware of selection state Update selection state when recycled list items are automatically selected, preventing automatically selected items from appearing as unselected until they are forced to re-bind (i.e. after scrolling) * DepictsFragment: pre-select place depictions for all UploadItems If several images are selected and set to different places, pre-select all place depictions to reinforce the intended upload workflow philosophy (i.e. all images in a set are intended to be from/of the same place). See discussion in commons-app/apps-android-commons#3694 * DepictsFragment: scroll to the top every time list is updated * Typo fixes (#4461) * Fixed typo on class documentation of TextUtils * corrected comma placement in documentation * Fixed typos in comments * fix-issue-4424 (#4445) Co-authored-by: Pratham2305 * fix edit categories ui (#4414) Co-authored-by: Pratham2305 * Fix doom version issue (#4463) * Update db version * DBOpenHelper version update * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict * Fixes #4437 - Changed indentation on files with 2 spaces to 4 spaces (#4462) * Edited Project.xml to make indent size 4 * Changed files with 2 space indentation to use 4 space indentation * Edited Project.xml to make indent size 4 * changed files with 2 space indent to 4 space indent * fix :Back Pressed Event not work in Explore tab when user not login (#4404) * fix :Back Pressed Event not work in Explore tab * minor changes * fix :Upload count or number of contribution does not get updated when media is successful uploaded (#4399) * * fix:Number of Contributions not updated * Add javadocs * minor changes * made minor changes * String was nonsense and untranslatible, fixed (#4466) * Ability to show captions and descriptions in all entered languages (#4355) * implement Ability to show captions and descriptions in all entered languages *Add Javadoc * handle Back event of fragment(mediaDetailFragment) * fix minor bugs * add internationalization * revert previous changes * fix visibility bug * resolve conflict Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: neslihanturan * Use more understandable strings (#4470) * Fix #3792 Missing Column Issue (#4468) * Fix Missing Column Issue * Fix tests * Add UploadCategoriesFragment Unit Tests (#4473) * Panorama (#4467) * panoramic images fixed * made requested changes * Minor refactoring Co-authored-by: Aditya Srivastava * Localisation updates from https://translatewiki.net. * Main activity title is sometimes "Contributions", sometimes "Commons" (#4472) Fixes #4438 Replace == with equals() in onRestoreInstanceState * Localisation updates from https://translatewiki.net. * caption and description copyable (#4481) * Removed next button in quiz (#4382) * issues resolved * modification done * warning fixed * issues resolved * Button added * don't know function added * Button added * modification done * modification done * Localisation updates from https://translatewiki.net. * Added option to show and modify location while uploading (#4475) * initial commit * Everything done * minor modification * minor modification * Issues fixed * minor modifications * issue fixed * Issues fixed * Tutorial removed from log out state (#4479) * tutorial removed from log out state * Issue removed * Update changelog.md * Versioning for v3.0.2 * Fix #4482 (#4484) * Fix crash when image resolution is very high (#4483) * Localisation updates from https://translatewiki.net. * Add Contributions Fragment Unit Tests (#4490) * Fix Tests Errors (#4491) * Add UploadMediaDetailFragment Unit Tests (#4492) * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * [GSoC] Image Selection (#4457) * Localisation updates from https://translatewiki.net. * Fixes #4357 After switching to different account, contributions screen shows pictures of previous account (#4421) * Update UploadMediaDetailFragment.java * Update LoginActivity.java Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account * Revert "Update UploadMediaDetailFragment.java" This reverts commit b1b4257f205b022ffaadee9f947357e5fc04c337. Co-authored-by: Obsidian_zero <1198474846@qq.com> * Remove unnecessary whitespace from a message (#4439) * Merge v3.0.1 into master (#4446) * Versioning and changelog for v3.0.0 (#4152) * Versioning for v3.0.0 * Update changelog.md * Handled migration 8-9-10 in BookmarksLocationDao (#4154) * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * #Fixes #4141 - Handled migrations for BookmarkLocationsDao from 8-9-10 * Fixes #4179 (#4180) * Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked() * Updated >ContributionsFragment#backButtonClicked() to handle back press properly * Fixes #4179 (#4181) * Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed() * Cherrypick for hotfix3.1 (#4205) * Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list. * fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146) * fixed bug:app crashes on viewing review in Review Fragment #4135 * Fixed the issue with back button in contribution tab. (#4177) Co-authored-by: Pratham2305 * Fixed the issue with back navigation button on toolbar in explore tab. (#4175) * Fix (#4148) Issues on theme change * fixed themeChange crashes * fixed comments * Overlooked the title bar Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> * Fixes #4173 (#4396) * Fix #4147 Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390) * Update query to fetch descriptions * Make description added to NearbyResultItem * Make string operations to display description and label in a combined way * Fix reviews, remove long description from list and swap label and description texts * Fix repeated information issue * Fix double information issue * fix style issues * Remove douplicated information * Changes made (#4354) * Remove nonexistent method * Fix #4283 IllegalStateException (#4440) * Fix #4283 IllegalStateException * Fix flickering issue * Versioning for v3.0.1 * Update changelog.md Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> * Localisation updates from https://translatewiki.net. * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Added a feature for editing coordinates (#4418) * not * Place Picker added * Pick location and API call linked * minor warnings resolved * Code conventions followed * issue fixed * Wikitext edited properly * minor modification * Location Picker added * Bottom sheet removed * Location picker fully implemented * credit added * credit added * issues fixed * issues fixed * minor issue fixed * Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451) * Localisation updates from https://translatewiki.net. * Fixes 4344 - Duplicate Uploads (#4442) * Fixes 4344 - Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries. * Update states to IN_PROGRESS before uploads are processed * Image selection added * Forwarded activity result to upload wizard * Initialised xmls, made folder and image item. * xmls done * xmls completed * removed unwanted attribute * Created models, adapters and view models (#4441) * created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * Image selection added * Forwarded activity result to upload wizard * [GSOC] Added Image Fetch (#4449) * Added basic Fetch * added permission request * Folder count rectified * Loaded thumbnail * disabled overlay * Added sha1 function * Documented the code * fixed merge errors * Documented the remaining function Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> * [GSoC] Show uploaded images differently. (#4464) * uploaded images shown differently * Loaded images before query * Handled exceptions, Made ImageLoader injectable, Document and clean code * [GSoC] Added Uploaded status table in room database. (#4476) * added Uploaded status table in room database * Added unique property, minor refractoring * Database intigrated * Database integrated * Handled result null exception * Exceptions handled and refractored * Introduced constants * moved to sealed class * No database insert on network error * queried original image * documented the code * Updated uploaded status on upload success * Image Helper test (#4485) * [GSoC] Adapter Tests (#4488) * Added FolderAdapterTest * Image Adapter Test * merge fix * rebase fix Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> Co-authored-by: Brigham Byerly <6891883+byerlyb20@users.noreply.github.com> Co-authored-by: Jamie Brown Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul Co-authored-by: Ashar * Tests updated (#4538) * orientation fixed (#4540) * refractoring (#4541) * refractoring (#4545) * [GSoC] Welcome Dialog (#4546) * Welcome Dialog * Condition Fix * Orientation, back button Fix * [GSoC] Image preview (#4559) * Image preview * refractor * update database version * remove duplicates Co-authored-by: Aditya Srivastava Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: translatewiki.net Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com> Co-authored-by: Obsidian_zero <1198474846@qq.com> Co-authored-by: Amir E. Aharoni Co-authored-by: Josephine Lim Co-authored-by: Ashish Co-authored-by: neslihanturan Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com> Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com> Co-authored-by: Pratham2305 Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com> Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> Co-authored-by: Brigham Byerly <6891883+byerlyb20@users.noreply.github.com> Co-authored-by: Jamie Brown Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Ashar --- app/build.gradle | 7 +- app/src/main/AndroidManifest.xml | 6 + .../nrw/commons/contributions/Contribution.kt | 6 +- .../contributions/ContributionController.java | 14 + .../ContributionsListFragment.java | 51 +++- .../customselector/database/UploadedDao.kt | 56 ++++ .../customselector/database/UploadedStatus.kt | 39 +++ .../customselector/helper/ImageHelper.kt | 79 +++++ .../listeners/FolderClickListener.kt | 15 + .../listeners/ImageLoaderListener.kt | 22 ++ .../listeners/ImageSelectListener.kt | 22 ++ .../customselector/model/CallbackStatus.kt | 22 ++ .../commons/customselector/model/Folder.kt | 47 +++ .../nrw/commons/customselector/model/Image.kt | 128 ++++++++ .../commons/customselector/model/Result.kt | 16 + .../ui/adapter/FolderAdapter.kt | 163 ++++++++++ .../customselector/ui/adapter/ImageAdapter.kt | 236 +++++++++++++++ .../ui/adapter/RecyclerViewAdapter.kt | 12 + .../ui/selector/CustomSelectorActivity.kt | 235 +++++++++++++++ .../ui/selector/CustomSelectorViewModel.kt | 58 ++++ .../CustomSelectorViewModelFactory.kt | 17 ++ .../ui/selector/FolderFragment.kt | 142 +++++++++ .../ui/selector/ImageFileLoader.kt | 112 +++++++ .../ui/selector/ImageFragment.kt | 210 +++++++++++++ .../customselector/ui/selector/ImageLoader.kt | 280 ++++++++++++++++++ .../free/nrw/commons/data/DBOpenHelper.java | 2 +- .../fr/free/nrw/commons/db/AppDatabase.kt | 5 +- .../nrw/commons/di/ActivityBuilderModule.java | 4 + .../commons/di/CommonsApplicationModule.java | 22 +- .../nrw/commons/di/FragmentBuilderModule.java | 8 + .../explore/ExploreListRootFragment.java | 2 +- .../nrw/commons/filepicker/Constants.java | 1 + .../nrw/commons/filepicker/FilePicker.java | 62 +++- .../nrw/commons/filepicker/PickedFiles.java | 68 ++++- .../media/MediaDetailPagerFragment.java | 1 + .../free/nrw/commons/upload/FileProcessor.kt | 4 +- .../free/nrw/commons/upload/UploadItem.java | 19 +- .../free/nrw/commons/upload/UploadModel.java | 3 +- .../nrw/commons/upload/worker/UploadWorker.kt | 34 +++ .../main/res/drawable-ldpi/circle_shape.xml | 4 + app/src/main/res/drawable-ldpi/commons.xml | 62 ++++ .../main/res/drawable/ic_arrow_back_black.xml | 10 + .../res/drawable/ic_custom_image_picker.xml | 3 + app/src/main/res/drawable/ic_done_black.xml | 10 + app/src/main/res/drawable/ic_done_white.xml | 5 + .../res/layout/activity_custom_selector.xml | 19 ++ .../bottom_container_location_picker.xml | 1 - .../layout/custom_selector_info_dialog.xml | 133 +++++++++ .../res/layout/custom_selector_toolbar.xml | 56 ++++ .../layout/fragment_contributions_list.xml | 13 + .../res/layout/fragment_custom_selector.xml | 41 +++ .../layout/item_custom_selector_folder.xml | 77 +++++ .../res/layout/item_custom_selector_image.xml | 88 ++++++ app/src/main/res/values/attrs.xml | 2 + app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 11 +- app/src/main/res/values/styles.xml | 4 + .../customselector/helper/ImageHelperTest.kt | 55 ++++ .../ui/adapter/FolderAdapterTest.kt | 99 +++++++ .../ui/adapter/ImageAdapterTest.kt | 145 +++++++++ .../ui/selector/CustomSelectorActivityTest.kt | 119 ++++++++ .../selector/CustomSelectorViewModelTest.kt | 41 +++ .../ui/selector/FolderFragmentTest.kt | 141 +++++++++ .../ui/selector/ImageFileLoaderTest.kt | 123 ++++++++ .../ui/selector/ImageFragmentTest.kt | 166 +++++++++++ .../ui/selector/ImageLoaderTest.kt | 220 ++++++++++++++ .../nrw/commons/filepicker/FilePickerTest.kt | 10 + app/src/test/resources/imageLoaderTestFile | 0 68 files changed, 3854 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedDao.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatus.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/helper/ImageHelper.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/listeners/FolderClickListener.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageLoaderListener.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageSelectListener.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/model/CallbackStatus.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/model/Folder.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/model/Image.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/model/Result.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapter.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/RecyclerViewAdapter.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModel.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelFactory.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/FolderFragment.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoader.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageLoader.kt create mode 100644 app/src/main/res/drawable-ldpi/circle_shape.xml create mode 100644 app/src/main/res/drawable-ldpi/commons.xml create mode 100644 app/src/main/res/drawable/ic_arrow_back_black.xml create mode 100644 app/src/main/res/drawable/ic_custom_image_picker.xml create mode 100644 app/src/main/res/drawable/ic_done_black.xml create mode 100644 app/src/main/res/drawable/ic_done_white.xml create mode 100644 app/src/main/res/layout/activity_custom_selector.xml create mode 100644 app/src/main/res/layout/custom_selector_info_dialog.xml create mode 100644 app/src/main/res/layout/custom_selector_toolbar.xml create mode 100644 app/src/main/res/layout/fragment_custom_selector.xml create mode 100644 app/src/main/res/layout/item_custom_selector_folder.xml create mode 100644 app/src/main/res/layout/item_custom_selector_image.xml create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/customselector/helper/ImageHelperTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapterTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapterTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivityTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/FolderFragmentTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoaderTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFragmentTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageLoaderTest.kt create mode 100644 app/src/test/resources/imageLoaderTestFile diff --git a/app/build.gradle b/app/build.gradle index c6f34cc68..0ccc38f60 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,6 +93,7 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1" testImplementation 'com.facebook.soloader:soloader:0.9.0' + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2" // Android testing androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION" @@ -142,6 +143,10 @@ dependencies { def work_version = "2.4.0" // Kotlin + coroutines implementation "androidx.work:work-runtime-ktx:$work_version" + + //Glide + implementation 'com.github.bumptech.glide:glide:4.12.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' } android { @@ -204,7 +209,7 @@ android { } } debug { - minifyEnabled true + minifyEnabled false testCoverageEnabled project.hasProperty('coverage') proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' testProguardFile 'test-proguard-rules.txt' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cc6576aad..a2c33778f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -110,6 +110,12 @@ + + FilePicker.openCustomSelector(activity, 0), + R.string.storage_permission_title, + R.string.write_storage_permission_rationale); + } + + /** * Open chooser for gallery uploads */ diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java index 5cc900931..44b042436 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java @@ -5,6 +5,7 @@ import static android.view.View.VISIBLE; import static fr.free.nrw.commons.di.NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; @@ -29,13 +30,16 @@ import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener; import androidx.recyclerview.widget.SimpleItemAnimator; import butterknife.BindView; import butterknife.ButterKnife; +import butterknife.OnClick; import com.google.android.material.floatingactionbutton.FloatingActionButton; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; +import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.utils.DialogUtil; import fr.free.nrw.commons.media.MediaClient; +import fr.free.nrw.commons.utils.SystemThemeUtils; import fr.free.nrw.commons.utils.ViewUtil; import java.util.Locale; import javax.inject.Inject; @@ -52,7 +56,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl WikipediaInstructionsDialogFragment.Callback { private static final String RV_STATE = "rv_scroll_state"; - + @BindView(R.id.contributionsList) RecyclerView rvContributionsList; @BindView(R.id.loadingContributionsProgressBar) @@ -67,6 +71,11 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl TextView noContributionsYet; @BindView(R.id.fab_layout) LinearLayout fab_layout; + @BindView(R.id.fab_custom_gallery) + FloatingActionButton fabCustomGallery; + + @Inject + SystemThemeUtils systemThemeUtils; @Inject ContributionController controller; @@ -260,23 +269,35 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl }); } + /** + * Launch Custom Selector. + */ + @OnClick(R.id.fab_custom_gallery) + void launchCustomSelector(){ + controller.initiateCustomGalleryPickWithPermission(getActivity()); + } + private void animateFAB(final boolean isFabOpen) { this.isFabOpen = !isFabOpen; if (fabPlus.isShown()) { - if (isFabOpen) { - fabPlus.startAnimation(rotate_backward); - fabCamera.startAnimation(fab_close); - fabGallery.startAnimation(fab_close); - fabCamera.hide(); - fabGallery.hide(); - } else { - fabPlus.startAnimation(rotate_forward); - fabCamera.startAnimation(fab_open); - fabGallery.startAnimation(fab_open); - fabCamera.show(); - fabGallery.show(); - } - this.isFabOpen = !isFabOpen; + if (isFabOpen) { + fabPlus.startAnimation(rotate_backward); + fabCamera.startAnimation(fab_close); + fabGallery.startAnimation(fab_close); + fabCustomGallery.startAnimation(fab_close); + fabCamera.hide(); + fabGallery.hide(); + fabCustomGallery.hide(); + } else { + fabPlus.startAnimation(rotate_forward); + fabCamera.startAnimation(fab_open); + fabGallery.startAnimation(fab_open); + fabCustomGallery.startAnimation(fab_open); + fabCamera.show(); + fabGallery.show(); + fabCustomGallery.show(); + } + this.isFabOpen = !isFabOpen; } } 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/UploadedDao.kt new file mode 100644 index 000000000..49a1f61c3 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedDao.kt @@ -0,0 +1,56 @@ +package fr.free.nrw.commons.customselector.database + +import androidx.room.* +import java.util.* + +/** + * UploadedStatusDao for Custom Selector. + */ +@Dao +abstract class UploadedStatusDao { + + /** + * Insert into uploaded status. + */ + @Insert( onConflict = OnConflictStrategy.REPLACE ) + abstract suspend fun insert(uploadedStatus: UploadedStatus) + + /** + * Update uploaded status entry. + */ + @Update + abstract suspend fun update(uploadedStatus: UploadedStatus) + + /** + * Delete uploaded status entry. + */ + @Delete + abstract suspend fun delete(uploadedStatus: UploadedStatus) + + /** + * Query uploaded status with image sha1. + */ + @Query("SELECT * FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) ") + 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? + + /** + * Asynchronous insert into uploaded status table. + */ + suspend fun insertUploaded(uploadedStatus: UploadedStatus) { + uploadedStatus.lastUpdated = Calendar.getInstance().time + insert(uploadedStatus) + } + + /** + * Asynchronous image sha1 query. + */ + suspend fun getUploadedFromImageSHA1(imageSHA1: String):UploadedStatus? { + return getFromImageSHA1(imageSHA1) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..93e4a8243 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatus.kt @@ -0,0 +1,39 @@ +package fr.free.nrw.commons.customselector.database + +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey +import java.util.* + +/** + * 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, + + /** + * Modified image sha1 (after exif changes). + */ + val modifiedImageSHA1 : String, + + /** + * imageSHA1 query result from API. + */ + var imageResult : Boolean, + + /** + * modifiedImageSHA1 query result from API. + */ + var modifiedImageResult : Boolean, + + /** + * lastUpdated for data validation. + */ + var lastUpdated : Date? = null +) 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 new file mode 100644 index 000000000..06ec4c36c --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/helper/ImageHelper.kt @@ -0,0 +1,79 @@ +package fr.free.nrw.commons.customselector.helper + +import fr.free.nrw.commons.customselector.model.Folder +import fr.free.nrw.commons.customselector.model.Image + +/** + * Image Helper object, includes all the static functions required by custom selector. + */ +object ImageHelper { + + /** + * Returns the list of folders from given image list. + */ + fun folderListFromImages(images: List): ArrayList { + val folderMap: MutableMap = LinkedHashMap() + for (image in images) { + val bucketId = image.bucketId + val bucketName = image.bucketName + var folder = folderMap[bucketId] + if (folder == null) { + folder = Folder(bucketId, bucketName) + folderMap[bucketId] = folder + } + folder.images.add(image) + } + return ArrayList(folderMap.values) + } + + /** + * Filters the images based on the given bucketId (folder) + */ + fun filterImages(images: ArrayList, bukketId: Long?): ArrayList { + if (bukketId == null) return images + + val filteredImages = arrayListOf() + for (image in images) { + if (image.bucketId == bukketId) { + filteredImages.add(image) + } + } + return filteredImages + } + + /** + * getIndex: Returns the index of image in given list. + */ + fun getIndex(list: ArrayList, image: Image): Int { + return 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) + return list.indexOf(i) + } + 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. + + val indexes = arrayListOf() + for(image in list) { + val index = getIndex(masterList, image) + if (index == -1) { + continue + } + indexes.add(index) + } + return indexes + } +} \ 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 new file mode 100644 index 000000000..bc3bd518d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/FolderClickListener.kt @@ -0,0 +1,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 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 new file mode 100644 index 000000000..5ba43082d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageLoaderListener.kt @@ -0,0 +1,22 @@ +package fr.free.nrw.commons.customselector.listeners + +import fr.free.nrw.commons.customselector.model.Image + +/** + * Custom Selector Image Loader Listener + * responds to the device image query. + */ +interface ImageLoaderListener { + + /** + * On image loaded + * @param images : queried device images. + */ + fun onImageLoaded(images: ArrayList) + + /** + * On failed + * @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 new file mode 100644 index 000000000..1d7310b1d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/listeners/ImageSelectListener.kt @@ -0,0 +1,22 @@ +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. + */ + fun onSelectedImagesChanged(selectedImages: ArrayList) + + /** + * onLongPress + * @param imageUri : uri of image + */ + fun onLongPress(imageUri: Uri) +} \ 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 new file mode 100644 index 000000000..5cdcfb9bf --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/model/CallbackStatus.kt @@ -0,0 +1,22 @@ +package fr.free.nrw.commons.customselector.model + +/** + * sealed class Callback Status. + * Current status of the device image query. + */ +sealed class CallbackStatus { + /** + IDLE : The callback is idle , doing nothing. + */ + object IDLE : CallbackStatus() + + /** + FETCHING : Fetching images. + */ + object FETCHING : CallbackStatus() + + /** + 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 new file mode 100644 index 000000000..6857589bd --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/model/Folder.kt @@ -0,0 +1,47 @@ +package fr.free.nrw.commons.customselector.model + +/** + * Custom selector data class Folder. + */ +data class Folder( + /** + bucketId : Unique directory id, eg 540528482 + */ + var bucketId: Long, + + /** + name : bucket/folder name, eg Camera + */ + var name: String, + + /** + images : folder images, list of all images under this folder. + */ + 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 + } + + other as Folder + + if (bucketId != other.bucketId) { + return false + } + if (name != other.name) { + return false + } + if (images != other.images) { + return false + } + + 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 new file mode 100644 index 000000000..12e75580d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/model/Image.kt @@ -0,0 +1,128 @@ +package fr.free.nrw.commons.customselector.model + +import android.net.Uri +import android.os.Parcel +import android.os.Parcelable + +/** + * Custom selector data class Image. + */ +data class Image( + /** + 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 + */ + var name: String, + + /** + 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 + */ + var path: String, + + /** + bucketId : bucketId of folder, eg 540528482 + */ + var bucketId: Long = 0, + + /** + bucketName : name of folder, eg Camera + */ + var bucketName: String = "", + + /** + sha1 : sha1 of original image. + */ + var sha1: String = "" +) : Parcelable { + + /** + 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()!! + ) + + /** + Write to parcel method. + */ + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeLong(id) + parcel.writeString(name) + parcel.writeParcelable(uri, flags) + parcel.writeString(path) + parcel.writeLong(bucketId) + parcel.writeString(bucketName) + parcel.writeString(sha1) + } + + /** + * Describe the kinds of special objects contained in this Parcelable + */ + override fun describeContents(): Int { + return 0 + } + + /** + * Indicates whether some other object is "equal to" this one. + */ + override fun equals(other: Any?): Boolean { + + if(javaClass != other?.javaClass) { + return false + } + + other as Image + + if(id != other.id) { + return false; + } + if(name != other.name) { + return false; + } + if(uri != other.uri) { + return false; + } + if(path != other.path) { + return false; + } + if(bucketId != other.bucketId) { + return false; + } + if(bucketName != other.bucketName) { + return false; + } + if(sha1 != other.sha1) { + return false; + } + + return true + } + + /** + * Parcelable companion object + */ + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Image { + return Image(parcel) + } + + override fun newArray(size: Int): Array { + return 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 new file mode 100644 index 000000000..11ed8ef00 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/model/Result.kt @@ -0,0 +1,16 @@ +package fr.free.nrw.commons.customselector.model + +/** + * Custom selector data class Result. + */ +data class Result( + /** + * CallbackStatus : stores the result status + */ + val status:CallbackStatus, + + /** + * Images : images retrieved + */ + val images: ArrayList) { +} \ No newline at end of file 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 new file mode 100644 index 000000000..60d299491 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapter.kt @@ -0,0 +1,163 @@ +package fr.free.nrw.commons.customselector.ui.adapter + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import fr.free.nrw.commons.R +import fr.free.nrw.commons.customselector.listeners.FolderClickListener +import fr.free.nrw.commons.customselector.model.Folder +import fr.free.nrw.commons.customselector.model.Image + +/** + * Custom selector FolderAdapter. + */ +class FolderAdapter( + /** + * Application context. + */ + context: Context, + + /** + * Folder Click listener for click events. + */ + private val itemClickListener: FolderClickListener + +) : RecyclerViewAdapter(context) { + + /** + * List of folders. + */ + private var folders: MutableList = mutableListOf() + + /** + * Create view holder, returns View holder item. + */ + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder { + val itemView = inflater.inflate(R.layout.item_custom_selector_folder, parent, false) + return FolderViewHolder(itemView) + } + + /** + * Bind view holder, setup the item view, title, count and click listener + */ + override fun onBindViewHolder(holder: FolderViewHolder, position: Int) { + val folder = folders[position] + val toBeRemoved = ArrayList() + + for(image in folder.images) { + // Remove all the top images that do not exist anymore + if(context.contentResolver.getType(image.uri) == null){ + // File not found + toBeRemoved.add(image) + } else { + break + } + } + holder.image.setImageDrawable (null) + folder.images.removeAll(toBeRemoved) + val count = folder.images.size + + if(count == 0) { + // Folder is empty, remove folder from the adapter. + holder.itemView.post{ + val updatePosition = folders.indexOf(folder) + folders.removeAt(updatePosition) + notifyItemRemoved(updatePosition) + notifyItemRangeChanged(updatePosition, folders.size) + } + } else { + val previewImage = folder.images[0] + Glide.with(holder.image).load(previewImage.uri).into(holder.image) + holder.name.text = folder.name + holder.count.text = count.toString() + holder.itemView.setOnClickListener { + itemClickListener.onFolderClick(folder.bucketId, folder.name, 0) + } + } + } + + /** + * Initialise the data set. + */ + fun init(newFolders: List) { + val oldFolderList: MutableList = folders + val newFolderList = newFolders.toMutableList() + val diffResult = DiffUtil.calculateDiff( + FoldersDiffCallback(oldFolderList, newFolderList) + ) + folders = newFolderList + diffResult.dispatchUpdatesTo(this) + } + + /** + * returns item count. + */ + override fun getItemCount(): Int { + return folders.size + } + + /** + * Folder view holder. + */ + class FolderViewHolder(itemView:View) : RecyclerView.ViewHolder(itemView) { + + /** + * Folder thumbnail image view. + */ + val image: ImageView = itemView.findViewById(R.id.folder_thumbnail) + + /** + * Folder/album name + */ + val name: TextView = itemView.findViewById(R.id.folder_name) + + /** + * Item count in Folder/Item + */ + val count: TextView = itemView.findViewById(R.id.folder_count) + } + + /** + * DiffUtilCallback. + */ + class FoldersDiffCallback( + var oldFolders: MutableList, + var newFolders: MutableList + ) : DiffUtil.Callback() { + /** + * Returns the size of the old list. + */ + override fun getOldListSize(): Int { + return oldFolders.size + } + + /** + * Returns the size of the new list. + */ + override fun getNewListSize(): Int { + return 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 + } + + /** + * 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)) + } + + } + +} \ 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 new file mode 100644 index 000000000..f3fce1cc0 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt @@ -0,0 +1,236 @@ +package fr.free.nrw.commons.customselector.ui.adapter + +import android.content.Context +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 +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import fr.free.nrw.commons.R +import fr.free.nrw.commons.customselector.helper.ImageHelper +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 + +/** + * Custom selector ImageAdapter. + */ +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) { + + /** + * ImageSelectedOrUpdated payload class. + */ + class ImageSelectedOrUpdated + + /** + * ImageUnselected payload class. + */ + class ImageUnselected + + /** + * Currently selected images. + */ + private var selectedImages = arrayListOf() + + /** + * List of all images in adapter. + */ + private var images: ArrayList = ArrayList() + + /** + * Create View holder. + */ + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder { + val itemView = inflater.inflate(R.layout.item_custom_selector_image, parent, false) + return ImageViewHolder(itemView) + } + + /** + * Bind View holder, load image, selected view, click listeners. + */ + override fun onBindViewHolder(holder: ImageViewHolder, position: Int) { + val image=images[position] + holder.image.setImageDrawable (null) + if (context.contentResolver.getType(image.uri) == null) { + // Image does not exist anymore, update adapter. + holder.itemView.post { + val updatedPosition = images.indexOf(image) + images.remove(image) + notifyItemRemoved(updatedPosition) + notifyItemRangeChanged(updatedPosition, images.size) + } + } else { + val selectedIndex = ImageHelper.getIndex(selectedImages, image) + val isSelected = selectedIndex != -1 + if (isSelected) { + holder.itemSelected(selectedIndex + 1) + } else { + holder.itemUnselected(); + } + Glide.with(holder.image).load(image.uri).thumbnail(0.3f).into(holder.image) + imageLoader.queryAndSetView(holder, image) + holder.itemView.setOnClickListener { + selectOrRemoveImage(holder, position) + } + + // launch media preview on long click. + holder.itemView.setOnLongClickListener { + imageSelectListener.onLongPress(image.uri) + true + } + } + } + + /** + * Handle click event on an image, update counter on images. + */ + private fun selectOrRemoveImage(holder: ImageViewHolder, position: Int){ + val clickedIndex = ImageHelper.getIndex(selectedImages, images[position]) + if (clickedIndex != -1) { + selectedImages.removeAt(clickedIndex) + notifyItemChanged(position, ImageUnselected()) + val indexes = ImageHelper.getIndexList(selectedImages, images) + for (index in indexes) { + notifyItemChanged(index, ImageSelectedOrUpdated()) + } + } else { + if(holder.isItemUploaded()){ + Toast.makeText(context, R.string.custom_selector_already_uploaded_image_text, Toast.LENGTH_SHORT).show() + } else { + selectedImages.add(images[position]) + notifyItemChanged(position, ImageSelectedOrUpdated()) + } + } + imageSelectListener.onSelectedImagesChanged(selectedImages) + } + + /** + * Initialize the data set. + */ + fun init(newImages: List) { + val oldImageList:ArrayList = images + val newImageList:ArrayList = ArrayList(newImages) + val diffResult = DiffUtil.calculateDiff( + ImagesDiffCallback(oldImageList, newImageList) + ) + images = newImageList + diffResult.dispatchUpdatesTo(this) + } + + /** + * Returns the total number of items in the data set held by the adapter. + * + * @return The total number of items in this adapter. + */ + override fun getItemCount(): Int { + return images.size + } + + fun getImageIdAt(position: Int): Long { + return images.get(position).id + } + + /** + * Image view holder. + */ + class ImageViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { + val image: ImageView = itemView.findViewById(R.id.image_thumbnail) + private val selectedNumber: TextView = itemView.findViewById(R.id.selected_count) + private val uploadedGroup: Group = itemView.findViewById(R.id.uploaded_group) + private val selectedGroup: Group = itemView.findViewById(R.id.selected_group) + + /** + * Item selected view. + */ + fun itemSelected(index: Int) { + selectedGroup.visibility = View.VISIBLE + selectedNumber.text = index.toString() + } + + /** + * Item Unselected view. + */ + fun itemUnselected() { + selectedGroup.visibility = View.GONE + } + + /** + * Item Uploaded view. + */ + fun itemUploaded() { + uploadedGroup.visibility = View.VISIBLE + } + + fun isItemUploaded():Boolean { + return uploadedGroup.visibility == View.VISIBLE + } + /** + * Item Not Uploaded view. + */ + fun itemNotUploaded() { + uploadedGroup.visibility = View.GONE + } + + } + + /** + * DiffUtilCallback. + */ + class ImagesDiffCallback( + var oldImageList: ArrayList, + var newImageList: ArrayList + ) : DiffUtil.Callback(){ + + /** + * Returns the size of the old list. + */ + override fun getOldListSize(): Int { + return oldImageList.size + } + + /** + * Returns the size of the new list. + */ + override fun getNewListSize(): Int { + return 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 + } + + /** + * 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]) + } + + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..75f935302 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/RecyclerViewAdapter.kt @@ -0,0 +1,12 @@ +package fr.free.nrw.commons.customselector.ui.adapter + +import android.content.Context +import android.view.LayoutInflater +import androidx.recyclerview.widget.RecyclerView + +/** + * Generic Recycler view 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 new file mode 100644 index 000000000..8dffe7306 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt @@ -0,0 +1,235 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.app.Activity +import android.app.Dialog +import android.content.Intent +import android.content.SharedPreferences +import android.net.Uri +import android.os.Bundle +import android.view.View +import android.view.Window +import android.widget.Button +import android.widget.ImageButton +import android.widget.TextView +import androidx.lifecycle.ViewModelProvider +import fr.free.nrw.commons.R +import fr.free.nrw.commons.customselector.listeners.FolderClickListener +import fr.free.nrw.commons.customselector.listeners.ImageSelectListener +import fr.free.nrw.commons.customselector.model.Image +import fr.free.nrw.commons.media.ZoomableActivity +import fr.free.nrw.commons.theme.BaseActivity +import java.io.File +import javax.inject.Inject + + +/** + * Custom Selector Activity. + */ +class CustomSelectorActivity: BaseActivity(), FolderClickListener, ImageSelectListener { + + /** + * View model. + */ + private lateinit var viewModel: CustomSelectorViewModel + + /** + * isImageFragmentOpen is true when the image fragment is in view. + */ + private var isImageFragmentOpen = false + + /** + * Current ImageFragment attributes. + */ + private var bucketId: Long = 0L + private lateinit var bucketName: String + + /** + * Pref for saving selector state. + */ + private lateinit var prefs: SharedPreferences + + /** + * View Model Factory. + */ + @Inject lateinit var customSelectorViewModelFactory: CustomSelectorViewModelFactory + + /** + * onCreate Activity, sets theme, initialises the view model, setup view. + */ + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_custom_selector) + + prefs = applicationContext.getSharedPreferences("CustomSelector", MODE_PRIVATE) + viewModel = ViewModelProvider(this, customSelectorViewModelFactory).get( + CustomSelectorViewModel::class.java + ) + + setupViews() + + if(prefs.getBoolean("customSelectorFirstLaunch", true)) { + // show welcome dialog on first launch + showWelcomeDialog() + prefs.edit().putBoolean("customSelectorFirstLaunch", false).apply() + } + + // Open folder if saved in prefs. + if(prefs.contains(FOLDER_ID)){ + val lastOpenFolderId: Long = prefs.getLong(FOLDER_ID, 0L) + val lastOpenFolderName: String? = prefs.getString(FOLDER_NAME, null) + val lastItemId: Long = prefs.getLong(ITEM_ID, 0) + lastOpenFolderName?.let { onFolderClick(lastOpenFolderId, it, lastItemId) } + } + } + + /** + * Show Custom Selector Welcome Dialog. + */ + private fun showWelcomeDialog() { + val dialog = Dialog(this) + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) + dialog.setContentView(R.layout.custom_selector_info_dialog) + (dialog.findViewById(R.id.btn_ok) as Button).setOnClickListener { dialog.dismiss() } + dialog.show() + } + + /** + * Set up view, default folder view. + */ + private fun setupViews() { + supportFragmentManager.beginTransaction() + .replace(R.id.fragment_container, FolderFragment.newInstance()) + .commit() + fetchData() + setUpToolbar() + } + + /** + * Start data fetch in view model. + */ + private fun fetchData() { + viewModel.fetchImages() + } + + /** + * Change the title of the toolbar. + */ + private fun changeTitle(title: String) { + val titleText = findViewById(R.id.title) + if(titleText != null) { + titleText.text = title + } + } + + /** + * Set up the toolbar, back listener, done listener. + */ + private fun setUpToolbar() { + val back : ImageButton = findViewById(R.id.back) + back.setOnClickListener { onBackPressed() } + + val done : ImageButton = findViewById(R.id.done) + done.setOnClickListener { onDone() } + } + + /** + * override on folder click, change the toolbar title on folder click. + */ + override fun onFolderClick(folderId: Long, folderName: String, lastItemId: Long) { + supportFragmentManager.beginTransaction() + .add(R.id.fragment_container, ImageFragment.newInstance(folderId, lastItemId)) + .addToBackStack(null) + .commit() + + changeTitle(folderName) + + bucketId = folderId + bucketName = folderName + isImageFragmentOpen = true + } + + /** + * override Selected Images Change, update view model selected images. + */ + override fun onSelectedImagesChanged(selectedImages: ArrayList) { + viewModel.selectedImages.value = selectedImages + + val done : ImageButton = findViewById(R.id.done) + done.visibility = if (selectedImages.isEmpty()) View.INVISIBLE else View.VISIBLE + } + + /** + * onLongPress + * @param imageUri : uri of image + */ + override fun onLongPress(imageUri: Uri) { + val intent = Intent(this, ZoomableActivity::class.java).setData(imageUri); + startActivity(intent) + } + + /** + * OnDone clicked. + * 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++ + } + finishPickImages(selectedImages) + } + + /** + * finishPickImages, Load the data to the intent and set result. + * Finish the activity. + */ + private fun finishPickImages(images: ArrayList) { + val data = Intent() + data.putParcelableArrayListExtra("Images", images) + setResult(Activity.RESULT_OK, data) + finish() + } + + /** + * Back pressed. + * Change toolbar title. + */ + override fun onBackPressed() { + super.onBackPressed() + val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container) + if(fragment != null && fragment is FolderFragment){ + isImageFragmentOpen = false + changeTitle(getString(R.string.custom_selector_title)) + } + } + + /** + * On activity destroy + * If image fragment is open, overwrite its attributes otherwise discard the values. + */ + override fun onDestroy() { + if(isImageFragmentOpen){ + prefs.edit().putLong(FOLDER_ID, bucketId).putString(FOLDER_NAME, bucketName).apply() + } else { + prefs.edit().remove(FOLDER_ID).remove(FOLDER_NAME).apply() + } + super.onDestroy() + } + + companion object { + const val FOLDER_ID : String = "FolderId" + const val FOLDER_NAME : String = "FolderName" + const val ITEM_ID : String = "ItemId" + } +} \ 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 new file mode 100644 index 000000000..cd7858d1b --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModel.kt @@ -0,0 +1,58 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.content.Context +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import fr.free.nrw.commons.customselector.listeners.ImageLoaderListener +import fr.free.nrw.commons.customselector.model.CallbackStatus +import fr.free.nrw.commons.customselector.model.Image +import fr.free.nrw.commons.customselector.model.Result +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel + +/** + * Custom Selector view model. + */ +class CustomSelectorViewModel(var context: Context,var imageFileLoader: ImageFileLoader) : ViewModel() { + + /** + * Scope for coroutine task (image fetch). + */ + private val scope = CoroutineScope(Dispatchers.Main) + + /** + * Stores selected images. + */ + var selectedImages: MutableLiveData> = MutableLiveData() + + /** + * Result Live Data. + */ + val result = MutableLiveData(Result(CallbackStatus.IDLE, arrayListOf())) + + /** + * Fetch Images and supply to result. + */ + 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())) + } + },scope) + } + + /** + * Clear the coroutine task linked with context. + */ + override fun onCleared() { + 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 new file mode 100644 index 000000000..d7a7d42f4 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelFactory.kt @@ -0,0 +1,17 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +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 + } + +} \ 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 new file mode 100644 index 000000000..456c14831 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/FolderFragment.kt @@ -0,0 +1,142 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ProgressBar +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import fr.free.nrw.commons.R +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.ui.adapter.FolderAdapter +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment +import fr.free.nrw.commons.media.MediaClient +import fr.free.nrw.commons.upload.FileProcessor +import kotlinx.android.synthetic.main.fragment_custom_selector.view.* +import javax.inject.Inject + +/** + * Custom selector folder fragment. + */ +class FolderFragment : CommonsDaggerSupportFragment() { + + /** + * View Model for images. + */ + private var viewModel: CustomSelectorViewModel? = null + + /** + * View Elements + */ + private var selectorRV: RecyclerView? = null + private var loader: ProgressBar? = null + + /** + * View Model Factory. + */ + var customSelectorViewModelFactory: CustomSelectorViewModelFactory? = null + @Inject set + + var fileProcessor: FileProcessor? = null + @Inject set + + var mediaClient: MediaClient? = null + @Inject set + /** + * Folder Adapter. + */ + private lateinit var folderAdapter: FolderAdapter + + /** + * Grid Layout Manager for recycler view. + */ + private lateinit var gridLayoutManager: GridLayoutManager + + /** + * Folder List. + */ + private lateinit var folders : ArrayList + + /** + * Companion newInstance. + */ + companion object{ + fun newInstance(): FolderFragment { + return FolderFragment() + } + } + + /** + * OnCreate Fragment, get the view model. + */ + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + 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? { + val root = inflater.inflate(R.layout.fragment_custom_selector, container, false) + folderAdapter = FolderAdapter(activity!!, activity as FolderClickListener) + gridLayoutManager = GridLayoutManager(context, columnCount()) + selectorRV = root.selector_rv + loader = root.loader + with(root.selector_rv){ + this.layoutManager = gridLayoutManager + setHasFixedSize(true) + this.adapter = folderAdapter + } + viewModel?.result?.observe(viewLifecycleOwner, Observer { + handleResult(it) + }) + return root + } + + /** + * Handle view model result. + * Get folders from images. + * Load adapter. + */ + private fun handleResult(result: Result) { + if(result.status is CallbackStatus.SUCCESS){ + folders = ImageHelper.folderListFromImages(result.images) + folderAdapter.init(folders) + folderAdapter.notifyDataSetChanged() + selectorRV?.let { + it.visibility = View.VISIBLE + } + } + loader?.let { + it.visibility = if (result.status is CallbackStatus.FETCHING) View.VISIBLE else View.GONE + } + } + + /** + * onResume + * notifyDataSetChanged, rebuild the holder views to account for deleted images, folders. + */ + override fun onResume() { + folderAdapter.notifyDataSetChanged() + super.onResume() + } + + /** + * Return Column count ie span count for grid view adapter. + */ + private fun columnCount(): Int { + return 2 + // todo change column count depending on the orientation of the device. + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..12e883a14 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoader.kt @@ -0,0 +1,112 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.content.ContentUris +import android.content.Context +import android.provider.MediaStore +import fr.free.nrw.commons.customselector.listeners.ImageLoaderListener +import fr.free.nrw.commons.customselector.model.Image +import kotlinx.coroutines.* +import java.io.File +import kotlin.coroutines.CoroutineContext + +/** + * Custom Selector Image File Loader. + * Loads device images. + */ +class ImageFileLoader(val context: Context) : CoroutineScope{ + + /** + * Coroutine context for fetching images. + */ + override val coroutineContext: CoroutineContext = Dispatchers.Main + + /** + * 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) + + /** + * Load Device Images under coroutine. + */ + fun loadDeviceImages(listener: ImageLoaderListener, scope: CoroutineScope) { + launch(Dispatchers.Main) { + withContext(Dispatchers.IO) { + getImages(listener) + } + } + } + + + /** + * 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") + if (cursor == null) { + listener.onFailed(NullPointerException()) + return + } + + val idColumn = cursor.getColumnIndex(MediaStore.Images.Media._ID) + val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME) + val dataColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATA) + val bucketIdColumn = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID) + val bucketNameColumn = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME) + + val images = arrayListOf() + if (cursor.moveToFirst()) { + do { + if (Thread.interrupted()) { + listener.onFailed(NullPointerException()) + return + } + val id = cursor.getLong(idColumn) + val name = cursor.getString(nameColumn) + val path = cursor.getString(dataColumn) + val bucketId = cursor.getLong(bucketIdColumn) + val bucketName = cursor.getString(bucketNameColumn) + + val file = + if (path == null || path.isEmpty()) { + null + } else try { + File(path) + } catch (ignored: Exception) { + null + } + + + if (file != null && file.exists()) { + if (id != null && name != null && path != null && bucketId != null && bucketName != null) { + val uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id) + val image = Image(id, name, uri, path, bucketId, bucketName) + images.add(image) + } + } + + } while (cursor.moveToNext()) + } + cursor.close() + listener.onImageLoaded(images) + } + + + /** + * Abort loading images. + */ + fun abortLoadImage(){ + //todo Abort loading images. + } + + /* + * + * TODO + * Sha1 for image (original image). + * + */ +} \ No newline at end of file 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 new file mode 100644 index 000000000..ef9ae1fce --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt @@ -0,0 +1,210 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.net.Uri +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ProgressBar +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import fr.free.nrw.commons.R +import fr.free.nrw.commons.customselector.helper.ImageHelper +import fr.free.nrw.commons.customselector.listeners.ImageSelectListener +import fr.free.nrw.commons.customselector.model.CallbackStatus +import fr.free.nrw.commons.customselector.model.Image +import fr.free.nrw.commons.customselector.model.Result +import fr.free.nrw.commons.customselector.ui.adapter.ImageAdapter +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment +import fr.free.nrw.commons.theme.BaseActivity +import kotlinx.android.synthetic.main.fragment_custom_selector.view.* +import java.io.File +import java.io.FileInputStream +import java.net.URI +import javax.inject.Inject + +/** + * Custom Selector Image Fragment. + */ +class ImageFragment: CommonsDaggerSupportFragment() { + + /** + * Current bucketId. + */ + private var bucketId: Long? = null + + /** + * Last ImageItem Id. + */ + private var lastItemId: Long? = null + + /** + * View model for images. + */ + private var viewModel: CustomSelectorViewModel? = null + + /** + * View Elements. + */ + private var selectorRV: RecyclerView? = null + private var loader: ProgressBar? = null + lateinit var filteredImages: ArrayList; + + /** + * View model Factory. + */ + lateinit var customSelectorViewModelFactory: CustomSelectorViewModelFactory + @Inject set + + /** + * Image loader for adapter. + */ + var imageLoader: ImageLoader? = null + @Inject set + + /** + * Image Adapter for recycle view. + */ + private lateinit var imageAdapter: ImageAdapter + + /** + * GridLayoutManager for recycler view. + */ + private lateinit var gridLayoutManager: GridLayoutManager + + + companion object { + + /** + * BucketId args name + */ + const val BUCKET_ID = "BucketId" + const val LAST_ITEM_ID = "LastItemId" + + /** + * newInstance from bucketId. + */ + fun newInstance(bucketId: Long, lastItemId: Long): ImageFragment { + val fragment = ImageFragment() + val args = Bundle() + args.putLong(BUCKET_ID, bucketId) + args.putLong(LAST_ITEM_ID, lastItemId) + fragment.arguments = args + return fragment + } + } + + /** + * OnCreate + * Get BucketId, view Model. + */ + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + bucketId = arguments?.getLong(BUCKET_ID) + lastItemId = arguments?.getLong(LAST_ITEM_ID, 0) + viewModel = ViewModelProvider(requireActivity(),customSelectorViewModelFactory).get(CustomSelectorViewModel::class.java) + } + + /** + * OnCreateView + * Init imageAdapter, gridLayoutManger. + * SetUp recycler view. + */ + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + + val root = inflater.inflate(R.layout.fragment_custom_selector, container, false) + imageAdapter = ImageAdapter(requireActivity(), activity as ImageSelectListener, imageLoader!!) + gridLayoutManager = GridLayoutManager(context,getSpanCount()) + with(root.selector_rv){ + this.layoutManager = gridLayoutManager + setHasFixedSize(true) + this.adapter = imageAdapter + } + + viewModel?.result?.observe(viewLifecycleOwner, Observer{ + handleResult(it) + }) + + selectorRV = root.selector_rv + loader = root.loader + + return root + } + + /** + * Handle view model result. + */ + private fun handleResult(result:Result){ + if(result.status is CallbackStatus.SUCCESS){ + val images = result.images + if(images.isNotEmpty()) { + filteredImages = ImageHelper.filterImages(images, bucketId) + imageAdapter.init(filteredImages) + selectorRV?.let { + it.visibility = View.VISIBLE + lastItemId?.let { pos -> + (it.layoutManager as GridLayoutManager) + .scrollToPosition(ImageHelper.getIndexFromId(filteredImages, pos)) + } + } + } + else{ + selectorRV?.let{ + it.visibility = View.GONE + } + } + } + loader?.let { + it.visibility = if (result.status is CallbackStatus.FETCHING) View.VISIBLE else View.GONE + } + } + + /** + * getSpanCount for GridViewManager. + * + * @return spanCount. + */ + private fun getSpanCount(): Int { + return 3 + // todo change span count depending on the device orientation and other factos. + } + + /** + * onResume + * notifyDataSetChanged, rebuild the holder views to account for deleted images. + */ + override fun onResume() { + imageAdapter.notifyDataSetChanged() + super.onResume() + } + + /** + * OnDestroy + * Cleanup the imageLoader coroutine. + * Save the Image Fragment state. + */ + override fun onDestroy() { + imageLoader?.cleanUP() + + val position = (selectorRV?.layoutManager as GridLayoutManager) + .findFirstVisibleItemPosition() + + // Check for empty RecyclerView. + if (position != -1) { + context?.let { context -> + context.getSharedPreferences( + "CustomSelector", + BaseActivity.MODE_PRIVATE + )?.let { prefs -> + prefs.edit()?.let { editor -> + editor.putLong("ItemId", imageAdapter.getImageIdAt(position))?.apply() + } + } + } + } + super.onDestroy() + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..cf45a87c1 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageLoader.kt @@ -0,0 +1,280 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.content.Context +import android.net.Uri +import androidx.exifinterface.media.ExifInterface +import fr.free.nrw.commons.customselector.database.UploadedStatus +import fr.free.nrw.commons.customselector.database.UploadedStatusDao +import fr.free.nrw.commons.customselector.model.Image +import fr.free.nrw.commons.customselector.ui.adapter.ImageAdapter.ImageViewHolder +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 +import kotlinx.coroutines.* +import timber.log.Timber +import java.io.FileNotFoundException +import java.io.IOException +import java.net.UnknownHostException +import java.util.* +import java.util.concurrent.TimeUnit +import javax.inject.Inject +import kotlin.collections.HashMap + +/** + * 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, + + /** + * 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 Dispatchers and Scope. + */ + private var defaultDispatcher : CoroutineDispatcher = Dispatchers.Default + private var ioDispatcher : CoroutineDispatcher = Dispatchers.IO + private val scope : CoroutineScope = MainScope() + + /** + * Query image and setUp the view. + */ + fun queryAndSetView(holder: ImageViewHolder, image: Image) { + + /** + * Recycler view uses same view holder, so we can identify the latest query image from holder. + */ + mapHolderImage[holder] = image + holder.itemNotUploaded() + + scope.launch { + + var result: Result = Result.NOTFOUND + + if (mapHolderImage[holder] != image) { + return@launch + } + + val imageSHA1 = getImageSHA1(image.uri) + 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) + } else { + "" + } + } + + if (mapHolderImage[holder] != image) { + return@launch + } + + if (result in arrayOf(Result.NOTFOUND, Result.INVALID) && sha1.isNotEmpty()) { + // Query original image. + result = querySHA1(imageSHA1) + if (result is Result.TRUE) { + // Original image found. + insertIntoUploaded(imageSHA1, sha1, result is Result.TRUE, false) + } else { + // Original image not found, query modified image. + result = querySHA1(sha1) + if (result != Result.ERROR) { + insertIntoUploaded(imageSHA1, sha1, false, result is Result.TRUE) + } + } + } + if(mapHolderImage[holder] == image) { + if (result is Result.TRUE) holder.itemUploaded() else holder.itemNotUploaded() + } + } + } + + /** + * Query SHA1, return result if previously queried, otherwise start a new query. + * + * @return Query result. + */ + + suspend fun querySHA1(SHA1: String): Result { + return withContext(ioDispatcher) { + mapResult[SHA1]?.let { + return@withContext it + } + var result: Result = Result.FALSE + try { + if (mediaClient.checkFileExistsUsingSha(SHA1).blockingGet()) { + mapResult[SHA1] = Result.TRUE + result = Result.TRUE + } + } catch (e: Exception) { + if (e is UnknownHostException) { + // Handle no network connection. + Timber.e(e, "Network Connection Error") + } + result = Result.ERROR + e.printStackTrace() + } + result + } + } + + /** + * Get SHA1, return SHA1 if available, otherwise generate and store the SHA1. + * + * @return sha1 of the image + */ + suspend fun getSHA1(image: Image): String { + mapModifiedImageSHA1[image]?.let{ + return it + } + val sha1 = generateModifiedSHA1(image); + mapModifiedImageSHA1[image] = sha1; + return sha1; + } + + /** + * Get the uploaded status entry from the database. + */ + suspend fun getFromUploaded(imageSha1:String): UploadedStatus? { + return 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 + ) + ) + } + + /** + * Get image sha1 from uri, used to retrieve the original image sha1. + */ + suspend fun getImageSHA1(uri: Uri): String { + return withContext(ioDispatcher) { + mapImageSHA1[uri]?.let{ + return@withContext it + } + try { + val result = fileUtilsWrapper.getSHA1(context.contentResolver.openInputStream(uri)) + mapImageSHA1[uri] = result + result + } catch (e: FileNotFoundException){ + e.printStackTrace() + "" + } + } + } + + /** + * 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 + } + + /** + * Generate Modified SHA1 using present Exif settings. + * + * @return modified sha1 + */ + private suspend fun generateModifiedSHA1(image: Image) : String { + return withContext(defaultDispatcher) { + val uploadableFile = PickedFiles.pickedExistingPicture(context, image.uri) + 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)) + uploadableFile.file.delete() + sha1 + } + } + + /** + * CleanUp function. + */ + fun cleanUP() { + scope.cancel() + } + + /** + * 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. + */ + const val INVALIDATE_DAY_COUNT: Long = 7 + } + +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java index 315bafe41..f9ba4a5ae 100644 --- a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java +++ b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java @@ -13,7 +13,7 @@ import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao; public class DBOpenHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = "commons.db"; - private static final int DATABASE_VERSION = 17; + private static final int DATABASE_VERSION = 18; public static final String CONTRIBUTIONS_TABLE = "contributions"; private final String DROP_TABLE_STATEMENT="DROP TABLE IF EXISTS %s"; 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 c408ea5f7..7f6ea7027 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,6 +5,8 @@ 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.UploadedStatus +import fr.free.nrw.commons.customselector.database.UploadedStatusDao import fr.free.nrw.commons.upload.depicts.Depicts import fr.free.nrw.commons.upload.depicts.DepictsDao @@ -12,9 +14,10 @@ import fr.free.nrw.commons.upload.depicts.DepictsDao * The database for accessing the respective DAOs * */ -@Database(entities = [Contribution::class, Depicts::class], version = 8, exportSchema = false) +@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class], version = 8, exportSchema = false) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun contributionDao(): ContributionDao abstract fun DepictsDao(): DepictsDao; + abstract fun UploadedStatusDao(): UploadedStatusDao; } diff --git a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java index 28c79c612..6381bdc8e 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java @@ -8,6 +8,7 @@ import fr.free.nrw.commons.auth.LoginActivity; import fr.free.nrw.commons.auth.SignupActivity; import fr.free.nrw.commons.category.CategoryDetailsActivity; import fr.free.nrw.commons.contributions.MainActivity; +import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity; import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity; import fr.free.nrw.commons.explore.SearchActivity; import fr.free.nrw.commons.notification.NotificationActivity; @@ -34,6 +35,9 @@ public abstract class ActivityBuilderModule { @ContributesAndroidInjector abstract MainActivity bindContributionsActivity(); + @ContributesAndroidInjector + abstract CustomSelectorActivity bindCustomSelectorActivity(); + @ContributesAndroidInjector abstract SettingsActivity bindSettingsActivity(); diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index 1e19de5f4..bffda8332 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -17,6 +17,8 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.contributions.ContributionDao; +import fr.free.nrw.commons.customselector.database.UploadedStatusDao; +import fr.free.nrw.commons.customselector.ui.selector.ImageFileLoader; import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.db.AppDatabase; import fr.free.nrw.commons.kvstore.JsonKvStore; @@ -66,6 +68,16 @@ public class CommonsApplicationModule { this.applicationContext = applicationContext; } + /** + * Provides ImageFileLoader used to fetch device images. + * @param context + * @return + */ + @Provides + public ImageFileLoader providesImageFileLoader(Context context) { + return new ImageFileLoader(context); + } + @Provides public Context providesApplicationContext() { return this.applicationContext; @@ -244,13 +256,21 @@ public class CommonsApplicationModule { } /** - * Get the reference of DepictsDao class + * Get the reference of DepictsDao class. */ @Provides public DepictsDao providesDepictDao(AppDatabase appDatabase) { return appDatabase.DepictsDao(); } + /** + * Get the reference of UploadedStatus class. + */ + @Provides + public UploadedStatusDao providesUploadedStatusDao(AppDatabase appDatabase) { + return appDatabase.UploadedStatusDao(); + } + @Provides public ContentResolver providesContentResolver(Context context){ return context.getContentResolver(); diff --git a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java index 3757a2147..f255134ea 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java @@ -8,6 +8,8 @@ import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment; import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment; import fr.free.nrw.commons.contributions.ContributionsFragment; import fr.free.nrw.commons.contributions.ContributionsListFragment; +import fr.free.nrw.commons.customselector.ui.selector.FolderFragment; +import fr.free.nrw.commons.customselector.ui.selector.ImageFragment; import fr.free.nrw.commons.explore.ExploreFragment; import fr.free.nrw.commons.explore.ExploreListRootFragment; import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment; @@ -49,6 +51,12 @@ public abstract class FragmentBuilderModule { @ContributesAndroidInjector abstract MediaDetailFragment bindMediaDetailFragment(); + @ContributesAndroidInjector + abstract FolderFragment bindFolderFragment(); + + @ContributesAndroidInjector + abstract ImageFragment bindImageFragment(); + @ContributesAndroidInjector abstract MediaDetailPagerFragment bindMediaDetailPagerFragment(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ExploreListRootFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/ExploreListRootFragment.java index 32b38fea7..e88f14b55 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/ExploreListRootFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/ExploreListRootFragment.java @@ -40,7 +40,7 @@ public class ExploreListRootFragment extends CommonsDaggerSupportFragment implem featuredArguments.putString("categoryName", title); listFragment.setArguments(featuredArguments); } - + @Nullable @Override public View onCreateView(@NonNull final LayoutInflater inflater, diff --git a/app/src/main/java/fr/free/nrw/commons/filepicker/Constants.java b/app/src/main/java/fr/free/nrw/commons/filepicker/Constants.java index 83d838bc2..4b5b91e68 100644 --- a/app/src/main/java/fr/free/nrw/commons/filepicker/Constants.java +++ b/app/src/main/java/fr/free/nrw/commons/filepicker/Constants.java @@ -10,6 +10,7 @@ public interface Constants { int FILE_PICKER_IMAGE_IDENTIFICATOR = 0b1101101100; //876 int SOURCE_CHOOSER = 1 << 15; + int PICK_PICTURE_FROM_CUSTOM_SELECTOR = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 10); int PICK_PICTURE_FROM_DOCUMENTS = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 11); int PICK_PICTURE_FROM_GALLERY = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 12); int TAKE_PICTURE = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 13); diff --git a/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.java b/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.java index 698e2d51f..bc43cb154 100644 --- a/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.java +++ b/app/src/main/java/fr/free/nrw/commons/filepicker/FilePicker.java @@ -15,6 +15,8 @@ import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import fr.free.nrw.commons.customselector.model.Image; +import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; @@ -51,6 +53,17 @@ public class FilePicker implements Constants { .putExtra(Intent.EXTRA_ALLOW_MULTIPLE, configuration(context).allowsMultiplePickingInGallery()); } + /** + * CreateCustomSectorIntent, creates intent for custom selector activity. + * @param context + * @param type + * @return Custom selector intent + */ + private static Intent createCustomSelectorIntent(@NonNull Context context, int type) { + storeType(context, type); + return new Intent(context, CustomSelectorActivity.class); + } + private static Intent createCameraForImageIntent(@NonNull Context context, int type) { storeType(context, type); @@ -97,6 +110,14 @@ public class FilePicker implements Constants { activity.startActivityForResult(intent, RequestCodes.PICK_PICTURE_FROM_GALLERY); } + /** + * Opens Custom Selector + */ + public static void openCustomSelector(Activity activity, int type) { + Intent intent = createCustomSelectorIntent(activity, type); + activity.startActivityForResult(intent, RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR); + } + /** * Opens the camera app to pick image clicked by user */ @@ -135,12 +156,15 @@ public class FilePicker implements Constants { if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY || requestCode == RequestCodes.TAKE_PICTURE || requestCode == RequestCodes.CAPTURE_VIDEO || - requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS) { + requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS || + requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) { if (resultCode == Activity.RESULT_OK) { if (requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS && !isPhoto(data)) { onPictureReturnedFromDocuments(data, activity, callbacks); } else if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY && !isPhoto(data)) { onPictureReturnedFromGallery(data, activity, callbacks); + } else if (requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) { + onPictureReturnedFromCustomSelector(data, activity, callbacks); } else if (requestCode == RequestCodes.TAKE_PICTURE) { onPictureReturnedFromCamera(activity, callbacks); } else if (requestCode == RequestCodes.CAPTURE_VIDEO) { @@ -197,6 +221,40 @@ public class FilePicker implements Constants { } } + /** + * onPictureReturnedFromCustomSelector. + * Retrieve and forward the images to upload wizard through callback. + */ + private static void onPictureReturnedFromCustomSelector(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) { + try { + List files = getFilesFromCustomSelector(data, activity); + callbacks.onImagesPicked(files, ImageSource.CUSTOM_SELECTOR, restoreType(activity)); + } catch (Exception e) { + e.printStackTrace(); + callbacks.onImagePickerError(e, ImageSource.CUSTOM_SELECTOR, restoreType(activity)); + } + } + + /** + * Get files from custom selector + * Retrieve and process the selected images from the custom selector. + */ + private static List getFilesFromCustomSelector(Intent data, Activity activity) throws IOException, SecurityException { + List files = new ArrayList<>(); + ArrayList images = data.getParcelableArrayListExtra("Images"); + for(Image image : images) { + Uri uri = image.getUri(); + UploadableFile file = PickedFiles.pickedExistingPicture(activity, uri); + files.add(file); + } + + if (configuration(activity).shouldCopyPickedImagesToPublicGalleryAppFolder()) { + PickedFiles.copyFilesInSeparateThread(activity, files); + } + + return files; + } + private static void onPictureReturnedFromGallery(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) { try { List files = getFilesFromGalleryPictures(data, activity); @@ -301,7 +359,7 @@ public class FilePicker implements Constants { public enum ImageSource { - GALLERY, DOCUMENTS, CAMERA_IMAGE, CAMERA_VIDEO + GALLERY, DOCUMENTS, CAMERA_IMAGE, CAMERA_VIDEO, CUSTOM_SELECTOR } public interface Callbacks { diff --git a/app/src/main/java/fr/free/nrw/commons/filepicker/PickedFiles.java b/app/src/main/java/fr/free/nrw/commons/filepicker/PickedFiles.java index 01e68c940..ea5983173 100644 --- a/app/src/main/java/fr/free/nrw/commons/filepicker/PickedFiles.java +++ b/app/src/main/java/fr/free/nrw/commons/filepicker/PickedFiles.java @@ -24,19 +24,38 @@ import java.util.UUID; import timber.log.Timber; +/** + * PickedFiles. + * Process the upload items. + */ +public class PickedFiles implements Constants { -class PickedFiles implements Constants { - + /** + * Get Folder Name + * @param context + * @return default application folder name. + */ private static String getFolderName(@NonNull Context context) { return FilePicker.configuration(context).getFolderName(); } + /** + * tempImageDirectory + * @param context + * @return temporary image directory to copy and perform exif changes. + */ private static File tempImageDirectory(@NonNull Context context) { File privateTempDir = new File(context.getCacheDir(), DEFAULT_FOLDER_NAME); if (!privateTempDir.exists()) privateTempDir.mkdirs(); return privateTempDir; } + /** + * writeToFile + * writes inputStream data to the destination file. + * @param in input stream of source file. + * @param file destination file + */ private static void writeToFile(InputStream in, File file) { try { OutputStream out = new FileOutputStream(file); @@ -52,11 +71,24 @@ class PickedFiles implements Constants { } } + /** + * Copy file function. + * Copies source file to destination file. + * @param src source file + * @param dst destination file + * @throws IOException (File input stream exception) + */ private static void copyFile(File src, File dst) throws IOException { InputStream in = new FileInputStream(src); writeToFile(in, dst); } + /** + * Copy files in separate thread. + * Copies all the uploadable files to the temp image folder on background thread. + * @param context + * @param filesToCopy uploadable file list to be copied. + */ static void copyFilesInSeparateThread(final Context context, final List filesToCopy) { new Thread(() -> { List copiedFiles = new ArrayList<>(); @@ -64,7 +96,9 @@ class PickedFiles implements Constants { for (UploadableFile uploadableFile : filesToCopy) { File fileToCopy = uploadableFile.getFile(); File dstDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), getFolderName(context)); - if (!dstDir.exists()) dstDir.mkdirs(); + if (!dstDir.exists()) { + dstDir.mkdirs(); + } String[] filenameSplit = fileToCopy.getName().split("\\."); String extension = "." + filenameSplit[filenameSplit.length - 1]; @@ -84,12 +118,24 @@ class PickedFiles implements Constants { }).run(); } + /** + * singleFileList. + * converts a single uploadableFile to list of uploadableFile. + * @param file uploadable file + * @return + */ static List singleFileList(UploadableFile file) { List list = new ArrayList<>(); list.add(file); return list; } + /** + * ScanCopiedImages + * Scan copied images metadata using media scanner. + * @param context + * @param copiedImages copied images list. + */ static void scanCopiedImages(Context context, List copiedImages) { String[] paths = new String[copiedImages.size()]; for (int i = 0; i < copiedImages.size(); i++) { @@ -104,7 +150,13 @@ class PickedFiles implements Constants { }); } - static UploadableFile pickedExistingPicture(@NonNull Context context, Uri photoUri) throws IOException, SecurityException {// SecurityException for those file providers who share URI but forget to grant necessary permissions + /** + * pickedExistingPicture + * convert the image into uploadable file. + * @param photoUri Uri of the image. + * @return Uploadable file ready for tag redaction. + */ + public static UploadableFile pickedExistingPicture(@NonNull Context context, Uri photoUri) throws IOException, SecurityException {// SecurityException for those file providers who share URI but forget to grant necessary permissions InputStream pictureInputStream = context.getContentResolver().openInputStream(photoUri); File directory = tempImageDirectory(context); File photoFile = new File(directory, UUID.randomUUID().toString() + "." + getMimeType(context, photoUri)); @@ -116,6 +168,9 @@ class PickedFiles implements Constants { return new UploadableFile(photoUri, photoFile); } + /** + * getCameraPictureLocation + */ static File getCameraPicturesLocation(@NonNull Context context) throws IOException { File dir = tempImageDirectory(context); return File.createTempFile(UUID.randomUUID().toString(), ".jpg", dir); @@ -142,6 +197,11 @@ class PickedFiles implements Constants { return extension; } + /** + * GetUriToFile + * @param file get uri of file + * @return uri of requested file. + */ static Uri getUriToFile(@NonNull Context context, @NonNull File file) { String packageName = context.getApplicationContext().getPackageName(); String authority = packageName + ".provider"; diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index 52f8b7385..ce6585b52 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -102,6 +102,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple pager.addOnPageChangeListener(this); adapter = new MediaDetailAdapter(getChildFragmentManager()); + ((BaseActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true); if (getActivity() != null) { final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); 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 ff3f63eb8..5ad6952ee 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 @@ -77,7 +77,7 @@ class FileProcessor @Inject constructor( * * @return tags to be redacted */ - private fun getExifTagsToRedact(): Set { + fun getExifTagsToRedact(): Set { val prefManageEXIFTags = defaultKvStore.getStringSet(Prefs.MANAGED_EXIF_TAGS) ?: emptySet() val redactTags: Set = @@ -91,7 +91,7 @@ class FileProcessor @Inject constructor( * @param exifInterface ExifInterface object * @param redactTags tags to be redacted */ - private fun redactExifTags(exifInterface: ExifInterface?, redactTags: Set) { + fun redactExifTags(exifInterface: ExifInterface?, redactTags: Set) { compositeDisposable.add( Observable.fromIterable(redactTags) .flatMap { Observable.fromArray(*FileMetadataUtils.getTagsFromPref(it)) } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.java index 0487fd87f..1b482717f 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.java @@ -24,6 +24,12 @@ public class UploadItem { private final BehaviorSubject imageQuality; private boolean hasInvalidLocation; + /** + * Uri of uploadItem + * Uri points to image location or name, eg content://media/external/images/camera/10495 (Android 10) + */ + private final Uri contentUri; + @SuppressLint("CheckResult") UploadItem(final Uri mediaUri, @@ -31,7 +37,8 @@ public class UploadItem { final ImageCoordinates gpsCoords, final Place place, final long createdTimestamp, - final String createdTimestampSource) { + final String createdTimestampSource, + final Uri contentUri) { this.createdTimestampSource = createdTimestampSource; uploadMediaDetails = new ArrayList<>(Collections.singletonList(new UploadMediaDetail())); this.place = place; @@ -39,6 +46,7 @@ public class UploadItem { this.mimeType = mimeType; this.gpsCoords = gpsCoords; this.createdTimestamp = createdTimestamp; + this.contentUri = contentUri; imageQuality = BehaviorSubject.createDefault(ImageUtils.IMAGE_WAIT); } @@ -66,8 +74,15 @@ public class UploadItem { return imageQuality.getValue(); } + /** + * getContentUri. + * @return Uri of uploadItem + * Uri points to image location or name, eg content://media/external/images/camera/10495 (Android 10) + */ + public Uri getContentUri() { return contentUri; } + public void setImageQuality(final int imageQuality) { - this.imageQuality.onNext(imageQuality); + this.imageQuality.onNext(imageQuality); } /** diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java index cf72fa5d6..1d1b7117f 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java @@ -106,7 +106,8 @@ public class UploadModel { final UploadItem uploadItem = new UploadItem( Uri.parse(uploadableFile.getFilePath()), uploadableFile.getMimeType(context), imageCoordinates, place, fileCreatedDate, - createdTimestampSource); + createdTimestampSource, + uploadableFile.getContentUri()); if (place != null) { uploadItem.getUploadMediaDetails().set(0, new UploadMediaDetail(place)); } 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 ad0c08c4c..d46c5e4ac 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 @@ -18,17 +18,22 @@ import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.contributions.ChunkInfo import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.ContributionDao +import fr.free.nrw.commons.customselector.database.UploadedStatus +import fr.free.nrw.commons.customselector.database.UploadedStatusDao import fr.free.nrw.commons.di.ApplicationlessInjection import fr.free.nrw.commons.location.LatLng import fr.free.nrw.commons.media.MediaClient +import fr.free.nrw.commons.upload.FileUtilsWrapper import fr.free.nrw.commons.upload.StashUploadState import fr.free.nrw.commons.upload.UploadClient import fr.free.nrw.commons.upload.UploadResult import fr.free.nrw.commons.wikidata.WikidataEditService import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber import java.io.IOException @@ -51,12 +56,18 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : @Inject lateinit var contributionDao: ContributionDao + @Inject + lateinit var uploadedStatusDao: UploadedStatusDao + @Inject lateinit var uploadClient: UploadClient @Inject lateinit var mediaClient: MediaClient + @Inject + lateinit var fileUtilsWrapper: FileUtilsWrapper + private val PROCESSING_UPLOADS_NOTIFICATION_TAG = BuildConfig.APPLICATION_ID + " : upload_tag" private val PROCESSING_UPLOADS_NOTIFICATION_ID = 101 @@ -417,6 +428,29 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : .blockingGet() contributionFromUpload.dateModified=Date() contributionDao.deleteAndSaveContribution(contribution, contributionFromUpload) + + // Upload success, save to uploaded status. + saveIntoUploadedStatus(contribution) + } + + /** + * Save to uploadedStatusDao. + */ + private fun saveIntoUploadedStatus(contribution: Contribution) { + contribution.contentUri?.let { + val imageSha1 = fileUtilsWrapper.getSHA1(appContext.contentResolver.openInputStream(it)) + val modifiedSha1 = fileUtilsWrapper.getSHA1(fileUtilsWrapper.getFileInputStream(contribution.localUri?.path)) + MainScope().launch { + uploadedStatusDao.insertUploaded( + UploadedStatus( + imageSha1, + modifiedSha1, + imageSha1 == modifiedSha1, + true + ) + ); + } + } } private fun findUniqueFileName(fileName: String): String { diff --git a/app/src/main/res/drawable-ldpi/circle_shape.xml b/app/src/main/res/drawable-ldpi/circle_shape.xml new file mode 100644 index 000000000..d581bfb9f --- /dev/null +++ b/app/src/main/res/drawable-ldpi/circle_shape.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-ldpi/commons.xml b/app/src/main/res/drawable-ldpi/commons.xml new file mode 100644 index 000000000..4c2e6cabf --- /dev/null +++ b/app/src/main/res/drawable-ldpi/commons.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_arrow_back_black.xml b/app/src/main/res/drawable/ic_arrow_back_black.xml new file mode 100644 index 000000000..b5487b3ea --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_back_black.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_custom_image_picker.xml b/app/src/main/res/drawable/ic_custom_image_picker.xml new file mode 100644 index 000000000..7dd39280a --- /dev/null +++ b/app/src/main/res/drawable/ic_custom_image_picker.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_done_black.xml b/app/src/main/res/drawable/ic_done_black.xml new file mode 100644 index 000000000..2d3858a70 --- /dev/null +++ b/app/src/main/res/drawable/ic_done_black.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_done_white.xml b/app/src/main/res/drawable/ic_done_white.xml new file mode 100644 index 000000000..2728880b7 --- /dev/null +++ b/app/src/main/res/drawable/ic_done_white.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_custom_selector.xml b/app/src/main/res/layout/activity_custom_selector.xml new file mode 100644 index 000000000..d96918fee --- /dev/null +++ b/app/src/main/res/layout/activity_custom_selector.xml @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/bottom_container_location_picker.xml b/app/src/main/res/layout/bottom_container_location_picker.xml index 98a8216c5..2eafaa4b6 100644 --- a/app/src/main/res/layout/bottom_container_location_picker.xml +++ b/app/src/main/res/layout/bottom_container_location_picker.xml @@ -77,5 +77,4 @@ - \ No newline at end of file diff --git a/app/src/main/res/layout/custom_selector_info_dialog.xml b/app/src/main/res/layout/custom_selector_info_dialog.xml new file mode 100644 index 000000000..a34f247dd --- /dev/null +++ b/app/src/main/res/layout/custom_selector_info_dialog.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/custom_selector_toolbar.xml b/app/src/main/res/layout/custom_selector_toolbar.xml new file mode 100644 index 000000000..72ab6386a --- /dev/null +++ b/app/src/main/res/layout/custom_selector_toolbar.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_contributions_list.xml b/app/src/main/res/layout/fragment_contributions_list.xml index 49e4e60c7..923cc8343 100644 --- a/app/src/main/res/layout/fragment_contributions_list.xml +++ b/app/src/main/res/layout/fragment_contributions_list.xml @@ -69,6 +69,19 @@ app:fabSize="mini" app:srcCompat="@drawable/ic_photo_white_24dp" /> + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_custom_selector_folder.xml b/app/src/main/res/layout/item_custom_selector_folder.xml new file mode 100644 index 000000000..0f2a72828 --- /dev/null +++ b/app/src/main/res/layout/item_custom_selector_folder.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_custom_selector_image.xml b/app/src/main/res/layout/item_custom_selector_image.xml new file mode 100644 index 000000000..f04a71922 --- /dev/null +++ b/app/src/main/res/layout/item_custom_selector_image.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 5387ca117..f43772fb5 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -49,6 +49,8 @@ + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index ac5d31cf7..b3fb6d1a4 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -58,6 +58,7 @@ 0dp + 2dp 6dp 10dp 20dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e0cf7d790..b594d182d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -100,6 +100,7 @@ Settings Sign Up Featured Images + Custom Selector Category Peer Review About @@ -639,6 +640,15 @@ Upload your first media by tapping on the add button. The shadow of the image view of the location picker Image Location Check whether location is correct + Custom Selector + No Images + Done + Back + Welcome to Custom Picture Selector + This picker shows differently pictures that are already to Commons. + Unlike the picture on the left, the picture on the right has the Commons logo indicating it is already uploaded. + Awesome + This image has already been uploaded to Commons. WLM You are contributing to Wiki Loves Monuments Campaign. Related templates will be added accordingly. Display monuments @@ -646,5 +656,4 @@ Upload your first media by tapping on the add button. LEARN MORE Wiki Loves Monuments Wiki Loves Monuments is an international photo contest for monuments organised by Wikimedia - diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 308291dea..1ae9e0a7c 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -55,6 +55,8 @@ @color/white @color/white @drawable/ic_search_white_24dp + @drawable/ic_done_white + @drawable/ic_arrow_back_white false false @@ -113,6 +115,8 @@ @color/disabled_button_text_color_dark @color/primaryDarkColor @drawable/ic_search_blue_24dp + @drawable/ic_done_black + @drawable/ic_arrow_back_black false false 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 new file mode 100644 index 000000000..2fe382368 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/helper/ImageHelperTest.kt @@ -0,0 +1,55 @@ +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.jupiter.api.Test +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") + private val mockImageList = ArrayList(listOf(folderImage1, folderImage2)) + private val folderImageList1 = ArrayList(listOf(folderImage1)) + private val folderImageList2 = ArrayList(listOf(folderImage2)) + + /** + * Test folder list from images. + */ + @Test + fun folderListFromImages() { + val folderList = ArrayList(listOf(Folder(1, "bucket1", folderImageList1), Folder(2, "bucket2", folderImageList2))) + assertEquals(folderList, ImageHelper.folderListFromImages(mockImageList)) + } + + /** + * Test filter images. + */ + @Test + fun filterImages() { + assertEquals(folderImageList1, ImageHelper.filterImages(mockImageList, 1)) + } + + /** + * Test get index from image list. + */ + @Test + fun getIndex() { + assertEquals(1,ImageHelper.getIndex(mockImageList, folderImage2)) + } + + /** + * Test get index list. + */ + @Test + 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/ui/adapter/FolderAdapterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapterTest.kt new file mode 100644 index 000000000..1c2a663f3 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/FolderAdapterTest.kt @@ -0,0 +1,99 @@ +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 +import android.view.View +import android.widget.GridLayout +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.whenever +import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.customselector.listeners.FolderClickListener +import fr.free.nrw.commons.customselector.model.Folder +import fr.free.nrw.commons.customselector.model.Image +import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity +import org.junit.Before +import org.junit.Test +import org.junit.jupiter.api.Assertions.assertEquals +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 + +/** + * Custom Selector Folder Adapter Test. + */ +@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 + private lateinit var image: Image + private lateinit var folder: Folder + private lateinit var folderList: ArrayList + + @Mock + private lateinit var context: Context + + @Mock + private lateinit var mockContentResolver: ContentResolver + + @Before + @Throws(Exception::class) + fun setUp() { + MockitoAnnotations.initMocks(this) + + activity = Robolectric.buildActivity(CustomSelectorActivity::class.java).get() + image = Image(1, "image", uri, "abc/abc", 1, "bucket1") + folder = Folder(1, "bucket1", ArrayList(listOf(image))) + folderList = ArrayList(listOf(folder, folder, folder)) + folderAdapter = FolderAdapter(activity, activity as FolderClickListener) + } + + /** + * Test on create view holder. + */ + @Test + fun onCreateViewHolder() { + folderAdapter.createViewHolder(GridLayout(activity), 0) + } + + /** + * Test on bind view holder. + */ + @Test + fun onBindViewHolder() { + val inflater = activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + val listItemView: View = inflater.inflate(R.layout.item_custom_selector_folder, null, false) + + whenever(context.contentResolver).thenReturn(mockContentResolver) + whenever(mockContentResolver.getType(any())).thenReturn("jpg") + Whitebox.setInternalState(folderAdapter, "context", context) + folderAdapter.init(folderList) + folderAdapter.onBindViewHolder(FolderAdapter.FolderViewHolder(listItemView), 0) + } + + /** + * Test init. + */ + @Test + fun init() { + folderAdapter.init(folderList) + } + + /** + * Test get item count. + */ + @Test + 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 new file mode 100644 index 000000000..fac24cb32 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapterTest.kt @@ -0,0 +1,145 @@ +package fr.free.nrw.commons.customselector.ui.adapter + +import android.content.ContentResolver +import android.content.Context +import android.net.Uri +import android.view.LayoutInflater +import android.view.View +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.customselector.listeners.ImageSelectListener +import fr.free.nrw.commons.customselector.model.Image +import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity +import fr.free.nrw.commons.customselector.ui.selector.ImageLoader +import org.junit.Before +import org.junit.Test +import org.junit.jupiter.api.Assertions +import org.junit.runner.RunWith +import org.mockito.* +import org.powermock.reflect.Whitebox +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import java.lang.reflect.Field + +/** + * Custom Selector image adapter test. + */ +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [21], application = TestCommonsApplication::class) +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 + + private lateinit var activity: CustomSelectorActivity + private lateinit var imageAdapter: ImageAdapter + 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 + + + /** + * Set up variables. + */ + @Before + @Throws(Exception::class) + fun setUp() { + MockitoAnnotations.initMocks(this) + activity = Robolectric.buildActivity(CustomSelectorActivity::class.java).get() + imageAdapter = ImageAdapter(activity, imageSelectListener, imageLoader) + image = Image(1, "image", uri, "abc/abc", 1, "bucket1") + images = ArrayList() + + val inflater = activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + val listItemView: View = inflater.inflate(R.layout.item_custom_selector_image, null, false) + holder = ImageAdapter.ImageViewHolder(listItemView) + + selectedImageField = imageAdapter.javaClass.getDeclaredField("selectedImages") + selectedImageField.isAccessible = true + } + + /** + * Test on create view holder. + */ + @Test + fun onCreateViewHolder() { + imageAdapter.createViewHolder(GridLayout(activity), 0) + } + + /** + * Test on bind view holder. + */ + @Test + fun onBindViewHolder() { + + whenever(context.contentResolver).thenReturn(mockContentResolver) + whenever(mockContentResolver.getType(uri)).thenReturn("jpg") + Whitebox.setInternalState(imageAdapter, "context", context) + + // Parameters. + images.add(image) + imageAdapter.init(images) + + // Test conditions. + imageAdapter.onBindViewHolder(holder, 0) + selectedImageField.set(imageAdapter, images) + imageAdapter.onBindViewHolder(holder, 0) + } + + /** + * Test init. + */ + @Test + fun init() { + imageAdapter.init(images) + } + + /** + * Test private function select or remove image. + */ + @Test + fun selectOrRemoveImage() { + // Access function + val func = imageAdapter.javaClass.getDeclaredMethod("selectOrRemoveImage", ImageAdapter.ImageViewHolder::class.java, Int::class.java) + func.isAccessible = true + + // Parameters + images.addAll(listOf(image, image)) + imageAdapter.init(images) + + // Test conditions + holder.itemUploaded() + func.invoke(imageAdapter, holder, 0) + holder.itemNotUploaded() + func.invoke(imageAdapter, holder, 0) + selectedImageField.set(imageAdapter, images) + func.invoke(imageAdapter, holder, 1) + } + + /** + * Test get item count. + */ + @Test + fun getItemCount() { + Assertions.assertEquals(0, imageAdapter.itemCount) + } + + /** + * Test getImageId + */ + @Test + fun getImageIdAt() { + imageAdapter.init(listOf(image)) + Assertions.assertEquals(1, imageAdapter.getImageIdAt(0)) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..21007daeb --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivityTest.kt @@ -0,0 +1,119 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.net.Uri +import android.os.Bundle +import android.os.Looper +import android.os.Looper.getMainLooper +import fr.free.nrw.commons.TestAppAdapter +import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.contributions.MainActivity +import fr.free.nrw.commons.customselector.model.Folder +import fr.free.nrw.commons.customselector.model.Image +import org.junit.Before +import org.junit.Test +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.runner.RunWith +import org.mockito.MockitoAnnotations +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.Shadows +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config +import org.wikipedia.AppAdapter +import java.lang.reflect.Method + +/** + * Custom Selector Activity Test + */ +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [21], application = TestCommonsApplication::class) +class CustomSelectorActivityTest { + + private lateinit var activity: CustomSelectorActivity + + /** + * Set up the tests. + */ + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + AppAdapter.set(TestAppAdapter()) + + activity = Robolectric.buildActivity(CustomSelectorActivity::class.java) + .get() + val onCreate = activity.javaClass.getDeclaredMethod("onCreate", Bundle::class.java) + onCreate.isAccessible = true + onCreate.invoke(activity, null) + } + + /** + * Test activity not null. + */ + @Test + @Throws(Exception::class) + fun testActivityNotNull() { + assertNotNull(activity) + } + + /** + * Test changeTitle function. + */ + @Test + @Throws(Exception::class) + fun testChangeTitle() { + val func = activity.javaClass.getDeclaredMethod("changeTitle", String::class.java) + func.isAccessible = true + func.invoke(activity, "test") + } + + /** + * Test onFolderClick function. + */ + @Test + @Throws(Exception::class) + fun testOnFolderClick() { + activity.onFolderClick(1, "test", 0); + } + + /** + * Test selectedImagesChanged function. + */ + @Test + @Throws(Exception::class) + fun testOnSelectedImagesChanged() { + activity.onSelectedImagesChanged(ArrayList()) + } + + /** + * Test onDone function. + */ + @Test + @Throws(Exception::class) + fun testOnDone() { + activity.onDone() + activity.onSelectedImagesChanged(ArrayList(arrayListOf(Image(1, "test", Uri.parse("test"), "test", 1)))); + activity.onDone() + } + + /** + * Test onBackPressed Function. + */ + @Test + @Throws(Exception::class) + fun testOnBackPressed() { + activity.onBackPressed() + } + + /** + * Test onDestroy Function. + */ + @Test + @Throws(Exception::class) + fun testOnDestroy() { + val method: Method = CustomSelectorActivity::class.java.getDeclaredMethod( + "onDestroy" + ) + method.isAccessible = true + method.invoke(activity) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..309392d4d --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorViewModelTest.kt @@ -0,0 +1,41 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.content.Context +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +/** + * Custom Selector View Model test. + */ +class CustomSelectorViewModelTest { + + private lateinit var viewModel: CustomSelectorViewModel + + @Mock + private lateinit var imageFileLoader: ImageFileLoader + + @Mock + private lateinit var context: Context + + /** + * Set up the test. + */ + @Before + fun setUp(){ + MockitoAnnotations.initMocks(this) + viewModel = CustomSelectorViewModel(context, imageFileLoader); + } + + /** + * Test onCleared(); + */ + @Test + fun testOnCleared(){ + val func = viewModel.javaClass.getDeclaredMethod("onCleared") + func.isAccessible = true + 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 new file mode 100644 index 000000000..c77f24342 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/FolderFragmentTest.kt @@ -0,0 +1,141 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.content.Context +import android.os.Bundle +import android.os.Looper +import android.view.LayoutInflater +import android.view.View +import fr.free.nrw.commons.customselector.model.Result +import android.widget.ProgressBar +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentTransaction +import androidx.recyclerview.widget.RecyclerView +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.soloader.SoLoader +import fr.free.nrw.commons.R +import fr.free.nrw.commons.TestAppAdapter +import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.customselector.model.CallbackStatus +import fr.free.nrw.commons.customselector.ui.adapter.FolderAdapter +import org.junit.Before +import org.junit.Test +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.powermock.reflect.Whitebox +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.Shadows +import org.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +import org.wikipedia.AppAdapter +import java.lang.reflect.Field + +/** + * Custom Selector Folder Fragment Test. + */ +@RunWith(RobolectricTestRunner::class) +@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 layoutInflater: LayoutInflater + private lateinit var context: Context + private lateinit var viewModelField:Field + + @Mock + private lateinit var adapter: FolderAdapter + + @Mock + private lateinit var savedInstanceState: Bundle + + /** + * Setup the folder fragment. + */ + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + context = RuntimeEnvironment.application.applicationContext + AppAdapter.set(TestAppAdapter()) + SoLoader.setInTestMode() + Fresco.initialize(context) + val activity = Robolectric.buildActivity(CustomSelectorActivity::class.java).create().get() + + fragment = FolderFragment.newInstance() + val fragmentManager: FragmentManager = activity.supportFragmentManager + val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction() + fragmentTransaction.add(fragment, null) + fragmentTransaction.commit() + + layoutInflater = LayoutInflater.from(activity) + view = layoutInflater.inflate(R.layout.fragment_custom_selector, null) as View + + selectorRV = view.findViewById(R.id.selector_rv) + loader = view.findViewById(R.id.loader) + + Whitebox.setInternalState(fragment, "folderAdapter", adapter) + Whitebox.setInternalState(fragment, "selectorRV", selectorRV ) + Whitebox.setInternalState(fragment, "loader", loader) + + viewModelField = fragment.javaClass.getDeclaredField("viewModel") + viewModelField.isAccessible = true + } + + /** + * Test onCreateView + */ + @Test + @Throws(Exception::class) + fun testOnCreateView() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + viewModelField.set(fragment, null) + fragment.onCreateView(layoutInflater, null, savedInstanceState) + } + + /** + * Test onCreate + */ + @Test + @Throws(Exception::class) + fun testOnCreate() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + fragment.onCreate(savedInstanceState) + } + + /** + * Test columnCount. + */ + @Test + fun testColumnCount() { + val func = fragment.javaClass.getDeclaredMethod("columnCount") + func.isAccessible = true + assertEquals(2, func.invoke(fragment)) + } + + /** + * Test handleResult. + */ + @Test + fun testHandleResult() { + val func = fragment.javaClass.getDeclaredMethod("handleResult", Result::class.java) + func.isAccessible = true + func.invoke(fragment, Result(CallbackStatus.SUCCESS, arrayListOf())) + } + + /** + * Test onResume. + */ + @Test + fun testOnResume() { + val func = fragment.javaClass.getDeclaredMethod("onResume") + 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 new file mode 100644 index 000000000..e30d47216 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFileLoaderTest.kt @@ -0,0 +1,123 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.content.ContentResolver +import android.content.Context +import android.provider.MediaStore +import com.nhaarman.mockitokotlin2.anyOrNull +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.same +import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.customselector.listeners.ImageLoaderListener +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +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.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +import org.robolectric.fakes.RoboCursor +import java.io.File +import kotlin.coroutines.CoroutineContext + +/** + * Custom Selector Image File loader test. + */ +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [21], application = TestCommonsApplication::class) +@LooperMode(LooperMode.Mode.PAUSED) +class ImageFileLoaderTest { + + @Mock + private lateinit var mockContentResolver: ContentResolver + + @Mock + private lateinit var context: Context; + + @Mock + private lateinit var imageLoaderListener: ImageLoaderListener + + @Mock + private lateinit var coroutineScope: CoroutineScope + + private lateinit var imageCursor: RoboCursor + private lateinit var coroutineContext: CoroutineContext + private lateinit var projection: List + private lateinit var imageFileLoader: ImageFileLoader + + /** + * Setup before tests. + */ + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + 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 + ) + + Whitebox.setInternalState(imageFileLoader, "coroutineContext", coroutineContext) + } + + /** + * Test loading device images. + */ + @Test + fun testLoadDeviceImages() { + imageFileLoader.loadDeviceImages(imageLoaderListener, coroutineScope) + } + + /** + * Test get images from the device function. + */ + @Test + fun testGetImages() { + val func = imageFileLoader.javaClass.getDeclaredMethod( + "getImages", + ImageLoaderListener::class.java + ) + func.isAccessible = true + + val image1 = arrayOf(1, "imageLoaderTestFile", "src/test/resources/imageLoaderTestFile", 1, "downloads") + val image2 = arrayOf(2, "imageLoaderTestFile", null, 1, "downloads") + File("src/test/resources/imageLoaderTestFile").createNewFile() + + imageCursor.setColumnNames(projection) + imageCursor.setResults(arrayOf(image1, image2)); + + val contentResolver: ContentResolver = mock { + on { + query( + same(MediaStore.Images.Media.EXTERNAL_CONTENT_URI), + anyOrNull(), + anyOrNull(), + anyOrNull(), + anyOrNull() + ) + } doReturn imageCursor; + } + + // test null cursor. + `when`( + context.contentResolver + ).thenReturn(mockContentResolver) + func.invoke(imageFileLoader, imageLoaderListener); + + // test demo cursor. + `when`( + context.contentResolver + ).thenReturn(contentResolver) + 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 new file mode 100644 index 000000000..10ebcc4e8 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageFragmentTest.kt @@ -0,0 +1,166 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.content.Context +import android.os.Bundle +import android.os.Looper +import android.os.Looper.getMainLooper +import android.view.LayoutInflater +import android.view.View +import android.widget.ProgressBar +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentTransaction +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.soloader.SoLoader +import com.nhaarman.mockitokotlin2.whenever +import fr.free.nrw.commons.R +import fr.free.nrw.commons.TestAppAdapter +import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.customselector.model.CallbackStatus +import fr.free.nrw.commons.customselector.model.Image +import fr.free.nrw.commons.customselector.model.Result +import fr.free.nrw.commons.customselector.ui.adapter.ImageAdapter +import org.junit.Before +import org.junit.Test +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.powermock.reflect.Whitebox +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.Shadows +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +import org.wikipedia.AppAdapter +import java.lang.reflect.Field + +/** + * Custom Selector Image Fragment Test. + */ +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [21], application = TestCommonsApplication::class) +@LooperMode(LooperMode.Mode.PAUSED) +class ImageFragmentTest { + + private lateinit var fragment: ImageFragment + private lateinit var view: View + 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 + + @Mock + private lateinit var layoutManager: GridLayoutManager + + @Mock + private lateinit var image: Image + + @Mock + private lateinit var adapter: ImageAdapter + + @Mock + private lateinit var savedInstanceState: Bundle + + /** + * Setup the image fragment. + */ + @Before + fun setUp(){ + MockitoAnnotations.initMocks(this) + context = RuntimeEnvironment.application.applicationContext + AppAdapter.set(TestAppAdapter()) + SoLoader.setInTestMode() + Fresco.initialize(context) + val activity = Robolectric.buildActivity(CustomSelectorActivity::class.java).create().get() + + fragment = ImageFragment.newInstance(1,0) + val fragmentManager: FragmentManager = activity.supportFragmentManager + val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction() + fragmentTransaction.add(fragment, null) + fragmentTransaction.commit() + + layoutInflater = LayoutInflater.from(activity) + view = layoutInflater.inflate(R.layout.fragment_custom_selector, null, false) as View + selectorRV = view.findViewById(R.id.selector_rv) + loader = view.findViewById(R.id.loader) + + Whitebox.setInternalState(fragment, "imageAdapter", adapter) + Whitebox.setInternalState(fragment, "selectorRV", selectorRV ) + Whitebox.setInternalState(fragment, "loader", loader) + + viewModelField = fragment.javaClass.getDeclaredField("viewModel") + viewModelField.isAccessible = true + } + + /** + * Test onCreate + */ + @Test + @Throws(Exception::class) + fun testOnCreate(){ + Shadows.shadowOf(Looper.getMainLooper()).idle() + fragment.onCreate(savedInstanceState); + } + + /** + * Test onCreateView + */ + @Test + @Throws(Exception::class) + fun testOnCreateView() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + viewModelField.set(fragment, null) + fragment.onCreateView(layoutInflater, null, savedInstanceState) + } + + /** + * Test handleResult. + */ + @Test + 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))) + } + + /** + * Test getSpanCount. + */ + @Test + fun testGetSpanCount() { + val func = fragment.javaClass.getDeclaredMethod("getSpanCount") + func.isAccessible = true + assertEquals(3, func.invoke(fragment)) + } + + + /** + * Test onResume. + */ + @Test + fun testOnResume() { + val func = fragment.javaClass.getDeclaredMethod("onResume") + func.isAccessible = true + func.invoke(fragment) + } + + /** + * Test onDestroy. + */ + @Test + fun testOnDestroy() { + shadowOf(getMainLooper()).idle() + selectorRV.layoutManager = layoutManager + whenever(layoutManager.findFirstVisibleItemPosition()).thenReturn(1) + val func = fragment.javaClass.getDeclaredMethod("onDestroy") + 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/ImageLoaderTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageLoaderTest.kt new file mode 100644 index 000000000..fe26921e5 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/customselector/ui/selector/ImageLoaderTest.kt @@ -0,0 +1,220 @@ +package fr.free.nrw.commons.customselector.ui.selector + +import android.content.ContentResolver +import android.content.Context +import android.net.Uri +import com.nhaarman.mockitokotlin2.* +import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.customselector.database.UploadedStatus +import fr.free.nrw.commons.customselector.database.UploadedStatusDao +import fr.free.nrw.commons.customselector.model.Image +import fr.free.nrw.commons.customselector.ui.adapter.ImageAdapter +import fr.free.nrw.commons.filepicker.PickedFiles +import fr.free.nrw.commons.filepicker.UploadableFile +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 junit.framework.Assert +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.* +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.* +import org.powermock.api.mockito.PowerMockito +import org.powermock.core.classloader.annotations.PrepareForTest +import org.powermock.modules.junit4.PowerMockRunner +import org.powermock.reflect.Whitebox +import org.robolectric.annotation.Config +import java.io.File +import java.io.FileInputStream +import java.util.* +import kotlin.collections.HashMap + +/** + * Image Loader Test. + */ +@RunWith(PowerMockRunner::class) +@PrepareForTest(PickedFiles::class) +@Config(sdk = [21], application = TestCommonsApplication::class) +@ExperimentalCoroutinesApi +class ImageLoaderTest { + + @Mock + private lateinit var uri:Uri + + @Mock + private lateinit var mediaClient: MediaClient + + @Mock + private lateinit var single: Single + + @Mock + private lateinit var fileProcessor: FileProcessor + + @Mock + private lateinit var fileUtilsWrapper: FileUtilsWrapper + + @Mock + private lateinit var uploadedStatusDao: UploadedStatusDao + + @Mock + private lateinit var holder: ImageAdapter.ImageViewHolder + + @Mock + private lateinit var context: Context + + @Mock + private lateinit var uploadableFile: UploadableFile + + @Mock + private lateinit var inputStream: FileInputStream + + @Mock + private lateinit var contentResolver: ContentResolver + + @ExperimentalCoroutinesApi + private val testDispacher = TestCoroutineDispatcher() + + private lateinit var imageLoader: ImageLoader; + private var mapImageSHA1: HashMap = HashMap() + private var mapHolderImage : HashMap = HashMap() + private var mapResult: HashMap = HashMap() + private var mapModifiedImageSHA1: HashMap = HashMap() + private lateinit var image: Image; + private lateinit var uploadedStatus: UploadedStatus; + + /** + * Setup before test. + */ + @Before + @ExperimentalCoroutinesApi + fun setup() { + Dispatchers.setMain(testDispacher) + MockitoAnnotations.initMocks(this) + + imageLoader = + ImageLoader(mediaClient, fileProcessor, fileUtilsWrapper, uploadedStatusDao, 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, "context", context) + Whitebox.setInternalState(imageLoader, "ioDispatcher", testDispacher) + Whitebox.setInternalState(imageLoader, "defaultDispatcher", testDispacher) + + whenever(contentResolver.openInputStream(uri)).thenReturn(inputStream) + whenever(context.contentResolver).thenReturn(contentResolver) + whenever(fileUtilsWrapper.getSHA1(inputStream)).thenReturn("testSha1") + } + + /** + * Reset Dispatchers. + */ + @After + @ExperimentalCoroutinesApi + fun tearDown() { + Dispatchers.resetMain() + testDispacher.cleanupTestCoroutines() + } + + /** + * Test queryAndSetView with upload Status as null. + */ + @Test + fun testQueryAndSetViewUploadedStatusNull() = testDispacher.runBlockingTest { + whenever(uploadedStatusDao.getUploadedFromImageSHA1(any())).thenReturn(null) + mapModifiedImageSHA1[image] = "testSha1" + mapImageSHA1[uri] = "testSha1" + + mapResult["testSha1"] = ImageLoader.Result.TRUE + imageLoader.queryAndSetView(holder, image) + + mapResult["testSha1"] = ImageLoader.Result.FALSE + imageLoader.queryAndSetView(holder, image) + } + + /** + * Test queryAndSetView with upload Status not null (ie retrieved from table) + */ + @Test + fun testQueryAndSetViewUploadedStatusNotNull() = testDispacher.runBlockingTest { + whenever(uploadedStatusDao.getUploadedFromImageSHA1(any())).thenReturn(uploadedStatus) + imageLoader.queryAndSetView(holder, image) + } + + /** + * Test querySha1 + */ + @Test + fun testQuerySha1() = testDispacher.runBlockingTest { + + whenever(single.blockingGet()).thenReturn(true) + whenever(mediaClient.checkFileExistsUsingSha("testSha1")).thenReturn(single) + whenever(fileUtilsWrapper.getSHA1(any())).thenReturn("testSha1") + + imageLoader.querySHA1("testSha1") + } + + /** + * Test getSha1 + */ + @Test + @ExperimentalCoroutinesApi + fun testGetSha1() = testDispacher.runBlockingTest { + + PowerMockito.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") + + Assert.assertEquals("testSha1", imageLoader.getSHA1(image)); + whenever(PickedFiles.pickedExistingPicture(context, Uri.parse("test"))).thenReturn( + uploadableFile + ) + + mapModifiedImageSHA1[image] = "testSha2" + Assert.assertEquals("testSha2", imageLoader.getSHA1(image)); + } + + /** + * Test getResultFromUploadedStatus. + */ + @Test + fun testGetResultFromUploadedStatus() { + 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)) + + // test Result.TRUE + uploadedStatus.imageResult = true; + Assert.assertEquals(ImageLoader.Result.TRUE, + imageLoader.getResultFromUploadedStatus(uploadedStatus)) + } + + @Test + fun testCleanUP() { + imageLoader.cleanUP() + } +} \ 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 b9712df04..ae841cd1c 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 @@ -54,4 +54,14 @@ class FilePickerTest { verify(activity).startActivityForResult(ArgumentMatchers.anyObject(), requestCodeCaptor?.capture()?.toInt()!!) assertEquals(requestCodeCaptor?.value, RequestCodes.TAKE_PICTURE) } + + @Test + fun testOpenCustomSelectorRequestCode() { + `when`(PreferenceManager.getDefaultSharedPreferences(activity)).thenReturn(sharedPref) + `when`(sharedPref.edit()).thenReturn(sharedPreferencesEditor) + `when`(sharedPref.edit().putInt("type", 0)).thenReturn(sharedPreferencesEditor) + FilePicker.openCustomSelector(activity, 0) + verify(activity).startActivityForResult(ArgumentMatchers.anyObject(), requestCodeCaptor?.capture()?.toInt()!!) + assertEquals(requestCodeCaptor?.value, RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) + } } \ No newline at end of file diff --git a/app/src/test/resources/imageLoaderTestFile b/app/src/test/resources/imageLoaderTestFile new file mode 100644 index 000000000..e69de29bb From c1e83d7ee88d5f1fd298ef3814bd749f53d55aa4 Mon Sep 17 00:00:00 2001 From: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Date: Wed, 18 Aug 2021 22:02:40 +0530 Subject: [PATCH 0002/1165] Add some more File Picker Unit Tests (#4536) * Add some more File Picker Unit Tests * Fix failing test * Fix syntax error --- .../nrw/commons/filepicker/FilePickerTest.kt | 173 +++++++++++++++++- .../filepicker/ShadowFileProvider.java | 32 ++++ .../UploadCategoriesFragmentUnitTests.kt | 4 + 3 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/filepicker/ShadowFileProvider.java 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 ae841cd1c..9b84f2582 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 @@ -2,35 +2,57 @@ package fr.free.nrw.commons.filepicker import android.app.Activity import android.app.Application +import android.content.Context +import android.content.Intent import android.content.SharedPreferences +import android.net.Uri import androidx.preference.PreferenceManager import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions +import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.filepicker.Constants.RequestCodes -import fr.free.nrw.commons.filepicker.FilePicker.handleActivityResult import junit.framework.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.Mockito.mock +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +import java.lang.reflect.Method +@RunWith(RobolectricTestRunner::class) +@Config( + sdk = [21], + application = TestCommonsApplication::class, + shadows = [ShadowFileProvider::class] +) +@LooperMode(LooperMode.Mode.PAUSED) class FilePickerTest { + @Mock internal lateinit var activity: Activity + @Mock internal lateinit var sharedPref: SharedPreferences + @Mock var sharedPreferencesEditor: SharedPreferences.Editor? = null + @Mock var unit: Unit? = null @Captor var requestCodeCaptor: ArgumentCaptor? = null + private lateinit var context: Context + @Before fun setup() { MockitoAnnotations.initMocks(this) + context = RuntimeEnvironment.application.applicationContext } @Test @@ -39,7 +61,10 @@ class FilePickerTest { `when`(sharedPref.edit()).thenReturn(sharedPreferencesEditor) `when`(sharedPref.edit().putInt("type", 0)).thenReturn(sharedPreferencesEditor) FilePicker.openGallery(activity, 0) - verify(activity).startActivityForResult(ArgumentMatchers.anyObject(), requestCodeCaptor?.capture()?.toInt()!!) + verify(activity).startActivityForResult( + ArgumentMatchers.anyObject(), + requestCodeCaptor?.capture()?.toInt()!! + ) assertEquals(requestCodeCaptor?.value, RequestCodes.PICK_PICTURE_FROM_GALLERY) } @@ -51,10 +76,150 @@ class FilePickerTest { val mockApplication = mock(Application::class.java) `when`(activity.applicationContext).thenReturn(mockApplication) FilePicker.openCameraForImage(activity, 0) - verify(activity).startActivityForResult(ArgumentMatchers.anyObject(), requestCodeCaptor?.capture()?.toInt()!!) + verify(activity).startActivityForResult( + ArgumentMatchers.anyObject(), + requestCodeCaptor?.capture()?.toInt()!! + ) assertEquals(requestCodeCaptor?.value, RequestCodes.TAKE_PICTURE) } + @Test + fun testCreateCameraPictureFile() { + val mockFilePicker = mock(FilePicker::class.java) + val method: Method = FilePicker::class.java.getDeclaredMethod( + "createCameraPictureFile", + Context::class.java + ) + method.isAccessible = true + method.invoke(mockFilePicker, context) + } + + @Test + fun testCreateCameraForImageIntent() { + val mockFilePicker = mock(FilePicker::class.java) + val method: Method = FilePicker::class.java.getDeclaredMethod( + "createCameraForImageIntent", + Context::class.java, + Int::class.java + ) + method.isAccessible = true + method.invoke(mockFilePicker, context, 0) + } + + @Test + fun testRevokeWritePermission() { + 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 + ) + method.isAccessible = true + method.invoke(mockFilePicker, mockContext, mockUri) + } + + @Test + fun testRestoreType() { + val mockFilePicker = mock(FilePicker::class.java) + val method: Method = FilePicker::class.java.getDeclaredMethod( + "restoreType", + Context::class.java + ) + method.isAccessible = true + method.invoke(mockFilePicker, context) + } + + @Test + fun testTakenCameraPicture() { + val mockFilePicker = mock(FilePicker::class.java) + val method: Method = FilePicker::class.java.getDeclaredMethod( + "takenCameraPicture", + Context::class.java + ) + method.isAccessible = true + method.invoke(mockFilePicker, context) + } + + @Test + fun testTakenCameraPictureCaseTrue() { + 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 + ) + method.isAccessible = true + method.invoke(mockFilePicker, activity) + } + + @Test + fun testTakenCameraVideo() { + val mockFilePicker = mock(FilePicker::class.java) + val method: Method = FilePicker::class.java.getDeclaredMethod( + "takenCameraVideo", + Context::class.java + ) + method.isAccessible = true + method.invoke(mockFilePicker, context) + } + + @Test + fun testTakenCameraVideoCaseTrue() { + 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 + ) + method.isAccessible = true + method.invoke(mockFilePicker, activity) + } + + @Test + 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 + ) + method.isAccessible = true + method.invoke(mockFilePicker, mockIntent) + } + + @Test + fun testHandleActivityResultCaseOne() { + val mockIntent = mock(Intent::class.java) + FilePicker.handleActivityResult( + RequestCodes.FILE_PICKER_IMAGE_IDENTIFICATOR, + Activity.RESULT_OK, + mockIntent, + activity, + object : DefaultCallback() { + override fun onCanceled(source: FilePicker.ImageSource, type: Int) { + super.onCanceled(source, type) + } + + override fun onImagePickerError( + e: Exception, + source: FilePicker.ImageSource, + type: Int + ) { + } + + override fun onImagesPicked( + imagesFiles: List, + source: FilePicker.ImageSource, + type: Int + ) { + } + }) + } + @Test fun testOpenCustomSelectorRequestCode() { `when`(PreferenceManager.getDefaultSharedPreferences(activity)).thenReturn(sharedPref) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/filepicker/ShadowFileProvider.java b/app/src/test/kotlin/fr/free/nrw/commons/filepicker/ShadowFileProvider.java new file mode 100644 index 000000000..4da9e2690 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/filepicker/ShadowFileProvider.java @@ -0,0 +1,32 @@ +package fr.free.nrw.commons.filepicker; + +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; +import android.provider.OpenableColumns; +import androidx.core.content.FileProvider; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@Implements(FileProvider.class) +public class ShadowFileProvider { + + @Implementation + public Cursor query(final Uri uri, final String[] projection, final String selection, + final String[] selectionArgs, + final String sortOrder) { + + if (uri == null) { + return null; + } + + final String[] columns = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}; + final Object[] values = {"dummy", 500}; + final MatrixCursor cursor = new MatrixCursor(columns, 1); + + if (!uri.equals(Uri.EMPTY)) { + cursor.addRow(values); + } + return cursor; + } +} 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 c7817f5d3..8620822f3 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 @@ -65,6 +65,9 @@ class UploadCategoriesFragmentUnitTests { @Mock private lateinit var tvTitle: TextView + @Mock + private lateinit var tvSubTitle: TextView + @Mock private lateinit var tooltip: ImageView @@ -104,6 +107,7 @@ class UploadCategoriesFragmentUnitTests { Whitebox.setInternalState(fragment, "rvCategories", rvCategories) Whitebox.setInternalState(fragment, "tvTitle", tvTitle) Whitebox.setInternalState(fragment, "tooltip", tooltip) + Whitebox.setInternalState(fragment, "tvSubTitle", tvSubTitle) } @Test From 68f2b65c5b41649141e3704e8bed805e7d6a9f83 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 19 Aug 2021 13:01:38 +0200 Subject: [PATCH 0003/1165] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-de/strings.xml | 6 +++++ app/src/main/res/values-fa/strings.xml | 14 +++++++++++ app/src/main/res/values-fr/strings.xml | 16 +++++++++++++ app/src/main/res/values-lb/strings.xml | 6 +++++ app/src/main/res/values-mnw/strings.xml | 5 ++++ app/src/main/res/values-pl/strings.xml | 3 +++ app/src/main/res/values-pms/strings.xml | 1 + app/src/main/res/values-qq/strings.xml | 1 + app/src/main/res/values-ta/strings.xml | 32 +++++++++++++++++++++++++ app/src/main/res/values-tr/strings.xml | 16 +++++++++++++ app/src/main/res/values-zh/strings.xml | 5 ++++ 11 files changed, 105 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index aaabdc763..7ce939834 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -334,6 +334,7 @@ Darf dieses Bildschirmfoto hochgeladen werden? App teilen Fehler beim Abrufen der Orte in der Nähe. + Fehler beim Abrufen von Denkmälern in der Nähe. Keine letzten Suchanfragen Bist du sicher, dass du deinen Suchverlauf löschen möchtest? Möchtest du diese Suche löschen? @@ -617,4 +618,9 @@ Der Schatten der Bildansicht der Ortsauswahl Bildstandort Überprüfe, ob der Standort korrekt ist + Du beiligst dich an der Wiki Loves Monuments-Kampagne. Zugehörige Vorlagen werden entsprechend hinzugefügt. + Denkmäler anzeigen + Es ist der Wiki Loves Monuments Monat! + MEHR ERFAHREN + Wiki Loves Monuments ist ein internationaler Fotowettbewerb für Denkmäler, der von Wikimedia organisiert wird. diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 939de2b98..94d5735df 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -119,6 +119,7 @@ تنظیمات ثبت نام تصاویر برگزیده + انتخاب‌گر سفارشی رده بررسی هم رده درباره @@ -315,6 +316,7 @@ آیا این عکس از صفحه برای بارگذاری مناسب است؟ اشتراک اپلیکیشن خطا در آوردن مکان‌های نزدیک. + خطا در واکشی یادمان‌های نزدیک. جستجوهای اخیر وجود ندارد آیا مطمئن هستید که می‌خواهید تاریخچه جستجو را حذف کنید؟ آیا می‌خواهید این جستجو را حذف کنید؟ @@ -455,4 +457,16 @@ هفتگی تمام وقت بارگذاری + انتخاب‌گر سفارشی + فاقد تصویر + انجام شد + بازگشت + به انتخاب‌گر سفارشی تصویر خوش آمدید + برخلاف تصویر سمت چپ، تصویر سمت راست دارای نشان‌وارهٔ ویکی‌انبار است که نشان می‌دهد از پیش بارگذاری شده‌است. + عالی + این تصویر از پیش در ویکی‌انبار بارگذاری شده‌است. + شما در حال مشارکت در پویش ویکی دوست‌دار یادمان‌ها هستید. الگوهای مرتبط متعاقباً افزوده خواهند شد. + نمایش یادمان‌ها + این ماه، ماه «ویکی دوست‌دار یادمان‌ها» است! + بیشتر بدانید diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 313e4068a..a36d2a6fe 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -132,6 +132,7 @@ Paramètres S’inscrire Images remarquables + Sélecteur personnalisé Catégorie Relecture par les pairs À propos @@ -342,6 +343,7 @@ Cette copie d’écran est-elle correcte pour être téléversée ? Partager l’application Erreur durant la recherche d’endroits proches. + Erreur de récupération des monuments à proximité. Aucune recherche récente Voulez-vous vraiment effacer votre historique de recherche ? Voulez-vous supprimer cette recherche ? @@ -625,4 +627,18 @@ L’ombre de l’affichage d’image du sélecteur d’événement Emplacement de l’image Vérifier si l’emplacement est correct + Sélecteur actuel + Pas d’images + Fait + Retour + Bienvenue au sélecteur d’images personnalisé + Ce sélecteur affiche différemment des images qui sont déjà dans Communs. + À l’inverse de l’image sur la gauche, l’image de droite a le logo Communs, indiquant qu’elle a déjà été téléchargée. + Génial + Cette image a déjà été téléchargée sur Communs. + Vous contribuez à la campagne de monuments aimés de Wiki. Les modèles associés seront ajoutés en accord avec cela. + Afficher les monuments + C’est le mois des monuments aimés de Wiki ! + EN SAVOIR PLUS + Monuments aimés de Wiki est un concours international de photographie de monuments, organisé par Wikimédia diff --git a/app/src/main/res/values-lb/strings.xml b/app/src/main/res/values-lb/strings.xml index f68fe56a1..9a081b104 100644 --- a/app/src/main/res/values-lb/strings.xml +++ b/app/src/main/res/values-lb/strings.xml @@ -354,4 +354,10 @@ An alle Sproochen Eng Plaz eraussichen Plaz eraussichen + Keng Biller + Fäerdeg + Zréck + Genial + Monumenter weisen + FIR MÉI ZE WËSSEN diff --git a/app/src/main/res/values-mnw/strings.xml b/app/src/main/res/values-mnw/strings.xml index 6ad0101ff..ebd9e2656 100644 --- a/app/src/main/res/values-mnw/strings.xml +++ b/app/src/main/res/values-mnw/strings.xml @@ -2,6 +2,7 @@ @@ -216,4 +217,8 @@ သွဟ် မဗၠေတ် ကဆံၚ် ဗီုရုပ် မတိုန်လဝ် + ဗီုရုပ် ဟွံမွဲ + တုဲဒှ် + ကလေင် + သေသာတ် diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index a73f71c07..6e8162b79 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -602,4 +602,7 @@ Licencja dla mediów Szczegóły mediów Zobacz stronę kategorii + Niestandardowy selektor + Brak obrazów + Tez plik został już przesłany do Commons. diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 4d4e8b057..408f58933 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -103,6 +103,7 @@ Paràmeter Marchesse Plance an evidensa + Seletor përsonalisà Categorìa Revision da par A propòsit diff --git a/app/src/main/res/values-qq/strings.xml b/app/src/main/res/values-qq/strings.xml index 3d065ac3b..d7432f021 100644 --- a/app/src/main/res/values-qq/strings.xml +++ b/app/src/main/res/values-qq/strings.xml @@ -171,4 +171,5 @@ {{Identical|Favorite}} <code>&amp;#169;</code> is the copyright symbol (©). A description of a visual element, location picker image shadow. Used for accesibility usually. + {{identical|Done}} diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 5cfe1f9a0..44cf9ba52 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -116,6 +116,8 @@ எடுத்துக்காட்டு பதிவேற்றம்: தலைப்பு: சிட்னி ஒப்பேரா மாளிகை விளக்கம்: விரிகுடாவின் குறுக்கே சிட்னி ஒப்பேரா மாளிகை பார்க்கப்படுகிறது + உங்கள் படங்களை பங்களிக்கவும். விக்கிபீடியா கட்டுரைகளை உயிர்ப்பிக்க உதவுங்கள்! + விக்கிபீடியாவில் படங்கள் விக்கிமீடியா காமன்ஸிலிருந்து வருகிறது. உலகெங்கிலும் உள்ள மக்களுக்கு கல்வி கற்பதற்கு உங்கள் படங்கள் உதவுகின்றன. இணையத்தில் நீங்கள் கண்ட பதிப்புரிமை பெற்ற பொருட்கள் மற்றும் சுவரொட்டிகள், புத்தக அட்டைகள் போன்றவற்றின் படங்களைத் தவிர்க்கவும். உங்களுக்கு கிடைத்தது என்று நினைக்கிறீர்களா? @@ -188,4 +190,34 @@ பதிவேற்ற செயல்முறைக்கு இணைய அணுகல் தேவை. உங்கள் பிணைய இணைப்பைச் சரிபார்க்கவும். படத்தில் காணப்படும் சிக்கல்கள் தயவுசெய்து நீங்களே எடுத்த படங்களை மட்டும் பதிவேற்றவும். இணையத்தில் நீங்கள் பதிவிறக்கம் செய்த படங்களை பதிவேற்றாதீர்கள். + பிழை! URL கிடைக்கவில்லை + நீக்குவதற்கு பரிந்துரை + இந்த படம் நீக்கப்படுவதற்கு பரிந்துரைக்கப்பட்டுள்ளது. + விவரங்களுக்கு வலைப்பக்கத்தைப் பார்க்கவும் + தவிர் + புகுபதிகை + தாங்கள் மெய்யாகவே மூடுவதற்கு விரும்புகிறீர்களா? + எதிர்காலத்தில் படங்களைப் பதிவேற்ற நீங்கள் உள்நுழைய வேண்டும். + இந்த செயலியல்பைப் பயன்படுத்த தயவுசெய்து உள்நுழைக + தற்காலிகச் சேமிப்பு இடத்திற்கு விக்கிஎழுத்தை நகலெடுக்கவும் + விக்கிஎழுத்து தற்காலிகச் சேமிப்பு இடத்திற்கு நகலெடுக்கப்பட்டது + அருகில் சரியாக வேலை செய்யாமல் போகலாம், இடம் கிடைக்கவில்லை. + அருகிலுள்ள இடங்களின் பட்டியலைக் காட்ட அனுமதி தேவை + திசைகள் + விக்கித்தரவு + விக்கிப்பீடியா + பொதுவகம் + மதிப்பிடுக + விவிமா + பயிற்சியை தவிர் + இணையம் கிடைக்கவில்லை + அறிவிப்புகளைப் பெறுவதில் பிழை + மதிப்பாய்விற்கு படத்தை பெறுவதில் பிழை. மீண்டும் முயற்சிக்க புதுப்பிப்பை அழுத்தவும். + அறிவிப்புகள் கிடைக்கவில்லை + மொழிபெயர்க்கவும் + மொழிகள் + நீங்கள் மொழிபெயர்ப்புகளைச் சமர்ப்பிக்க விரும்பும் மொழியைத் தேர்ந்தெடுக்கவும் + தொடர் + ரத்து செய் + மறு முயற்சி செய் diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index f2d80b0c6..fc550f043 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -125,6 +125,7 @@ Ayarlar Kaydol Seçkin Resimler + Özel Seçici Kategori Hakem Denetimi Hakkında @@ -335,6 +336,7 @@ Bu ekran görüntüsü yüklemeye uygun mu? Uygulamayı Paylaş Yakındaki yerler getirilirken hata oluştu. + Yakındaki anıtlar getirilirken hata oluştu. Son arama yok Arama geçmişinizi silmek istediğinizden emin misiniz? Bu aramayı silmek istiyor musunuz? @@ -618,4 +620,18 @@ Konum seçicinin resim görünümünün gölgesi Görüntü Konumu Konumun doğru olup olmadığını kontrol edin + Özel Seçici + Resim Yok + Yapıldı + Geri + Özel Resim Seçici\'ye Hoş Geldiniz + Bu seçici, halihazırda Commons\'ta bulunan farklı resimleri gösterir. + Soldaki resimden farklı olarak, sağdaki resimde zaten yüklendiğini belirten Commons logosu var. + Harika + Bu resim zaten Commons\'a yüklendi. + Viki, Anıtları Seviyor kampanyasına katkıda bulunuyorsunuz. İlgili şablonlar buna göre eklenecektir. + Anıtları göster + Viki, Anıtları Seviyor ayı! + DAHA FAZLA BİLGİ EDİN + Viki, Anıtları Seviyor; Wikimedia tarafından düzenlenen anıtlar için uluslararası bir fotoğraf yarışmasıdır. diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 6c0e0c439..86be4b4b4 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -19,6 +19,7 @@ * Shizhao * Shuiwater * Si109 +* SkyEye FAST * SomeyaMako * Vikarna * VulpesVulpes825 @@ -629,4 +630,8 @@ 地点拾取器的照片观测视野的阴影。 图像位置 检查位置是否正确 + 无图片 + 完成 + 返回 + 了解更多 From 6d073d25a36d05f86cd6fd0a5a5920004d0da3c8 Mon Sep 17 00:00:00 2001 From: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Date: Thu, 19 Aug 2021 18:26:45 +0530 Subject: [PATCH 0004/1165] [GSoC] Update Dialog and Fix failing tests (#4564) * Update Dialog and Fix failing tests * Dialog Fix --- app/src/main/res/values/strings.xml | 2 +- .../contributions/ContributionsListFragmentUnitTests.kt | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b594d182d..599a2a294 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -646,7 +646,7 @@ Upload your first media by tapping on the add button. Back Welcome to Custom Picture Selector This picker shows differently pictures that are already to Commons. - Unlike the picture on the left, the picture on the right has the Commons logo indicating it is already uploaded. + Unlike the picture on the left, the picture on the right has the Commons logo indicating it is already uploaded. \n Touch and hold for image preview. Awesome This image has already been uploaded to Commons. WLM 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 b0c7734f8..7230633f0 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 @@ -87,6 +87,9 @@ class ContributionsListFragmentUnitTests { @Mock private lateinit var fabGallery: FloatingActionButton + @Mock + private lateinit var fabCustomGallery: FloatingActionButton + @Mock private lateinit var newConfig: Configuration @@ -119,6 +122,7 @@ class ContributionsListFragmentUnitTests { Whitebox.setInternalState(fragment, "fabPlus", fabPlus) Whitebox.setInternalState(fragment, "fabCamera", fabCamera) Whitebox.setInternalState(fragment, "fabGallery", fabGallery) + Whitebox.setInternalState(fragment, "fabCustomGallery", fabCustomGallery) Whitebox.setInternalState(fragment, "fab_layout", fabLayout) Whitebox.setInternalState( fragment, From a1ed4142bbff334eda746e79ac382013028bf9e5 Mon Sep 17 00:00:00 2001 From: Ashish Date: Fri, 20 Aug 2021 06:05:41 +0530 Subject: [PATCH 0005/1165] Bug Fix in uploads - null checks for Wikidata Place (#4567) Fixes #4565 --- .../java/fr/free/nrw/commons/db/AppDatabase.kt | 2 +- .../nrw/commons/upload/PageContentsCreator.java | 14 +++++++++----- .../free/nrw/commons/upload/worker/UploadWorker.kt | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) 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 7f6ea7027..1085865df 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 @@ -14,7 +14,7 @@ import fr.free.nrw.commons.upload.depicts.DepictsDao * The database for accessing the respective DAOs * */ -@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class], version = 8, exportSchema = false) +@Database(entities = [Contribution::class, Depicts::class, UploadedStatus::class], version = 9, exportSchema = false) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun contributionDao(): ContributionDao diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.java b/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.java index 60b4a02c8..dc26b939d 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.java @@ -26,24 +26,28 @@ class PageContentsCreator { private final Context context; @Inject - public PageContentsCreator(Context context) { + public PageContentsCreator(final Context context) { this.context = context; } - public String createFrom(Contribution contribution, String countryCode) { + public String createFrom(final Contribution contribution, final String countryCode) { StringBuilder buffer = new StringBuilder(); final Media media = contribution.getMedia(); buffer .append("== {{int:filedesc}} ==\n") .append("{{Information\n") - .append("|description=").append(media.getFallbackDescription()) - .append("{{ on Wikidata|").append(contribution.getWikidataPlace().getId()).append("}}") + .append("|description=").append(media.getFallbackDescription()).append("\n"); + if (contribution.getWikidataPlace() != null) { + buffer.append("{{ on Wikidata|").append(contribution.getWikidataPlace().getId()) + .append("}}"); + } + buffer .append("\n") .append("|source=").append("{{own}}\n") .append("|author=[[User:").append(media.getAuthor()).append("|") .append(media.getAuthor()).append("]]\n"); - String templatizedCreatedDate = getTemplatizedCreatedDate( + final String templatizedCreatedDate = getTemplatizedCreatedDate( contribution.getDateCreated(), contribution.getDateCreatedSource()); if (!StringUtils.isBlank(templatizedCreatedDate)) { buffer.append("|date=").append(templatizedCreatedDate); 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 d46c5e4ac..cec598eaa 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 @@ -290,7 +290,7 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : //Upload the file from stash var countryCode: String? =null with(contribution.wikidataPlace?.location){ - if(contribution.wikidataPlace?.isMonumentUpload!!) { + if (contribution.wikidataPlace?.isMonumentUpload == true) { countryCode = reverseGeoCode(contribution.wikidataPlace?.location!!)?.toLowerCase() } From b977571bd6b5c51855693d0f40ef0ef6094a3b64 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 23 Aug 2021 13:01:39 +0200 Subject: [PATCH 0006/1165] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-da/strings.xml | 1 + app/src/main/res/values-de/strings.xml | 10 ++++++++++ app/src/main/res/values-fa/strings.xml | 2 +- app/src/main/res/values-fi/strings.xml | 9 +++++++++ app/src/main/res/values-fr/strings.xml | 8 ++++---- app/src/main/res/values-it/strings.xml | 6 ++++++ app/src/main/res/values-iw/strings.xml | 12 ++++++++++++ app/src/main/res/values-mk/strings.xml | 16 ++++++++++++++++ app/src/main/res/values-mnw/strings.xml | 2 +- app/src/main/res/values-pms/strings.xml | 15 +++++++++++++++ app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values-sk/strings.xml | 14 ++++++++++++++ app/src/main/res/values-sr/strings.xml | 4 ++++ app/src/main/res/values-tcy/strings.xml | 7 ++++--- app/src/main/res/values-tr/strings.xml | 2 +- app/src/main/res/values-uk/strings.xml | 18 +++++++++++++++++- app/src/main/res/values-zh-rTW/strings.xml | 16 ++++++++++++++++ 17 files changed, 132 insertions(+), 11 deletions(-) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 286907bc5..d07fd46ce 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -362,4 +362,5 @@ Begrænset forbindelsestilstand Kvalitetsbilleder Læs mere + Tilbage diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 7ce939834..f8df6b99b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -124,6 +124,7 @@ Einstellungen Registrieren Vorgestellte Bilder + Benutzerdefinierte Auswahl Kategorie Peer Review Über @@ -618,6 +619,15 @@ Der Schatten der Bildansicht der Ortsauswahl Bildstandort Überprüfe, ob der Standort korrekt ist + Benutzerdefinierte Auswahl + Keine Bilder + Fertig + Zurück + Willkommen bei der benutzerdefinierten Auswahl + Diese Auswahl zeigt verschiedene Bilder, die bereits zu Commons gehören. + Im Gegensatz zum Bild auf der linken Seite ist das Bild auf der rechten Seite mit dem Commons-Logo versehen, das anzeigt, dass es bereits hochgeladen wurde. + Großartig + Dieses Bild ist bereits auf Commons hochgeladen worden. Du beiligst dich an der Wiki Loves Monuments-Kampagne. Zugehörige Vorlagen werden entsprechend hinzugefügt. Denkmäler anzeigen Es ist der Wiki Loves Monuments Monat! diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 94d5735df..5e42ce917 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -462,7 +462,7 @@ انجام شد بازگشت به انتخاب‌گر سفارشی تصویر خوش آمدید - برخلاف تصویر سمت چپ، تصویر سمت راست دارای نشان‌وارهٔ ویکی‌انبار است که نشان می‌دهد از پیش بارگذاری شده‌است. + برخلاف تصویر سمت چپ، تصویر سمت راست دارای نشان‌وارهٔ ویکی‌انبار است که نشان می‌دهد از پیش بارگذاری شده‌است. عالی این تصویر از پیش در ویکی‌انبار بارگذاری شده‌است. شما در حال مشارکت در پویش ویکی دوست‌دار یادمان‌ها هستید. الگوهای مرتبط متعاقباً افزوده خواهند شد. diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 32de41a1c..b47db3dba 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -382,6 +382,8 @@ Kuvat, jotka eivät ole luokissa, ovat harvoin käyttökelpoisia. Haluatko varmasti jatkaa valitsematta luokkia? Kuvauksia ei valittu Kuvat, joissa on kuvatekstejä, löytyvät helpommin ja todennäköisemmin niitä käytetään. Haluatko varmasti jatkaa valitsematta kuvatekstejä? + Peruuta tallennus + Jatka tallennusta (Kaikille sarjan kuville) Hae tältä alueelta Lupapyyntö @@ -482,6 +484,8 @@ Ei voitu lisätä luokkia. Päivitetään luokkia Onnistui + Koordinaatit %1$s on lisätty. + Koordinaatteja ei voitu lisätä. Jaa kuva Et ole vielä lahjoittanut mitään Käyttäjätunnus luotu! @@ -562,4 +566,9 @@ Muokkaa sijaintia Kuvan sijainti Tarkista, onko sijainti oikea + Ei kuvia + Valmis + Takaisin + Mahtava + LUE LISÄÄ diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a36d2a6fe..13c6e8e1d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -628,12 +628,12 @@ Emplacement de l’image Vérifier si l’emplacement est correct Sélecteur actuel - Pas d’images + Pas d’image Fait Retour - Bienvenue au sélecteur d’images personnalisé - Ce sélecteur affiche différemment des images qui sont déjà dans Communs. - À l’inverse de l’image sur la gauche, l’image de droite a le logo Communs, indiquant qu’elle a déjà été téléchargée. + Bienvenue dans le sélecteur d’images personnalisé + Ce sélecteur affiche différemment les images qui sont déjà dans Commons. + À l’inverse de l’image sur la gauche, l’image de droite a le logo Commons, indiquant qu’elle a déjà été téléversée. Génial Cette image a déjà été téléchargée sur Communs. Vous contribuez à la campagne de monuments aimés de Wiki. Les modèles associés seront ajoutés en accord avec cela. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 87ed689b7..ed9cd4cc9 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -9,6 +9,7 @@ * Lorelai87 * Lorem Ipsum * Nemo bis +* Noretta93 * S4b1nuz E.656 * Sarah Bernabei * Senpremì @@ -513,4 +514,9 @@ Scegli una posizione Seleziona una posizione Modifica posizione + Nessuna immagine + Fatto + Indietro + Fantastico + ULTERIORI INFORMAZIONI diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 617501071..823c4af1c 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -135,6 +135,7 @@ הגדרות רישום תמונות מומלצות + בורר מותאם אישית קטגוריה ביקורת עמיתים אודות @@ -345,6 +346,7 @@ האם בסדר להעלות את צילום המסך הזה? שיתוף היישום שגיאה באחזור המקומות בסביבתך. + שגיאה באחזור אתרים בסביבתך. אין חיפושים אחרונים האם באמת ברצונך לנקות את היסטוריית החיפוש שלך? האם ברצונך למחוק חיפוש זה? @@ -630,4 +632,14 @@ הצל של תצוגת תמונה של בורר המיקום מיקום תמונה לבדוק האם המיקום נכון + בורר מותאם אישית + אין תמונות + בוצע + חזרה + מגניב + התמונה הזאת כבר הועלתה לוויקישיתוף. + הצגת אתרי מורשת + זהו חודש ויקי אוהבת אתרי מורשת! + מידע נוסף + ויקי אוהבת אתרי מורשת היא תחרות בין־לאומית לצילום אתרי מורשת שמאורגנת על־ידי ויקימדיה diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 6860c4fa1..17ad7b277 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -105,6 +105,7 @@ Нагодувања Зачленување Избрани слики + Прилагоден избирач Категорија Оценување За извршникот @@ -315,6 +316,7 @@ Дали оваа екранска снимка е во ред за подигање? Сподели прилог Грешка при добивањето на околните места. + Грешка при добивањето на околните споменици. Нема скорешни пребарувања Дали сте сигурни дека сакате да ја избришете историјата на пребарувања? Дали би сакале да го избришете ова пребарување? @@ -598,4 +600,18 @@ Сенка на сликата за избирање местоположба Местоположба на сликата Проверете дали местоположбата е точна + Прилагоден избирач + Нема слики + Готово + Назад + Добре дојдовте на Прилагодениот избирач + Овој избирач ги покажува поинаку сликите кои веќе се на Ризницата. + За разлика од сликата лево, таа десно има лого на Ризницата, што значи дека веќе е подигната. + Одлично + Оваа слика е веќе подигната на Ризницата. + Учествувате во походот „Вики ги сака спомениците“. Поврзаните предлошки ќе бидат соодветно додадени. + Прикажи споменици + Ова е месец на „Вики ги сака спомениците“! + ДОЗНАЈТЕ ПОВЕЌЕ + „Вики ги сака спомениците“ е меѓународен фотографски натпревар за споменици приреден од Викимедија diff --git a/app/src/main/res/values-mnw/strings.xml b/app/src/main/res/values-mnw/strings.xml index ebd9e2656..28d8f1ae1 100644 --- a/app/src/main/res/values-mnw/strings.xml +++ b/app/src/main/res/values-mnw/strings.xml @@ -196,7 +196,7 @@ ဗီုရုပ်ဏအ် ဒးဒုၚ်ရုဲပတိုန်သမ္တီလတ် သ္ဂောအ် ပလီုရ. သလာ် လုပ်လံက်အေန် - ဝဳကဳဒေတ + ဝဳကဳဒါတာ ဝဳကဳပဳဒဳယာ COMMONS <u>FAQ</u> diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 408f58933..7e3b22c57 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -314,6 +314,7 @@ Costa schermà a va bin da carié? Partagé j\'aplicassion Eror durant l\'esplorassion dj\'anviron. + Eror d\'arcuperassion dij monument davzin. Gnun-e arserche recente É-lo sigur ëd vorèj dëscancelé lë stòrich ëd soe arserche? Veul-lo dëscancelé costa arserca? @@ -597,4 +598,18 @@ L\'ombra dla vista dla plancia dël selessionator ëd pòst Pòst ëd la plancia Controlé si ël pòst a l\'é giust + Seletor përsonalisà + Gnun-e plance + Fàit + André + Bin-ëvnù ant ël seletor ëd plance përsonalisà + Ës seletor a smon ëd fasson diferenta dle plance ch\'a son già an Comun + A diferensa da la plancia a snistra, la plancia a drita a la marca Comun ch\'a mostra ch\'a l\'é già stàita carià. + Fiamengh + La plancia a l\'é già stàita carià su Comun. + Chiel a contribuiss a la campagna ëd Wiki a-j veul bin ai monument. Jë stamp associà a saran giontà an acòrd con sòn. + Smon-e ij monument + A l\'é ël mèis ëd Wiki a-j veul bin ai monument! + SAVÈJNE ËD PI + Wiki a-j veul bin ai monument a l\'é un concors antërnassional ëd fotografìa ëd monument organisà da Wikimedia diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 9ba02bca8..256b235cd 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -641,4 +641,5 @@ Изменить местоположение Расположение изображения Проверьте правильность местоположения + Назад diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index d14b79eda..24ad745b4 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -124,6 +124,7 @@ Nastavenia Zaregistrovať sa Najlepšie obrázky + Vlastný výber Kategória Kontrola O aplikácii @@ -334,6 +335,7 @@ Je v poriadku nahrať túto snímku obrazovky? Zdieľať aplikáciu Nastala chyba pri hľadaní miest v okolí. + Nastala chyba pri načítavaní miest v okolí. Žiadne nedávne hľadania Naozaj chcete odstrániť históriu vášho vyhľadávania? Chcete odstrániť toto hľadanie? @@ -619,4 +621,16 @@ Tieň obrázka pre výber polohy Poloha obrázka Skontrolovať či je poloha správna + Vlastný výber + Žiadne obrázky + Hotovo + Späť + Vitajte vo vlastnom výbere obrázkov + Úžasné + Tento obrázok už bol do Commons nahraný. + Prispievate do kampane Wiki miluje monumenty. Súvisiace šablóny budú podľa toho pridané. + Zobraziť monumenty + Je mesiac Wiki miluje monumenty! + ZISTIŤ VIAC + Wkki miluje monumenty je medzinárodná súťaž fotografií monumentov organizovaná Wikemédiou diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 84990944e..79cc83802 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -451,4 +451,8 @@ Годишње Недељно У близини + Нема слика + Готово + Назад + Ова слика је већ отпремљена на Оставу. diff --git a/app/src/main/res/values-tcy/strings.xml b/app/src/main/res/values-tcy/strings.xml index 68f34b750..cd0316468 100644 --- a/app/src/main/res/values-tcy/strings.xml +++ b/app/src/main/res/values-tcy/strings.xml @@ -140,7 +140,7 @@ ಇಂಟರ್ನೆಟ್ನ್‌ಡ್ತ್ ಇರ್ ತೊಜಾತ್ ಬರ್ಪಿನ ಕೃತಿಸ್ವಾಮ್ಯತ ವಸ್ತುಲೆನ್ ಅಂಚನೆ ಪೋಸ್ಟರ್ ಚಿತ್ರೊಲು, ಬೂಕುಲೆನ ವರ್ಗೊ ಇತ್ಯಾದಿ ತಪ್ಪಾತ್ ಇಪ್ಪು. ಇರೆಗ್ ತಿಕ್ಕ್‌ಂಡ್ಂದು ಇರ್ ಭಾವಿಸುವರಾ? ಅಂದ್! - ಹೆಚ್ಚಿನ ಮಾಹಿತಿಲು + ಜಾಸ್ತಿ ಮಾಹಿತಿ ವರ್ಗೊಲು ದಿಂಜಾವೊಂದುಂಡು…… ಒಂಜಿಲಾ ಅಯ್ಕೆ ಮಾಲ್ತಿಜ್ಜಿ. @@ -195,7 +195,7 @@ ಒವೆ ವಿವರಣೆ ಇಜ್ಜಿ. ಕಾಮನ್ಸ್ ಫೈಲ್ ಪುಟೊ ವಿಕಿಡಾಟ ಐಟಂ - ವಿಕಿಪೀಡಿಯ ಲೇಖನೊ + ವಿಕಿಪೀಡಿಯ ಲೇಕನೊ ದಯಮಾಲ್ತ್ ಸಾಧ್ಯವಾಯಿನ ಮಾಧ್ಯಮಟ್ ವಿವರಣೆ ಕೊರ್ಲೆ: ಒಲ್ಪಾ ಅಯಿನ್ ದೆಪ್ಪೊಲಿ? ಉಂದೆನ್ ಎಂಚ ತೋಜಾವೊಲಿ? ಸನ್ನಿವೇಶ ಪಂಡದಾದ? ದಯಮಾಲ್ತ್ ವಸ್ತುಲು ಅತ್ತಂದೆ ವ್ಯಕ್ತಿಲು ಸುಲಭವಾತ್ ಊಹಿಸಲು ಸಾದ್ಯ ಇಜ್ಜಿ ಮಾಹಿತಿನ್ ಬಹಿರಂಗ ಮಾಲ್ಪುಲೆ, ಉದಾಹರಣೆಗ್ ಉಂದು ಭೂದೃಶ್ಯವಾತ್ ಇತ್ತಂಡ್ ದಿನೊತ ಸಮಯ. ಮಾಧ್ಯಮೊ ಅಸಾಮಾನ್ಯವಾತ್ ಇಪ್ಪುನಯಿನ್ ತೋಜಾತ್, ಅವು ಅಸಾಮಾನ್ಯವಾತ್ ವಿವರಣೆ ಇಪ್ಪು. ಈ ಆಕೃತಿದ ಸಂಭಾವ್ಯ ಸಮಸ್ಯೆಲು: ಆಕೃತಿ ಮಸ್ತ್ ಕಪ್ಪಾದುಂಡು. @@ -337,8 +337,9 @@ ಸಂಯೋಜನೆಲು ಸೂಚನೆಲು ನನಾತ್ - ಬಳಕೆದಾರೆರ್ + ಬಳಕೆದಾರೆ ಕೈತಲ್‍ದ ನನಾತ್ ಓದುಲೇ ಮಾಂತಾ ಬಾಸೆಲೆಡ್ + ಆಂಡ್ diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index fc550f043..8f1139ec5 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -626,7 +626,7 @@ Geri Özel Resim Seçici\'ye Hoş Geldiniz Bu seçici, halihazırda Commons\'ta bulunan farklı resimleri gösterir. - Soldaki resimden farklı olarak, sağdaki resimde zaten yüklendiğini belirten Commons logosu var. + Soldaki resimden farklı olarak, sağdaki resimde zaten yüklendiğini belirten Commons logosu var. Harika Bu resim zaten Commons\'a yüklendi. Viki, Anıtları Seviyor kampanyasına katkıda bulunuyorsunuz. İlgili şablonlar buna göre eklenecektir. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 8368f5630..1b1c9d83b 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -83,7 +83,7 @@ Завантаження додано до черги (увімкнений режим обмеженого з\'єднання) Завантажено %1$s! Торкніться, щоб переглянути Ваше завантаження - Вивантаження файлу: %s + Завантаження файлу: %s %1$s завантажується Завершення завантаження %1$s Не вдалося завантажити %1$s @@ -129,6 +129,7 @@ Налаштування Зареєструватися Вибрані зображення + Користувацький селектор Категорія Перевірка колегами Про програму @@ -339,6 +340,7 @@ Чи можна завантажувати цей знімок? Поширити програму Помилка отримання місць поблизу. + Помилка отримання пам\'ятників поблизу. Історія пошуку порожня Ви впевнені, що хочете стерти історію пошукових запитів? Вилучити цей пошук? @@ -624,4 +626,18 @@ Вигляд вибору розташування як тіні зображення Підпис зображення Перевірте правильність розташування + Користувацький селектор + Без зображень + Готово + Назад + Ласкаво просимо до користувацького вибору зображень + Цей інструмент вибору по-різному показує зображення, які вже є у спільності. + На відміну від зображення зліва, зображення праворуч має логотип Commons, яке вказує, що воно вже завантажене. + Чудово + Це зображення вже завантажено на Спільний ресурс. + Ви вносите свій внесок у кампанію «Вікі любить пам’ятники». Відповідні шаблони будуть додані відповідно. + Показати пам’ятники + Цей місяць Wiki Loves Monuments! + ДОСЛІДИТИ БІЛЬШЕ + Wiki Loves Monuments — це міжнародний фотоконкурс пам’яток, організований Вікімедією diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index fb0e6fd91..e20736184 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -117,6 +117,7 @@ 設定 註冊 特色圖片 + 自訂選擇器 分類 同行評審 關於 @@ -327,6 +328,7 @@ 這張截圖可以上傳嗎? 分享應用程式 索取附近地點時出錯。 + 索取附近古蹟景點時出錯。 無近期搜尋 確定要清除搜尋歷史嗎? 您要刪除此搜尋嗎? @@ -610,4 +612,18 @@ 位置點選器的圖片檢視陰影 圖片位置 檢查位置是否正確 + 自訂選擇器 + 沒有圖片 + 完成 + 返回 + 歡迎來到自訂圖片選擇器 + 此點選器顯示已在維基共享資源上的各種不同圖片。 + 右邊的圖片與與左邊圖片不同,它有代表已經上傳到維基共享資源的標誌。 + 棒極了 + 此圖片已上傳到維基共享資源。 + 您正在為維基愛古蹟活動做出貢獻。相關模板將會相應地添加上。 + 顯示古蹟景點 + 維基愛古蹟的月份! + 了解更多 + 維基愛古蹟是由維基媒體舉行的國際古蹟攝影比賽 From 979b4f36d9df03f7d063e805c32c2bbdd4926070 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 26 Aug 2021 13:02:22 +0200 Subject: [PATCH 0007/1165] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ban/strings.xml | 3 ++- app/src/main/res/values-da/strings.xml | 1 + app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-hi/strings.xml | 2 ++ app/src/main/res/values-iw/strings.xml | 4 ++++ app/src/main/res/values-sk/strings.xml | 2 ++ 6 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-ban/strings.xml b/app/src/main/res/values-ban/strings.xml index f8e53c1f0..e451e6015 100644 --- a/app/src/main/res/values-ban/strings.xml +++ b/app/src/main/res/values-ban/strings.xml @@ -96,7 +96,7 @@ Kategori Ulasan Peer Indik - Kardi <a href=\"%1$s\">isu GitHub</a> anyar antuk parihindik miwah panikayan kakutu. + Kardi <a href=\"%1$s\">isu GitHub</a> anyar antuk parikanda miwah panikayan kakutu. Parikrama paragaan Krédit Indik @@ -151,6 +151,7 @@ Lisénsi Koordinat Kode O2F + Napiké ragané yakti jagi medal log? Logo Commons Situs Commons Kaca Facebook Commons diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index d07fd46ce..e130c1034 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -363,4 +363,5 @@ Kvalitetsbilleder Læs mere Tilbage + LÆR MERE diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f8df6b99b..89b51d6ef 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -625,7 +625,7 @@ Zurück Willkommen bei der benutzerdefinierten Auswahl Diese Auswahl zeigt verschiedene Bilder, die bereits zu Commons gehören. - Im Gegensatz zum Bild auf der linken Seite ist das Bild auf der rechten Seite mit dem Commons-Logo versehen, das anzeigt, dass es bereits hochgeladen wurde. + Im Gegensatz zum Bild auf der linken Seite ist das Bild auf der rechten Seite mit dem Commons-Logo versehen, das anzeigt, dass es bereits hochgeladen wurde. Für die Bildvorschau berühren und halten. Großartig Dieses Bild ist bereits auf Commons hochgeladen worden. Du beiligst dich an der Wiki Loves Monuments-Kampagne. Zugehörige Vorlagen werden entsprechend hinzugefügt. diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index c4f070b14..9228ade5b 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -82,6 +82,7 @@ साझा करें शीर्षक (आवश्यक) विवरण + शीर्षक प्रवेश नहीं हो रहा - नेटवर्क विफल ढेर सारे असफल प्रयास होने के कारण कुछ मिनटों के बाद प्रयास करें। क्षमा करें, यह सदस्य कॉमन्स में अवरोधित है @@ -280,6 +281,7 @@ क्या आप सचमुच अपना खोज इतिहास मिटाना चाहते हैं? खोज इतिहास मिट गया हटाने के लिये नामांकन करें + हटाएँ उपलब्धियाँ सांख्यिकी धन्यवाद प्राप्त किया diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 823c4af1c..fd94b1ec0 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -636,8 +636,12 @@ אין תמונות בוצע חזרה + ברוך בואך לבוחר התמונות המותאמות + הבוחר מציג תמונות שונות מאלו שכבר בוויקישיתוף. + בניגוד לתמונה מימין, על התמונה שמשמאל יש את הלוגו של ויקישיתוף שמעיד שהיא כבר הועלתה. יש לגעת ולהחזיר לקבלת תצוגה מקדימה של התמונה. מגניב התמונה הזאת כבר הועלתה לוויקישיתוף. + זאת תהיה תרומה למבצע ויקי אוהבת אתרי מורשת. תוספנה תבניות מתאימות. הצגת אתרי מורשת זהו חודש ויקי אוהבת אתרי מורשת! מידע נוסף diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 24ad745b4..5a5752c94 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -626,6 +626,8 @@ Hotovo Späť Vitajte vo vlastnom výbere obrázkov + Tento výber zobrazuje iné obrázky, ktoré už sú na Commons. + Na rozdiel od obrázka v ľavo, obrázkov v pravo má logo Commons, ktoré indikuje, že už je nahraný. Podržte kurzor nad obrázkom pre náhľad. Úžasné Tento obrázok už bol do Commons nahraný. Prispievate do kampane Wiki miluje monumenty. Súvisiace šablóny budú podľa toho pridané. From f902270fb4c2d212406daad4d3aacb49d51c8548 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 30 Aug 2021 13:02:17 +0200 Subject: [PATCH 0008/1165] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values-lv/strings.xml | 1 + app/src/main/res/values-nqo/strings.xml | 3 ++ app/src/main/res/values-pt-rBR/strings.xml | 11 ++++++- app/src/main/res/values-uz/strings.xml | 36 ++++++++++++++++++++-- 5 files changed, 48 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 13c6e8e1d..7bed72304 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -633,7 +633,7 @@ Retour Bienvenue dans le sélecteur d’images personnalisé Ce sélecteur affiche différemment les images qui sont déjà dans Commons. - À l’inverse de l’image sur la gauche, l’image de droite a le logo Commons, indiquant qu’elle a déjà été téléversée. + À l’inverse de l’image sur la gauche, l’image de droite a le logo Commons, indiquant qu’elle a déjà été téléversée.\n Toucher et maintenir pour avoir un aperçu de l\'image. Génial Cette image a déjà été téléchargée sur Communs. Vous contribuez à la campagne de monuments aimés de Wiki. Les modèles associés seront ajoutés en accord avec cela. diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 3abcb5f44..eb3092cd4 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -184,4 +184,5 @@ Dalībnieks Skaits Tuvumā + Atpakaļ diff --git a/app/src/main/res/values-nqo/strings.xml b/app/src/main/res/values-nqo/strings.xml index fd4c10a98..260debf50 100644 --- a/app/src/main/res/values-nqo/strings.xml +++ b/app/src/main/res/values-nqo/strings.xml @@ -413,4 +413,7 @@ ߖߌ߬ߦߊ߬ߓߍ ߛߎ߯ߦߊ ߟߊ߬ߦߟߍ߬ߟߌ ߟߊߟߐ߭ ߦߴߌ ߘߐ߫... ߡߋߘߌߦߊ ߝߊߙߊ߲ߝߊ߯ߛߌ + ߖߌ߬ߦߊ߬ߓߍ߫ ߕߴߦߋ߲߬ + ߌ ߞߐߛߊ߬ߦߌ߬ + ߖߌ߬ߦߊ߬ߓߍ ߣߌ߲߬ ߓߘߊ߫ ߓߊ߲߫ ߠߊߦߟߍ߬ ߟߊ߫ ߞߐߡߐ߲ߛ ߞߣߐ߫ ߞߘߐ߬ߡߊ߲߫ diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 98712a46e..b66befe04 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -119,6 +119,7 @@ Configurações Criar conta Imagens destacadas + Seletor personalizado Categoria Revisão por pares Sobre @@ -329,6 +330,7 @@ Esta captura de tela pode ser carregada? Compartilhar o aplicativo Erro ao buscar lugares próximos. + Erro ao buscar monumentos próximos. Nenhuma pesquisa recente Tem certeza de que deseja apagar o histórico de pesquisa? Deseja eliminar esta pesquisa? @@ -582,7 +584,7 @@ &#169; <a href=\"https://www.mapbox.com/about/maps/\">Mapbox</a> &#169; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> <a href=\"https://www.mapbox.com/map-feedback/\">Melhorar o mapa</a> Modo de conexão limitada habilitado! Modo de conexão limitada desativado. Os carregamentos pendentes serão retomados agora. - Modo de Conexão Limitada + Modo de conexão limitada Imagens de qualidade Imagens de qualidade são diagramas ou fotografias que atendem a certos padrões de qualidade (que são principalmente de natureza técnica) e são valiosos para projetos da Wikimedia Retomando carregamento… @@ -608,4 +610,11 @@ Editar localização Localização da imagem Verifique se a localização está correta + Seletor personalizado + Sem imagens + Feito + Voltar + Esta imagem já foi enviada para Commons. + Monumentos de exibição + SABER MAIS diff --git a/app/src/main/res/values-uz/strings.xml b/app/src/main/res/values-uz/strings.xml index 3e169ff96..db256b732 100644 --- a/app/src/main/res/values-uz/strings.xml +++ b/app/src/main/res/values-uz/strings.xml @@ -4,27 +4,56 @@ * Zafar Shamsiddinov --> + + (%1$d) + (%1$d) + Yuklashni boshlash %d yuklandi %d yuklandi + + %d yuklandi + %d yuklandi + + + Ushbu rasm %1$s litsenziyasi asosidadir + Ushbu rasmlar %1$s litsenziyasi asosidadir + + + %1$d yuklandi + %1$d yuklandi + + + Umumiy tarkibni qabul qilish. Rasmning oʻlchamiga va qurilmangizga qarab tasvirni qayta ishlash biroz vaqt talab qilishi mumkin + Umumiy tarkibni qabul qilish. Rasmlarning oʻlchamiga va qurilmangizga qarab tasvirni qayta ishlash biroz vaqt talab qilishi mumkin + + Kezish + Koʻrinishi + Asosiy + Fikr-mulohazalar Konfidensiallik Vikiombor - Sozlamalar + Tanlovlar Vikiomborga yuklash Foydalanuvchi nomi Parol - Vikiombor Beta hisobga kirish + Vikiombor Beta hisobiga kirish Kirish Parolni unutdingizmi? Roʻyxatdan oʻtish Kirish Iltimos kuting... - Kirish muvaffaqiyatli! + Kirish muvaffaqiyatli bajarildi! Kirish muvaffaqiyatsiz yakunlandi! Fayl topilmadi. Iltimos, boshqa faylni izlab koʻring. + Kirib boʻlmadi. Qaytadan urinib koʻring Yuklash boshlandi! + %1$s yuklanmoqda + %1$s yuklab boʻlindi + %1$sʼni yuklab boʻlmadi + %1$sʼni yuklash toʻxtatildi Razm solish uchun bosing Razm solish uchun bosing Soʻnggi yuklanmalarim @@ -54,6 +83,7 @@ Vikiombor (Wikimedia Commons) ilovasi — bu Vikipediya hamjamiyati koʻngillilari tomonidan yaratilgan hamda qoʻllab-quvvatlanadigan ochiq manbali ilova. Vikimedia Jamgʻarmasi ushbu ilovani yaratish, ishlab chiqish yoki unga xizmat koʻrsatishda ishtirok etmaydi. Xatolar haqida hisobot va takliflar uchun yangi <a href=\"%1$s\">GitHub issue</a> yarating. Maxfiylik siyosati + Mualliflar Haqida Fikr-mulohazalar yuborish (email orqali) Siz hali hech qanaqa rasm joylamagansiz. From 2100701be7bd6ae192414d0759f79c5c919d8d06 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 2 Sep 2021 13:01:43 +0200 Subject: [PATCH 0009/1165] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-sv/strings.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 420143bc7..7441d960c 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -110,6 +110,7 @@ Inställningar Registrera Utvalda bilder + Anpassad selektor Kategori Granskning Om @@ -320,6 +321,7 @@ Är det okej att ladda upp denna skärmdump? Dela app Fel uppstod när platser i närheten hämtades. + Fel uppstod när monument i närheten hämtades. Inga senaste sökningar Är du säker på att du vill rensa din sökhistorik? Vill du radera denna sökning? @@ -603,4 +605,18 @@ Skuggan för platsväljarens bildvisare Bildplats Kontrollera om platsen är korrekt + Anpassad selektor + Inga bilder + Klar + Tillbaka + Välkommen till anpassade bildselektorn + Denna väljare visar olika bilder som redan finns på Commons. + Till skillnad från bilden till vänster har bilden till höger Commons-logotypen, vilket innebär att den redan är uppladdad.\n Tryck och håll ned för att förhandsgranska bilden. + Häftigt + Denna bild har redan laddats upp till Commons. + Du bidrar till kampanjen Wiki Loves Monuments. Relaterade mallar kommer att läggas till. + Visa monument + Det är Wiki Loves Monuments-månaden! + LÄS MER + Wiki Loves Monuments är en internationell fototävling för monument som organiseras av Wikimedia From 7eab53afa83076686388e0e6daec5a882fbc8a72 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 6 Sep 2021 13:01:34 +0200 Subject: [PATCH 0010/1165] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ban/strings.xml | 4 ++++ app/src/main/res/values-fa/strings.xml | 3 ++- app/src/main/res/values-pt/strings.xml | 16 ++++++++++++++++ app/src/main/res/values-zh-rTW/strings.xml | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-ban/strings.xml b/app/src/main/res/values-ban/strings.xml index e451e6015..f7d17b5d0 100644 --- a/app/src/main/res/values-ban/strings.xml +++ b/app/src/main/res/values-ban/strings.xml @@ -103,6 +103,7 @@ Kirim umpan walik (liwat Rerepél) Kategori sané mangkin kaanggén Ngantosang sinkronisasi kapertama… + Ragné durung naénin ngunggah photo Indayang malih Wangdé Unduh @@ -146,10 +147,13 @@ Nénten Sesirah Murda + Déskripsi Pabligbagan + Pangardi Tanggal kaunggah Lisénsi Koordinat + Tan kaicain Kode O2F Napiké ragané yakti jagi medal log? Logo Commons diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 5e42ce917..8a3ce0d9d 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -5,6 +5,7 @@ * Amirsara * Arash.pt * BaRaN6161 TURK +* Darafsh * Ebraminio * Eshagh79 * FarsiNevis @@ -462,7 +463,7 @@ انجام شد بازگشت به انتخاب‌گر سفارشی تصویر خوش آمدید - برخلاف تصویر سمت چپ، تصویر سمت راست دارای نشان‌وارهٔ ویکی‌انبار است که نشان می‌دهد از پیش بارگذاری شده‌است. + برخلاف تصویر سمت چپ، تصویر سمت راست دارای نشان‌وارهٔ ویکی‌انبار است که نشان می‌دهد از پیش بارگذاری شده‌است.\nبرای مشاهدهٔ تصویر، صفحه را لمس کنید و نگه دارید. عالی این تصویر از پیش در ویکی‌انبار بارگذاری شده‌است. شما در حال مشارکت در پویش ویکی دوست‌دار یادمان‌ها هستید. الگوهای مرتبط متعاقباً افزوده خواهند شد. diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index b94fcb7bd..63a191a02 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -118,6 +118,7 @@ Configurações Registar-se Imagens destacadas + Seletor personalizado Categoria Revisão por pares Sobre @@ -328,6 +329,7 @@ Esta captura de ecrã pode ser carregada? Partilhar aplicação Erro ao procurar locais próximos. + Erro ao procurar monumentos próximos. Não há pesquisas recentes Tem a certeza de que deseja limpar o historial de pesquisas? Deseja eliminar esta pesquisa? @@ -611,4 +613,18 @@ A sombra da vista de imagem do selecionador de localização Localização da imagem Verificar se a localização está correta + Seletor personalizado + Não existem imagens + Concluído + Voltar + Bem-vindo ao seletor de imagens personalizado + Este seletor mostra de forma diferente imagens que já estão na wiki Commons. + Ao contrário da imagem à esquerda, a imagem à direita tem o logótipo da wiki Commons que indica que ela já foi carregada.\n Tocar e manter para uma antevisão da imagem. + Excecional + Esta imagem já foi carregada na wiki Commons. + Está a participar na campanha Wiki Loves Monuments. As predefinições associadas serão adicionadas. + Mostrar monumentos + É o mês da Wiki Loves Monuments! + SABER MAIS + Wiki Loves Monuments é um concurso internacional de fotografias de monumentos, organizado pela Wikimedia diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index e20736184..275307359 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -618,7 +618,7 @@ 返回 歡迎來到自訂圖片選擇器 此點選器顯示已在維基共享資源上的各種不同圖片。 - 右邊的圖片與與左邊圖片不同,它有代表已經上傳到維基共享資源的標誌。 + 右邊的圖片與與左邊圖片不同,它有代表已經上傳到維基共享資源的標誌。\n請輕壓圖片來預覽。 棒極了 此圖片已上傳到維基共享資源。 您正在為維基愛古蹟活動做出貢獻。相關模板將會相應地添加上。 From 4d2487dcc94c159c07c7da1c9e040d26d732f85c Mon Sep 17 00:00:00 2001 From: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Date: Tue, 7 Sep 2021 12:31:08 +0530 Subject: [PATCH 0011/1165] Add ContributionViewHolder Unit Tests (#4612) --- .../ContributionViewHolderUnitTests.kt | 327 ++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionViewHolderUnitTests.kt 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 new file mode 100644 index 000000000..72ad9c8a5 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionViewHolderUnitTests.kt @@ -0,0 +1,327 @@ +package fr.free.nrw.commons.contributions + +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 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 +import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.media.MediaClient +import fr.free.nrw.commons.profile.ProfileActivity +import io.reactivex.disposables.CompositeDisposable +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.`when` +import org.mockito.MockitoAnnotations +import org.powermock.reflect.Whitebox +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.Shadows +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 ContributionViewHolderUnitTests { + + private lateinit var contributionViewHolder: ContributionViewHolder + private lateinit var activity: ProfileActivity + private lateinit var parent: View + private lateinit var pauseResumeButton: ImageButton + private lateinit var addToWikipediaButton: ImageButton + private lateinit var cancelButton: ImageButton + private lateinit var retryButton: ImageButton + private lateinit var imageOptions: RelativeLayout + private lateinit var imageView: SimpleDraweeView + private lateinit var titleView: TextView + private lateinit var authorView: TextView + private lateinit var stateView: TextView + private lateinit var seqNumView: TextView + private lateinit var progressView: ProgressBar + + @Mock + private lateinit var callback: ContributionsListAdapter.Callback + + @Mock + private lateinit var mediaClient: MediaClient + + @Mock + private lateinit var uri: Uri + + @Mock + private lateinit var contribution: Contribution + + @Mock + private lateinit var compositeDisposable: CompositeDisposable + + @Mock + private lateinit var media: Media + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + SoLoader.setInTestMode() + Fresco.initialize(RuntimeEnvironment.application.applicationContext) + activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get() + parent = LayoutInflater.from(activity).inflate(R.layout.layout_contribution, null) + contributionViewHolder = ContributionViewHolder(parent, callback, mediaClient) + + pauseResumeButton = parent.findViewById(R.id.pauseResumeButton) + Whitebox.setInternalState(contributionViewHolder, "pauseResumeButton", pauseResumeButton) + + addToWikipediaButton = parent.findViewById(R.id.wikipediaButton) + Whitebox.setInternalState( + contributionViewHolder, + "addToWikipediaButton", + addToWikipediaButton + ) + + cancelButton = parent.findViewById(R.id.cancelButton) + Whitebox.setInternalState(contributionViewHolder, "cancelButton", cancelButton) + + retryButton = parent.findViewById(R.id.retryButton) + Whitebox.setInternalState(contributionViewHolder, "retryButton", retryButton) + + imageOptions = parent.findViewById(R.id.image_options) + Whitebox.setInternalState(contributionViewHolder, "imageOptions", imageOptions) + + imageView = parent.findViewById(R.id.contributionImage) + Whitebox.setInternalState(contributionViewHolder, "imageView", imageView) + + titleView = parent.findViewById(R.id.contributionTitle) + Whitebox.setInternalState(contributionViewHolder, "titleView", titleView) + + authorView = parent.findViewById(R.id.authorView) + Whitebox.setInternalState(contributionViewHolder, "authorView", authorView) + + stateView = parent.findViewById(R.id.contributionState) + Whitebox.setInternalState(contributionViewHolder, "stateView", stateView) + + seqNumView = parent.findViewById(R.id.contributionSequenceNumber) + Whitebox.setInternalState(contributionViewHolder, "seqNumView", seqNumView) + + progressView = parent.findViewById(R.id.contributionProgress) + Whitebox.setInternalState(contributionViewHolder, "progressView", progressView) + + + Whitebox.setInternalState( + contributionViewHolder, + "compositeDisposable", + compositeDisposable + ) + + } + + @Test + @Throws(Exception::class) + fun checkNotNull() { + Assert.assertNotNull(contributionViewHolder) + } + + @Test + @Throws(Exception::class) + fun testSetResume() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + val method: Method = ContributionViewHolder::class.java.getDeclaredMethod( + "setResume" + ) + method.isAccessible = true + method.invoke(contributionViewHolder) + } + + @Test + @Throws(Exception::class) + fun testSetPaused() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + val method: Method = ContributionViewHolder::class.java.getDeclaredMethod( + "setPaused" + ) + method.isAccessible = true + method.invoke(contributionViewHolder) + } + + @Test + @Throws(Exception::class) + fun testPause() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + val method: Method = ContributionViewHolder::class.java.getDeclaredMethod( + "pause" + ) + method.isAccessible = true + method.invoke(contributionViewHolder) + } + + @Test + @Throws(Exception::class) + fun testResume() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + val method: Method = ContributionViewHolder::class.java.getDeclaredMethod( + "resume" + ) + method.isAccessible = true + method.invoke(contributionViewHolder) + } + + @Test + @Throws(Exception::class) + fun testOnPauseResumeButtonClickedCaseTrue() { + contributionViewHolder.onPauseResumeButtonClicked() + } + + @Test + @Throws(Exception::class) + fun testOnPauseResumeButtonClickedCaseFalse() { + pauseResumeButton.tag = "" + contributionViewHolder.onPauseResumeButtonClicked() + } + + @Test + @Throws(Exception::class) + fun testWikipediaButtonClicked() { + contributionViewHolder.wikipediaButtonClicked() + } + + @Test + @Throws(Exception::class) + fun testImageClicked() { + contributionViewHolder.imageClicked() + } + + @Test + @Throws(Exception::class) + fun testDeleteUpload() { + contributionViewHolder.deleteUpload() + } + + @Test + @Throws(Exception::class) + fun testRetryUpload() { + contributionViewHolder.retryUpload() + } + + @Test + @Throws(Exception::class) + fun testChooseImageSource() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + val method: Method = ContributionViewHolder::class.java.getDeclaredMethod( + "chooseImageSource", + String::class.java, + Uri::class.java + ) + method.isAccessible = true + method.invoke(contributionViewHolder, "", uri) + } + + @Test + @Throws(Exception::class) + fun testDisplayWikipediaButton() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + val method: Method = ContributionViewHolder::class.java.getDeclaredMethod( + "displayWikipediaButton", + Boolean::class.javaObjectType + ) + method.isAccessible = true + method.invoke(contributionViewHolder, false) + } + + @Test + @Throws(Exception::class) + fun testCheckIfMediaExistsOnWikipediaPageCaseNull() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + `when`(contribution.wikidataPlace).thenReturn(null) + val method: Method = ContributionViewHolder::class.java.getDeclaredMethod( + "checkIfMediaExistsOnWikipediaPage", + Contribution::class.java + ) + method.isAccessible = true + method.invoke(contributionViewHolder, contribution) + } + + @Test + @Throws(Exception::class) + fun testInitCaseNull() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + contributionViewHolder.init(0, null) + } + + @Test + @Throws(Exception::class) + fun testInitCaseNonNull_STATE_COMPLETED() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + `when`(contribution.state).thenReturn(Contribution.STATE_COMPLETED) + `when`(contribution.media).thenReturn(media) + `when`(media.mostRelevantCaption).thenReturn("") + `when`(media.author).thenReturn("") + contributionViewHolder.init(0, contribution) + } + + @Test + @Throws(Exception::class) + fun testInitCaseNonNull_STATE_QUEUED() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + `when`(contribution.state).thenReturn(Contribution.STATE_QUEUED) + `when`(contribution.media).thenReturn(media) + `when`(media.mostRelevantCaption).thenReturn("") + `when`(media.author).thenReturn("") + contributionViewHolder.init(0, contribution) + } + + @Test + @Throws(Exception::class) + fun testInitCaseNonNull_STATE_QUEUED_LIMITED_CONNECTION_MODE() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + `when`(contribution.state).thenReturn(Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE) + `when`(contribution.media).thenReturn(media) + `when`(media.mostRelevantCaption).thenReturn("") + `when`(media.author).thenReturn("") + contributionViewHolder.init(0, contribution) + } + + @Test + @Throws(Exception::class) + fun testInitCaseNonNull_STATE_IN_PROGRESS() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + `when`(contribution.state).thenReturn(Contribution.STATE_IN_PROGRESS) + `when`(contribution.media).thenReturn(media) + `when`(media.mostRelevantCaption).thenReturn("") + `when`(media.author).thenReturn("") + contributionViewHolder.init(0, contribution) + } + + @Test + @Throws(Exception::class) + fun testInitCaseNonNull_STATE_PAUSED() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + `when`(contribution.state).thenReturn(Contribution.STATE_PAUSED) + `when`(contribution.media).thenReturn(media) + `when`(media.mostRelevantCaption).thenReturn("") + `when`(media.author).thenReturn("") + contributionViewHolder.init(0, contribution) + } + + @Test + @Throws(Exception::class) + fun testInitCaseNonNull_STATE_FAILED() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + `when`(contribution.state).thenReturn(Contribution.STATE_FAILED) + `when`(contribution.media).thenReturn(media) + `when`(media.mostRelevantCaption).thenReturn("") + `when`(media.author).thenReturn("") + contributionViewHolder.init(0, contribution) + } + +} \ No newline at end of file From 76e48a0616d29e11a46b224fb8af09331c64e7e4 Mon Sep 17 00:00:00 2001 From: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Date: Wed, 8 Sep 2021 11:18:26 +0530 Subject: [PATCH 0012/1165] Add WikidataItemDetailsActivity Unit Tests (#4618) --- .../WikidataItemDetailsActivityUnitTests.kt | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivityUnitTests.kt 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 new file mode 100644 index 000000000..5f55f06ff --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivityUnitTests.kt @@ -0,0 +1,187 @@ +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.viewpager.widget.ViewPager +import com.google.android.material.tabs.TabLayout +import fr.free.nrw.commons.R +import fr.free.nrw.commons.TestAppAdapter +import fr.free.nrw.commons.TestCommonsApplication +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 +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.`when` +import org.mockito.MockitoAnnotations +import org.powermock.reflect.Whitebox +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +import org.robolectric.fakes.RoboMenu +import org.robolectric.fakes.RoboMenuItem +import org.wikipedia.AppAdapter + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [21], application = TestCommonsApplication::class) +@LooperMode(LooperMode.Mode.PAUSED) +class WikidataItemDetailsActivityUnitTests { + + private lateinit var activity: WikidataItemDetailsActivity + private lateinit var parent: View + + @Mock + private lateinit var mediaDetailPagerFragment: MediaDetailPagerFragment + + @Mock + private lateinit var depictionImagesListFragment: DepictedImagesFragment + + @Mock + private lateinit var supportFragmentManager: FragmentManager + + @Mock + private lateinit var depictedItem: DepictedItem + + @Mock + private lateinit var mediaContainer: FrameLayout + + @Mock + private lateinit var tabLayout: TabLayout + + @Mock + private lateinit var viewPager: ViewPager + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + AppAdapter.set(TestAppAdapter()) + val intent = Intent( + RuntimeEnvironment.application.applicationContext, + WikidataItemDetailsActivity::class.java + ) + intent.putExtra("wikidataItemName", "depictionName") + intent.putExtra("entityId", 0) + activity = + Robolectric.buildActivity(WikidataItemDetailsActivity::class.java, intent).create() + .get() + Whitebox.setInternalState(activity, "mediaDetailPagerFragment", mediaDetailPagerFragment) + Whitebox.setInternalState( + activity, + "depictionImagesListFragment", + depictionImagesListFragment + ) + Whitebox.setInternalState(activity, "supportFragmentManager", supportFragmentManager) + + parent = + LayoutInflater.from(activity).inflate(R.layout.activity_wikidata_item_details, null) + + mediaContainer = parent.findViewById(R.id.mediaContainer) + Whitebox.setInternalState(activity, "mediaContainer", mediaContainer) + + tabLayout = parent.findViewById(R.id.tab_layout) + Whitebox.setInternalState(activity, "tabLayout", tabLayout) + + viewPager = parent.findViewById(R.id.viewPager) + Whitebox.setInternalState(activity, "viewPager", viewPager) + + } + + @Test + @Throws(Exception::class) + fun checkActivityNotNull() { + Assert.assertNotNull(activity) + } + + @Test + @Throws(Exception::class) + fun testViewPagerNotifyDataSetChanged() { + activity.viewPagerNotifyDataSetChanged() + } + + @Test + @Throws(Exception::class) + fun testGetMediaAtPosition() { + activity.getMediaAtPosition(0) + } + + @Test + @Throws(Exception::class) + fun testOnBackPressed() { + `when`(supportFragmentManager.backStackEntryCount).thenReturn(1) + activity.onBackPressed() + } + + @Test + @Throws(Exception::class) + fun testOnBackPressedCaseReturn() { + `when`(supportFragmentManager.backStackEntryCount).thenReturn(1) + `when`(mediaDetailPagerFragment.backButtonClicked()).thenReturn(true) + activity.onBackPressed() + } + + @Test + @Throws(Exception::class) + fun testGetTotalMediaCount() { + activity.totalMediaCount + } + + @Test + @Throws(Exception::class) + fun testGetContributionStateAt() { + Assert.assertEquals(activity.getContributionStateAt(0), null) + } + + @Test + @Throws(Exception::class) + fun testRefreshNominatedMedia() { + activity.refreshNominatedMedia(0) + } + + @Test + @Throws(Exception::class) + fun testOnCreateOptionsMenu() { + Assert.assertEquals(activity.onCreateOptionsMenu(RoboMenu()), true) + } + + @Test + @Throws(Exception::class) + fun testOnOptionsItemSelectedCaseOne() { + Assert.assertEquals( + activity.onOptionsItemSelected(RoboMenuItem(R.id.browser_actions_menu_items)), + true + ) + } + + @Test + @Throws(Exception::class) + fun testOnOptionsItemSelectedCaseTwo() { + Assert.assertEquals(activity.onOptionsItemSelected(RoboMenuItem(android.R.id.home)), true) + } + + @Test + @Throws(Exception::class) + fun testOnOptionsItemSelected() { + Assert.assertEquals(activity.onOptionsItemSelected(RoboMenuItem()), false) + } + + @Test + @Throws(Exception::class) + fun testStartYourself() { + WikidataItemDetailsActivity.startYourself(activity, depictedItem) + } + + @Test + @Throws(Exception::class) + fun testOnMediaClicked() { + activity.onMediaClicked(0) + } + +} \ No newline at end of file From ac842c78c0d2b677ea61459fce03b70ca32b654c Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 9 Sep 2021 13:01:22 +0200 Subject: [PATCH 0013/1165] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-fa/strings.xml | 2 +- app/src/main/res/values-in/strings.xml | 37 ++++++++++++++++++ app/src/main/res/values-my/strings.xml | 53 ++++++++++++++++++++++++-- app/src/main/res/values-su/strings.xml | 12 +++--- 4 files changed, 93 insertions(+), 11 deletions(-) diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 8a3ce0d9d..c308d3f09 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -364,7 +364,7 @@ من تغییر عقیده دادم، نمی‌خواهم دیگر برای همه قابل‌مشاهده باشد. با پوزش، این تصویر برای یک دانشنامه مناسب نیست بارگذاری‌شده توسط خودم در %1$s؛ استفاده‌شده در %2$d مقاله. - به کامانز خوش آمدید!\n\nاولین فایلتان را با فشردن کلید اضافه بارگذاری کنید. + به ویکی‌انبار خوش آمدید!\n\nاولین فایلتان را با فشردن کلید اضافه بارگذاری کنید. رده‌ای انتخاب نشده‌است تصاویر بدون رده به ندرت قابل‌استفاده هستند. آیا مطمئنید که می‌خواهید بدون انتخاب رده ادامه دهید؟ لغو بارگذاری diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 76579521d..91e2b8475 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -1,5 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/script/style/ruleset.xml b/script/style/ruleset.xml deleted file mode 100644 index d516ddcf3..000000000 --- a/script/style/ruleset.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - Custom ruleset for Commons Android app - - .*/R.java - .*/gen/.* - - - - - From 07d170a79c9496fea0326e2284b5fe56ea2b01a8 Mon Sep 17 00:00:00 2001 From: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Date: Mon, 20 Sep 2021 12:30:27 +0530 Subject: [PATCH 0018/1165] Add MediaDetailFragment Unit Tests (#4632) * Add MediaDetailFragment Unit Tests * Add ZoomableActivity Unit Tests --- .../commons/media/MediaDetailFragment.java | 2 +- .../nrw/commons/media/ZoomableActivity.java | 25 +- .../media/MediaDetailFragmentUnitTests.kt | 473 ++++++++++++++++++ .../media/ZoomableActivityUnitTests.kt | 48 ++ .../free/nrw/commons/utils/PagedListMock.kt | 10 +- 5 files changed, 538 insertions(+), 20 deletions(-) create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailFragmentUnitTests.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/media/ZoomableActivityUnitTests.kt diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index ed28960b2..289a859cc 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -306,7 +306,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements final View view = inflater.inflate(R.layout.fragment_media_detail, container, false); ButterKnife.bind(this,view); - Utils.setUnderlinedText(seeMore, R.string.nominated_see_more, container.getContext()); + Utils.setUnderlinedText(seeMore, R.string.nominated_see_more, requireContext()); if (isCategoryImage){ authorLayout.setVisibility(VISIBLE); diff --git a/app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.java b/app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.java index 63b582550..286384ff1 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.java @@ -1,32 +1,27 @@ package fr.free.nrw.commons.media; -import androidx.annotation.NonNull; +import android.graphics.drawable.Animatable; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; +import android.widget.ProgressBar; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import butterknife.BindView; import butterknife.ButterKnife; -import fr.free.nrw.commons.R; -import com.facebook.drawee.drawable.ProgressBarDrawable; -import fr.free.nrw.commons.media.zoomControllers.zoomable.DoubleTapGestureListener; -import fr.free.nrw.commons.media.zoomControllers.zoomable.ZoomableDraweeView; -import timber.log.Timber; - -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.Animatable; -import android.net.Uri; -import android.os.Bundle; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.controller.BaseControllerListener; import com.facebook.drawee.controller.ControllerListener; +import com.facebook.drawee.drawable.ProgressBarDrawable; import com.facebook.drawee.drawable.ScalingUtils; import com.facebook.drawee.generic.GenericDraweeHierarchy; import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; import com.facebook.drawee.interfaces.DraweeController; -import android.view.View; -import android.widget.ProgressBar; - import com.facebook.imagepipeline.image.ImageInfo; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.media.zoomControllers.zoomable.DoubleTapGestureListener; +import fr.free.nrw.commons.media.zoomControllers.zoomable.ZoomableDraweeView; +import timber.log.Timber; public class ZoomableActivity extends AppCompatActivity { 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 new file mode 100644 index 000000000..e54762405 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/media/MediaDetailFragmentUnitTests.kt @@ -0,0 +1,473 @@ +package fr.free.nrw.commons.media + +import android.content.Context +import android.content.res.Configuration +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewTreeObserver +import android.webkit.WebView +import android.widget.* +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentTransaction +import androidx.recyclerview.widget.RecyclerView +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.generic.GenericDraweeHierarchy +import com.facebook.drawee.view.SimpleDraweeView +import com.facebook.soloader.SoLoader +import fr.free.nrw.commons.Media +import fr.free.nrw.commons.R +import fr.free.nrw.commons.TestAppAdapter +import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.category.CategoryEditSearchRecyclerViewAdapter +import fr.free.nrw.commons.explore.SearchActivity +import fr.free.nrw.commons.kvstore.JsonKvStore +import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.ui.widget.HtmlTextView +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.`when` +import org.mockito.Mockito.mock +import org.mockito.MockitoAnnotations +import org.powermock.reflect.Whitebox +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +import org.wikipedia.AppAdapter +import java.lang.reflect.Field +import java.lang.reflect.Method +import java.util.* +import kotlin.collections.HashMap + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [21], application = TestCommonsApplication::class) +@LooperMode(LooperMode.Mode.PAUSED) +class MediaDetailFragmentUnitTests { + + private lateinit var fragment: MediaDetailFragment + private lateinit var fragmentManager: FragmentManager + private lateinit var layoutInflater: LayoutInflater + private lateinit var view: View + private lateinit var context: Context + + @Mock + private lateinit var categoryEditSearchRecyclerViewAdapter: CategoryEditSearchRecyclerViewAdapter + + @Mock + private lateinit var savedInstanceState: Bundle + + @Mock + private lateinit var scrollView: ScrollView + + @Mock + private lateinit var media: Media + + @Mock + private lateinit var categoryRecyclerView: RecyclerView + + @Mock + private lateinit var simpleDraweeView: SimpleDraweeView + + @Mock + private lateinit var textView: TextView + + @Mock + private lateinit var htmlTextView: HtmlTextView + + @Mock + private lateinit var linearLayout: LinearLayout + + @Mock + private lateinit var genericDraweeHierarchy: GenericDraweeHierarchy + + @Mock + private lateinit var button: Button + + @Mock + private lateinit var detailProvider: MediaDetailPagerFragment.MediaDetailProvider + + @Mock + private lateinit var applicationKvStore: JsonKvStore + + @Mock + private lateinit var webView: WebView + + @Mock + private lateinit var progressBar: ProgressBar + + @Mock + private lateinit var listView: ListView + + @Mock + private lateinit var searchView: SearchView + + @Before + fun setUp() { + + MockitoAnnotations.initMocks(this) + + context = RuntimeEnvironment.application.applicationContext + + AppAdapter.set(TestAppAdapter()) + + SoLoader.setInTestMode() + + Fresco.initialize(RuntimeEnvironment.application.applicationContext) + + val activity = Robolectric.buildActivity(SearchActivity::class.java).create().get() + + fragment = MediaDetailFragment() + fragmentManager = activity.supportFragmentManager + val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction() + fragmentTransaction.add(fragment, null) + fragmentTransaction.commitNowAllowingStateLoss() + + layoutInflater = LayoutInflater.from(activity) + + view = LayoutInflater.from(activity) + .inflate(R.layout.fragment_media_detail, null) as View + + scrollView = view.findViewById(R.id.mediaDetailScrollView) + Whitebox.setInternalState(fragment, "scrollView", scrollView) + + categoryRecyclerView = view.findViewById(R.id.rv_categories) + Whitebox.setInternalState(fragment, "categoryRecyclerView", categoryRecyclerView) + + Whitebox.setInternalState(fragment, "media", media) + Whitebox.setInternalState(fragment, "progressBar", progressBar) + Whitebox.setInternalState(fragment, "captionsListView", listView) + Whitebox.setInternalState(fragment, "descriptionWebView", webView) + Whitebox.setInternalState(fragment, "detailProvider", detailProvider) + Whitebox.setInternalState(fragment, "image", simpleDraweeView) + Whitebox.setInternalState(fragment, "title", textView) + Whitebox.setInternalState(fragment, "toDoReason", textView) + Whitebox.setInternalState(fragment, "desc", htmlTextView) + Whitebox.setInternalState(fragment, "license", textView) + Whitebox.setInternalState(fragment, "coordinates", textView) + Whitebox.setInternalState(fragment, "seeMore", textView) + Whitebox.setInternalState(fragment, "uploadedDate", textView) + Whitebox.setInternalState(fragment, "mediaCaption", textView) + Whitebox.setInternalState(fragment, "captionLayout", linearLayout) + Whitebox.setInternalState(fragment, "depictsLayout", linearLayout) + Whitebox.setInternalState(fragment, "depictionContainer", linearLayout) + Whitebox.setInternalState(fragment, "toDoLayout", linearLayout) + Whitebox.setInternalState(fragment, "dummyCategoryEditContainer", linearLayout) + Whitebox.setInternalState(fragment, "showCaptionAndDescriptionContainer", linearLayout) + Whitebox.setInternalState(fragment, "updateCategoriesButton", button) + Whitebox.setInternalState(fragment, "categoryContainer", linearLayout) + Whitebox.setInternalState(fragment, "categorySearchView", searchView) + Whitebox.setInternalState(fragment, "mediaDiscussion", textView) + Whitebox.setInternalState( + fragment, + "categoryEditSearchRecyclerViewAdapter", + categoryEditSearchRecyclerViewAdapter + ) + + `when`(simpleDraweeView.hierarchy).thenReturn(genericDraweeHierarchy) + val map = HashMap() + map[Locale.getDefault().language] = "" + `when`(media.descriptions).thenReturn(map) + } + + @Test + @Throws(Exception::class) + fun checkFragmentNotNull() { + Assert.assertNotNull(fragment) + } + + @Test + @Throws(Exception::class) + fun testOnCreateView() { + Whitebox.setInternalState(fragment, "applicationKvStore", applicationKvStore) + `when`(applicationKvStore.getBoolean("login_skipped")).thenReturn(true) + fragment.onCreateView(layoutInflater, null, savedInstanceState) + } + + @Test + @Throws(Exception::class) + fun testOnSaveInstanceState() { + fragment.onSaveInstanceState(savedInstanceState) + } + + @Test + @Throws(Exception::class) + fun testLaunchZoomActivity() { + `when`(media.imageUrl).thenReturn("") + fragment.launchZoomActivity(view) + } + + @Test + @Throws(Exception::class) + fun testOnResume() { + fragment.onResume() + } + + @Test + @Throws(Exception::class) + fun testOnConfigurationChangedCaseTrue() { + val newConfig = mock(Configuration::class.java) + fragment.onConfigurationChanged(newConfig) + } + + @Test + @Throws(Exception::class) + fun testOnConfigurationChangedCaseFalse() { + val newConfig = mock(Configuration::class.java) + Whitebox.setInternalState(fragment, "heightVerifyingBoolean", false) + fragment.onConfigurationChanged(newConfig) + } + + @Test + @Throws(Exception::class) + fun testOnDestroyView() { + val layoutListener = mock(ViewTreeObserver.OnGlobalLayoutListener::class.java) + Whitebox.setInternalState(fragment, "layoutListener", layoutListener) + fragment.onDestroyView() + } + + @Test + @Throws(Exception::class) + fun testExtractDescription() { + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "extractDescription", + String::class.java + ) + method.isAccessible = true + method.invoke(fragment, "") + } + + @Test + @Throws(Exception::class) + fun testGetDescription() { + `when`(media.filename).thenReturn("") + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "getDescription" + ) + method.isAccessible = true + method.invoke(fragment) + } + + @Test + @Throws(Exception::class) + fun testGetCaptions() { + `when`(media.captions).thenReturn(mapOf(Pair("a", "b"))) + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "getCaptions" + ) + method.isAccessible = true + method.invoke(fragment) + } + + @Test + @Throws(Exception::class) + fun testGetCaptionsCaseEmpty() { + `when`(media.captions).thenReturn(mapOf()) + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "getCaptions" + ) + method.isAccessible = true + method.invoke(fragment) + } + + @Test + @Throws(Exception::class) + fun testSetUpCaptionAndDescriptionLayout() { + `when`(media.filename).thenReturn("") + val field: Field = + MediaDetailFragment::class.java.getDeclaredField("descriptionHtmlCode") + field.isAccessible = true + field.set(fragment, null) + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "setUpCaptionAndDescriptionLayout" + ) + method.isAccessible = true + method.invoke(fragment) + } + + @Test + @Throws(Exception::class) + fun testUpdateCategoryDisplayCaseNull() { + Assert.assertEquals(fragment.updateCategoryDisplay(null), false) + } + + @Test + @Throws(Exception::class) + fun testUpdateCategoryDisplayCaseNonNull() { + Assert.assertEquals(fragment.updateCategoryDisplay(listOf()), true) + } + + @Test + @Throws(Exception::class) + fun testShowCaptionAndDescriptionCaseVisible() { + fragment.showCaptionAndDescription() + } + + @Test + @Throws(Exception::class) + fun testShowCaptionAndDescription() { + `when`(linearLayout.visibility).thenReturn(View.GONE) + `when`(media.filename).thenReturn("") + fragment.showCaptionAndDescription() + } + + @Test + @Throws(Exception::class) + fun testPrettyCoordinatesCaseNull() { + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "prettyCoordinates", + Media::class.java + ) + method.isAccessible = true + method.invoke(fragment, media) + } + + @Test + @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 + ) + method.isAccessible = true + method.invoke(fragment, media) + } + + @Test + @Throws(Exception::class) + fun testPrettyUploadedDateCaseNull() { + `when`(media.dateUploaded).thenReturn(null) + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "prettyUploadedDate", + Media::class.java + ) + method.isAccessible = true + method.invoke(fragment, media) + } + + @Test + @Throws(Exception::class) + fun testPrettyUploadedDateCaseNonNull() { + `when`(media.dateUploaded).thenReturn(Date(2000, 1, 1)) + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "prettyUploadedDate", + Media::class.java + ) + method.isAccessible = true + method.invoke(fragment, media) + } + + @Test + @Throws(Exception::class) + fun testPrettyLicenseCaseNull() { + `when`(media.license).thenReturn(null) + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "prettyLicense", + Media::class.java + ) + method.isAccessible = true + method.invoke(fragment, media) + } + + @Test + @Throws(Exception::class) + fun testPrettyLicenseCaseNonNull() { + `when`(media.license).thenReturn("licence") + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "prettyLicense", + Media::class.java + ) + method.isAccessible = true + method.invoke(fragment, media) + } + + @Test + @Throws(Exception::class) + fun testPrettyDiscussion() { + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "prettyDiscussion", + String::class.java + ) + method.isAccessible = true + method.invoke(fragment, "mock") + } + + @Test + @Throws(Exception::class) + fun testPrettyCaptionCaseEmpty() { + `when`(media.captions).thenReturn(mapOf(Pair("a", ""))) + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "prettyCaption", + Media::class.java + ) + method.isAccessible = true + method.invoke(fragment, media) + } + + @Test + @Throws(Exception::class) + fun testPrettyCaptionCaseNonEmpty() { + `when`(media.captions).thenReturn(mapOf(Pair("a", "b"))) + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "prettyCaption", + Media::class.java + ) + method.isAccessible = true + method.invoke(fragment, media) + } + + @Test + @Throws(Exception::class) + fun testPrettyCaption() { + `when`(media.captions).thenReturn(mapOf()) + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "prettyCaption", + Media::class.java + ) + method.isAccessible = true + method.invoke(fragment, media) + } + + @Test + @Throws(Exception::class) + fun testSetupImageView() { + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "setupImageView" + ) + method.isAccessible = true + method.invoke(fragment) + } + + @Test + @Throws(Exception::class) + fun testSetupToDo() { + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "setupToDo" + ) + method.isAccessible = true + method.invoke(fragment) + } + + @Test + @Throws(Exception::class) + fun testOnDiscussionLoaded() { + val method: Method = MediaDetailFragment::class.java.getDeclaredMethod( + "onDiscussionLoaded", + String::class.java + ) + method.isAccessible = true + method.invoke(fragment, "") + } + + @Test + @Throws(Exception::class) + fun testForMedia() { + MediaDetailFragment.forMedia(0, true, true, true) + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..14b395973 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/media/ZoomableActivityUnitTests.kt @@ -0,0 +1,48 @@ +package fr.free.nrw.commons.media + +import android.content.Context +import android.content.Intent +import android.net.Uri +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.soloader.SoLoader +import fr.free.nrw.commons.TestCommonsApplication +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [21], application = TestCommonsApplication::class) +@LooperMode(LooperMode.Mode.PAUSED) +class ZoomableActivityUnitTests { + + private lateinit var context: Context + private lateinit var activity: ZoomableActivity + + @Mock + private lateinit var uri: Uri + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + context = RuntimeEnvironment.application.applicationContext + SoLoader.setInTestMode() + Fresco.initialize(context) + val intent = Intent().setData(uri) + activity = Robolectric.buildActivity(ZoomableActivity::class.java, intent).create().get() + } + + @Test + @Throws(Exception::class) + fun checkActivityNotNull() { + Assert.assertNotNull(activity) + } + +} \ 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 f4a42fa91..39f8f8899 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 @@ -37,7 +37,7 @@ fun createMockDataSourceFactory(itemList: List): DataSource.Factory(private val itemList: List) : - LimitOffsetDataSource(mockDb(), mockQuery(), false, null) { - override fun convertRows(cursor: Cursor?): MutableList = itemList.toMutableList() + LimitOffsetDataSource(mockDb(), mockQuery(), false, "") { + override fun convertRows(cursor: Cursor): MutableList { + return itemList.toMutableList() + } override fun countItems(): Int = itemList.count() override fun isInvalid(): Boolean = false override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback) { From 121119a3589d667bb99f88c6419847857aa366ea Mon Sep 17 00:00:00 2001 From: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com> Date: Mon, 20 Sep 2021 12:30:36 +0530 Subject: [PATCH 0019/1165] Add some more Nearby Unit Tests (#4633) --- .../nearby/NearbyBaseMarkerUnitTests.kt | 92 +++++++++++++++ ...ilterSearchRecyclerViewAdapterUnitTests.kt | 105 ++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyBaseMarkerUnitTests.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapterUnitTests.kt diff --git a/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyBaseMarkerUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyBaseMarkerUnitTests.kt new file mode 100644 index 000000000..60fafab82 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyBaseMarkerUnitTests.kt @@ -0,0 +1,92 @@ +package fr.free.nrw.commons.nearby + +import android.os.Parcel +import com.mapbox.mapboxsdk.annotations.Icon +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.powermock.reflect.Whitebox + +class NearbyBaseMarkerUnitTests { + + private lateinit var marker: NearbyBaseMarker + + @Mock + private lateinit var place: Place + + @Mock + private lateinit var parcel: Parcel + + @Mock + private lateinit var icon: Icon + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + marker = NearbyBaseMarker() + Whitebox.setInternalState(marker, "icon", icon) + } + + @Test + @Throws(Exception::class) + fun checkNotNull() { + Assert.assertNotNull(marker) + } + + @Test + @Throws(Exception::class) + fun testPlace() { + marker.place(place) + } + + @Test + @Throws(Exception::class) + fun testGetThis() { + marker.getThis() + } + + @Test + @Throws(Exception::class) + fun testGetMarker() { + marker.marker + } + + @Test + @Throws(Exception::class) + fun testGetPlace() { + marker.place + } + + @Test + @Throws(Exception::class) + fun testDescribeContents() { + marker.describeContents() + } + + @Test + @Throws(Exception::class) + fun testWriteToParcel() { + marker.writeToParcel(parcel, 0) + } + + @Test + @Throws(Exception::class) + fun testEquals() { + marker.equals(this) + } + + @Test + @Throws(Exception::class) + fun testEqualsCaseNull() { + Assert.assertFalse(marker.equals(this)) + } + + @Test + @Throws(Exception::class) + fun testHashCode() { + marker.hashCode() + } + +} \ 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 new file mode 100644 index 000000000..3352f2d6b --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapterUnitTests.kt @@ -0,0 +1,105 @@ +package fr.free.nrw.commons.nearby + +import android.content.Context +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import fr.free.nrw.commons.TestCommonsApplication +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +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 + + @Mock + private lateinit var recyclerView: RecyclerView + + @Mock + private lateinit var callback: NearbyFilterSearchRecyclerViewAdapter.Callback + + @Mock + private lateinit var viewHolder: NearbyFilterSearchRecyclerViewAdapter.RecyclerViewHolder + + @Mock + private lateinit var imageView: ImageView + + @Mock + private lateinit var textView: TextView + + @Mock + private lateinit var linearLayout: LinearLayout + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + context = RuntimeEnvironment.application.applicationContext + adapter = NearbyFilterSearchRecyclerViewAdapter(context, ArrayList