mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <Pratham2305@users.noreply.github.com> * fix edit categories ui (#4414) Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com> * 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 <tur.neslihan@gmail.com> * 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 <iamaditya2009@gmail.com> * 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <jamiejbrown521@gmail.com> Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com> Co-authored-by: Ashar <asharalikhan200@gmail.com> * [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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <Pratham2305@users.noreply.github.com> * fix edit categories ui (#4414) Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com> * 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 <tur.neslihan@gmail.com> * 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 <iamaditya2009@gmail.com> * 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <jamiejbrown521@gmail.com> Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com> Co-authored-by: Ashar <asharalikhan200@gmail.com> * 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <Pratham2305@users.noreply.github.com> * fix edit categories ui (#4414) Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com> * 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 <tur.neslihan@gmail.com> * 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 <iamaditya2009@gmail.com> * 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <jamiejbrown521@gmail.com> Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com> Co-authored-by: Ashar <asharalikhan200@gmail.com> * [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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <Pratham2305@users.noreply.github.com> * fix edit categories ui (#4414) Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com> * 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 <tur.neslihan@gmail.com> * 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 <iamaditya2009@gmail.com> * 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <jamiejbrown521@gmail.com> Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com> Co-authored-by: Ashar <asharalikhan200@gmail.com> * 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <Pratham2305@users.noreply.github.com> * fix edit categories ui (#4414) Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com> * 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 <tur.neslihan@gmail.com> * 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 <iamaditya2009@gmail.com> * 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <jamiejbrown521@gmail.com> Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com> Co-authored-by: Ashar <asharalikhan200@gmail.com> * [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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <Pratham2305@users.noreply.github.com> * fix edit categories ui (#4414) Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com> * 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 <tur.neslihan@gmail.com> * 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 <iamaditya2009@gmail.com> * 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <jamiejbrown521@gmail.com> Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com> Co-authored-by: Ashar <asharalikhan200@gmail.com> * 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <Pratham2305@users.noreply.github.com> * fix edit categories ui (#4414) Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com> * 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 <tur.neslihan@gmail.com> * 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 <iamaditya2009@gmail.com> * 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 commitb1b4257f20. 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 <Pratham2305@users.noreply.github.com> * 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 <Pratham2305@users.noreply.github.com> 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 <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@users.noreply.github.com> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <jamiejbrown521@gmail.com> Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com> Co-authored-by: Ashar <asharalikhan200@gmail.com> * 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 <iamaditya2009@gmail.com> Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com> Co-authored-by: translatewiki.net <l10n-bot@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 <amir.aharoni@mail.huji.ac.il> Co-authored-by: Josephine Lim <josephinelim86@gmail.com> Co-authored-by: Ashish <ashishkumar468@gmail.com> Co-authored-by: neslihanturan <tur.neslihan@gmail.com> 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 <Pratham2305@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> 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 <jamiejbrown521@gmail.com> Co-authored-by: Prince kushwaha <65972015+Prince-kushwaha@users.noreply.github.com> Co-authored-by: Ashar <asharalikhan200@gmail.com>
This commit is contained in:
parent
6588a6fd0e
commit
c6f4649fcf
68 changed files with 3854 additions and 35 deletions
|
|
@ -93,6 +93,7 @@ dependencies {
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1"
|
testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1"
|
||||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1"
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1"
|
||||||
testImplementation 'com.facebook.soloader:soloader:0.9.0'
|
testImplementation 'com.facebook.soloader:soloader:0.9.0'
|
||||||
|
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2"
|
||||||
|
|
||||||
// Android testing
|
// Android testing
|
||||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION"
|
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION"
|
||||||
|
|
@ -142,6 +143,10 @@ dependencies {
|
||||||
def work_version = "2.4.0"
|
def work_version = "2.4.0"
|
||||||
// Kotlin + coroutines
|
// Kotlin + coroutines
|
||||||
implementation "androidx.work:work-runtime-ktx:$work_version"
|
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 {
|
android {
|
||||||
|
|
@ -204,7 +209,7 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
minifyEnabled true
|
minifyEnabled false
|
||||||
testCoverageEnabled project.hasProperty('coverage')
|
testCoverageEnabled project.hasProperty('coverage')
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||||
testProguardFile 'test-proguard-rules.txt'
|
testProguardFile 'test-proguard-rules.txt'
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,12 @@
|
||||||
<activity android:name=".quiz.QuizResultActivity"
|
<activity android:name=".quiz.QuizResultActivity"
|
||||||
android:label="@string/result"/>
|
android:label="@string/result"/>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".customselector.ui.selector.CustomSelectorActivity"
|
||||||
|
android:label="@string/title_activity_custom_selector"
|
||||||
|
android:configChanges="screenSize|keyboard|orientation"
|
||||||
|
android:parentActivityName=".contributions.MainActivity" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".category.CategoryDetailsActivity"
|
android:name=".category.CategoryDetailsActivity"
|
||||||
android:label="@string/title_activity_featured_images"
|
android:label="@string/title_activity_featured_images"
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,8 @@ data class Contribution constructor(
|
||||||
var dataLength: Long = 0,
|
var dataLength: Long = 0,
|
||||||
var dateCreated: Date? = null,
|
var dateCreated: Date? = null,
|
||||||
var dateModified: Date? = null,
|
var dateModified: Date? = null,
|
||||||
var hasInvalidLocation : Int = 0
|
var hasInvalidLocation : Int = 0,
|
||||||
|
var contentUri: Uri? = null
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
fun completeWith(media: Media): Contribution {
|
fun completeWith(media: Media): Contribution {
|
||||||
|
|
@ -64,7 +65,8 @@ data class Contribution constructor(
|
||||||
decimalCoords = item.gpsCoords.decimalCoords,
|
decimalCoords = item.gpsCoords.decimalCoords,
|
||||||
dateCreatedSource = "",
|
dateCreatedSource = "",
|
||||||
depictedItems = depictedItems,
|
depictedItems = depictedItems,
|
||||||
wikidataPlace = from(item.place)
|
wikidataPlace = from(item.place),
|
||||||
|
contentUri = item.contentUri
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,20 @@ public class ContributionController {
|
||||||
initiateGalleryUpload(activity, allowMultipleUploads);
|
initiateGalleryUpload(activity, allowMultipleUploads);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate gallery picker with permission
|
||||||
|
*/
|
||||||
|
public void initiateCustomGalleryPickWithPermission(final Activity activity) {
|
||||||
|
setPickerConfiguration(activity,true);
|
||||||
|
|
||||||
|
PermissionUtils.checkPermissionsAndPerformAction(activity,
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||||
|
() -> FilePicker.openCustomSelector(activity, 0),
|
||||||
|
R.string.storage_permission_title,
|
||||||
|
R.string.write_storage_permission_rationale);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open chooser for gallery uploads
|
* Open chooser for gallery uploads
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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 static fr.free.nrw.commons.di.NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
@ -29,13 +30,16 @@ import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
import butterknife.OnClick;
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import fr.free.nrw.commons.Media;
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.Utils;
|
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.di.CommonsDaggerSupportFragment;
|
||||||
import fr.free.nrw.commons.utils.DialogUtil;
|
import fr.free.nrw.commons.utils.DialogUtil;
|
||||||
import fr.free.nrw.commons.media.MediaClient;
|
import fr.free.nrw.commons.media.MediaClient;
|
||||||
|
import fr.free.nrw.commons.utils.SystemThemeUtils;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
@ -52,7 +56,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
||||||
WikipediaInstructionsDialogFragment.Callback {
|
WikipediaInstructionsDialogFragment.Callback {
|
||||||
|
|
||||||
private static final String RV_STATE = "rv_scroll_state";
|
private static final String RV_STATE = "rv_scroll_state";
|
||||||
|
|
||||||
@BindView(R.id.contributionsList)
|
@BindView(R.id.contributionsList)
|
||||||
RecyclerView rvContributionsList;
|
RecyclerView rvContributionsList;
|
||||||
@BindView(R.id.loadingContributionsProgressBar)
|
@BindView(R.id.loadingContributionsProgressBar)
|
||||||
|
|
@ -67,6 +71,11 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
||||||
TextView noContributionsYet;
|
TextView noContributionsYet;
|
||||||
@BindView(R.id.fab_layout)
|
@BindView(R.id.fab_layout)
|
||||||
LinearLayout fab_layout;
|
LinearLayout fab_layout;
|
||||||
|
@BindView(R.id.fab_custom_gallery)
|
||||||
|
FloatingActionButton fabCustomGallery;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SystemThemeUtils systemThemeUtils;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ContributionController controller;
|
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) {
|
private void animateFAB(final boolean isFabOpen) {
|
||||||
this.isFabOpen = !isFabOpen;
|
this.isFabOpen = !isFabOpen;
|
||||||
if (fabPlus.isShown()) {
|
if (fabPlus.isShown()) {
|
||||||
if (isFabOpen) {
|
if (isFabOpen) {
|
||||||
fabPlus.startAnimation(rotate_backward);
|
fabPlus.startAnimation(rotate_backward);
|
||||||
fabCamera.startAnimation(fab_close);
|
fabCamera.startAnimation(fab_close);
|
||||||
fabGallery.startAnimation(fab_close);
|
fabGallery.startAnimation(fab_close);
|
||||||
fabCamera.hide();
|
fabCustomGallery.startAnimation(fab_close);
|
||||||
fabGallery.hide();
|
fabCamera.hide();
|
||||||
} else {
|
fabGallery.hide();
|
||||||
fabPlus.startAnimation(rotate_forward);
|
fabCustomGallery.hide();
|
||||||
fabCamera.startAnimation(fab_open);
|
} else {
|
||||||
fabGallery.startAnimation(fab_open);
|
fabPlus.startAnimation(rotate_forward);
|
||||||
fabCamera.show();
|
fabCamera.startAnimation(fab_open);
|
||||||
fabGallery.show();
|
fabGallery.startAnimation(fab_open);
|
||||||
}
|
fabCustomGallery.startAnimation(fab_open);
|
||||||
this.isFabOpen = !isFabOpen;
|
fabCamera.show();
|
||||||
|
fabGallery.show();
|
||||||
|
fabCustomGallery.show();
|
||||||
|
}
|
||||||
|
this.isFabOpen = !isFabOpen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
@ -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<Image>): ArrayList<Folder> {
|
||||||
|
val folderMap: MutableMap<Long, Folder> = 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<Image>, bukketId: Long?): ArrayList<Image> {
|
||||||
|
if (bukketId == null) return images
|
||||||
|
|
||||||
|
val filteredImages = arrayListOf<Image>()
|
||||||
|
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: Image): Int {
|
||||||
|
return list.indexOf(image)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getIndex: Returns the index of image in given list.
|
||||||
|
*/
|
||||||
|
fun getIndexFromId(list: ArrayList<Image>, 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<Image>, masterList: ArrayList<Image>): ArrayList<Int> {
|
||||||
|
|
||||||
|
// Can be optimised as masterList is sorted by time.
|
||||||
|
|
||||||
|
val indexes = arrayListOf<Int>()
|
||||||
|
for(image in list) {
|
||||||
|
val index = getIndex(masterList, image)
|
||||||
|
if (index == -1) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
indexes.add(index)
|
||||||
|
}
|
||||||
|
return indexes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -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<Image>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On failed
|
||||||
|
* @param throwable : throwable exception on failure.
|
||||||
|
*/
|
||||||
|
fun onFailed(throwable: Throwable)
|
||||||
|
}
|
||||||
|
|
@ -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<Image>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onLongPress
|
||||||
|
* @param imageUri : uri of image
|
||||||
|
*/
|
||||||
|
fun onLongPress(imageUri: Uri)
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
@ -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<Image> = arrayListOf<Image>()
|
||||||
|
|
||||||
|
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Image> {
|
||||||
|
override fun createFromParcel(parcel: Parcel): Image {
|
||||||
|
return Image(parcel)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newArray(size: Int): Array<Image?> {
|
||||||
|
return arrayOfNulls(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Image>) {
|
||||||
|
}
|
||||||
|
|
@ -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<FolderAdapter.FolderViewHolder?>(context) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of folders.
|
||||||
|
*/
|
||||||
|
private var folders: MutableList<Folder> = 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<Image>()
|
||||||
|
|
||||||
|
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<Folder>) {
|
||||||
|
val oldFolderList: MutableList<Folder> = 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<Folder>,
|
||||||
|
var newFolders: MutableList<Folder>
|
||||||
|
) : 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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<ImageAdapter.ImageViewHolder>(context) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ImageSelectedOrUpdated payload class.
|
||||||
|
*/
|
||||||
|
class ImageSelectedOrUpdated
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ImageUnselected payload class.
|
||||||
|
*/
|
||||||
|
class ImageUnselected
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently selected images.
|
||||||
|
*/
|
||||||
|
private var selectedImages = arrayListOf<Image>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of all images in adapter.
|
||||||
|
*/
|
||||||
|
private var images: ArrayList<Image> = 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<Image>) {
|
||||||
|
val oldImageList:ArrayList<Image> = images
|
||||||
|
val newImageList:ArrayList<Image> = 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<Image>,
|
||||||
|
var newImageList: ArrayList<Image>
|
||||||
|
) : 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])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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<T : RecyclerView.ViewHolder?>(val context: Context): RecyclerView.Adapter<T>() {
|
||||||
|
val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||||
|
}
|
||||||
|
|
@ -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<TextView>(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<Image>) {
|
||||||
|
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<Image>) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<ArrayList<Image>> = 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<Image>) {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<CustomSelectorViewModel: ViewModel?> create(modelClass: Class<CustomSelectorViewModel>) : CustomSelectorViewModel {
|
||||||
|
return CustomSelectorViewModel(context,imageFileLoader) as CustomSelectorViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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<Folder>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Image>()
|
||||||
|
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).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
@ -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<Image>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Image, String> = HashMap()
|
||||||
|
private var mapHolderImage : HashMap<ImageViewHolder, Image> = HashMap()
|
||||||
|
private var mapResult: HashMap<String, Result> = HashMap()
|
||||||
|
private var mapImageSHA1: HashMap<Uri, String> = 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@ import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
|
||||||
public class DBOpenHelper extends SQLiteOpenHelper {
|
public class DBOpenHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "commons.db";
|
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";
|
public static final String CONTRIBUTIONS_TABLE = "contributions";
|
||||||
private final String DROP_TABLE_STATEMENT="DROP TABLE IF EXISTS %s";
|
private final String DROP_TABLE_STATEMENT="DROP TABLE IF EXISTS %s";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import androidx.room.RoomDatabase
|
||||||
import androidx.room.TypeConverters
|
import androidx.room.TypeConverters
|
||||||
import fr.free.nrw.commons.contributions.Contribution
|
import fr.free.nrw.commons.contributions.Contribution
|
||||||
import fr.free.nrw.commons.contributions.ContributionDao
|
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.Depicts
|
||||||
import fr.free.nrw.commons.upload.depicts.DepictsDao
|
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
|
* 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)
|
@TypeConverters(Converters::class)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
abstract fun contributionDao(): ContributionDao
|
abstract fun contributionDao(): ContributionDao
|
||||||
abstract fun DepictsDao(): DepictsDao;
|
abstract fun DepictsDao(): DepictsDao;
|
||||||
|
abstract fun UploadedStatusDao(): UploadedStatusDao;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import fr.free.nrw.commons.auth.LoginActivity;
|
||||||
import fr.free.nrw.commons.auth.SignupActivity;
|
import fr.free.nrw.commons.auth.SignupActivity;
|
||||||
import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
||||||
import fr.free.nrw.commons.contributions.MainActivity;
|
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.depictions.WikidataItemDetailsActivity;
|
||||||
import fr.free.nrw.commons.explore.SearchActivity;
|
import fr.free.nrw.commons.explore.SearchActivity;
|
||||||
import fr.free.nrw.commons.notification.NotificationActivity;
|
import fr.free.nrw.commons.notification.NotificationActivity;
|
||||||
|
|
@ -34,6 +35,9 @@ public abstract class ActivityBuilderModule {
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract MainActivity bindContributionsActivity();
|
abstract MainActivity bindContributionsActivity();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract CustomSelectorActivity bindCustomSelectorActivity();
|
||||||
|
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract SettingsActivity bindSettingsActivity();
|
abstract SettingsActivity bindSettingsActivity();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.auth.AccountUtil;
|
import fr.free.nrw.commons.auth.AccountUtil;
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.contributions.ContributionDao;
|
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.data.DBOpenHelper;
|
||||||
import fr.free.nrw.commons.db.AppDatabase;
|
import fr.free.nrw.commons.db.AppDatabase;
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||||
|
|
@ -66,6 +68,16 @@ public class CommonsApplicationModule {
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides ImageFileLoader used to fetch device images.
|
||||||
|
* @param context
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
public ImageFileLoader providesImageFileLoader(Context context) {
|
||||||
|
return new ImageFileLoader(context);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
public Context providesApplicationContext() {
|
public Context providesApplicationContext() {
|
||||||
return this.applicationContext;
|
return this.applicationContext;
|
||||||
|
|
@ -244,13 +256,21 @@ public class CommonsApplicationModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the reference of DepictsDao class
|
* Get the reference of DepictsDao class.
|
||||||
*/
|
*/
|
||||||
@Provides
|
@Provides
|
||||||
public DepictsDao providesDepictDao(AppDatabase appDatabase) {
|
public DepictsDao providesDepictDao(AppDatabase appDatabase) {
|
||||||
return appDatabase.DepictsDao();
|
return appDatabase.DepictsDao();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the reference of UploadedStatus class.
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
public UploadedStatusDao providesUploadedStatusDao(AppDatabase appDatabase) {
|
||||||
|
return appDatabase.UploadedStatusDao();
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
public ContentResolver providesContentResolver(Context context){
|
public ContentResolver providesContentResolver(Context context){
|
||||||
return context.getContentResolver();
|
return context.getContentResolver();
|
||||||
|
|
|
||||||
|
|
@ -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.bookmarks.pictures.BookmarkPicturesFragment;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsFragment;
|
import fr.free.nrw.commons.contributions.ContributionsFragment;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsListFragment;
|
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.ExploreFragment;
|
||||||
import fr.free.nrw.commons.explore.ExploreListRootFragment;
|
import fr.free.nrw.commons.explore.ExploreListRootFragment;
|
||||||
import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment;
|
import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment;
|
||||||
|
|
@ -49,6 +51,12 @@ public abstract class FragmentBuilderModule {
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract MediaDetailFragment bindMediaDetailFragment();
|
abstract MediaDetailFragment bindMediaDetailFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract FolderFragment bindFolderFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract ImageFragment bindImageFragment();
|
||||||
|
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract MediaDetailPagerFragment bindMediaDetailPagerFragment();
|
abstract MediaDetailPagerFragment bindMediaDetailPagerFragment();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ public class ExploreListRootFragment extends CommonsDaggerSupportFragment implem
|
||||||
featuredArguments.putString("categoryName", title);
|
featuredArguments.putString("categoryName", title);
|
||||||
listFragment.setArguments(featuredArguments);
|
listFragment.setArguments(featuredArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ public interface Constants {
|
||||||
int FILE_PICKER_IMAGE_IDENTIFICATOR = 0b1101101100; //876
|
int FILE_PICKER_IMAGE_IDENTIFICATOR = 0b1101101100; //876
|
||||||
int SOURCE_CHOOSER = 1 << 15;
|
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_DOCUMENTS = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 11);
|
||||||
int PICK_PICTURE_FROM_GALLERY = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 12);
|
int PICK_PICTURE_FROM_GALLERY = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 12);
|
||||||
int TAKE_PICTURE = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 13);
|
int TAKE_PICTURE = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 13);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import android.text.TextUtils;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
|
@ -51,6 +53,17 @@ public class FilePicker implements Constants {
|
||||||
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, configuration(context).allowsMultiplePickingInGallery());
|
.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) {
|
private static Intent createCameraForImageIntent(@NonNull Context context, int type) {
|
||||||
storeType(context, type);
|
storeType(context, type);
|
||||||
|
|
||||||
|
|
@ -97,6 +110,14 @@ public class FilePicker implements Constants {
|
||||||
activity.startActivityForResult(intent, RequestCodes.PICK_PICTURE_FROM_GALLERY);
|
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
|
* 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 ||
|
if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY ||
|
||||||
requestCode == RequestCodes.TAKE_PICTURE ||
|
requestCode == RequestCodes.TAKE_PICTURE ||
|
||||||
requestCode == RequestCodes.CAPTURE_VIDEO ||
|
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 (resultCode == Activity.RESULT_OK) {
|
||||||
if (requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS && !isPhoto(data)) {
|
if (requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS && !isPhoto(data)) {
|
||||||
onPictureReturnedFromDocuments(data, activity, callbacks);
|
onPictureReturnedFromDocuments(data, activity, callbacks);
|
||||||
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY && !isPhoto(data)) {
|
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY && !isPhoto(data)) {
|
||||||
onPictureReturnedFromGallery(data, activity, callbacks);
|
onPictureReturnedFromGallery(data, activity, callbacks);
|
||||||
|
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) {
|
||||||
|
onPictureReturnedFromCustomSelector(data, activity, callbacks);
|
||||||
} else if (requestCode == RequestCodes.TAKE_PICTURE) {
|
} else if (requestCode == RequestCodes.TAKE_PICTURE) {
|
||||||
onPictureReturnedFromCamera(activity, callbacks);
|
onPictureReturnedFromCamera(activity, callbacks);
|
||||||
} else if (requestCode == RequestCodes.CAPTURE_VIDEO) {
|
} 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<UploadableFile> 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<UploadableFile> getFilesFromCustomSelector(Intent data, Activity activity) throws IOException, SecurityException {
|
||||||
|
List<UploadableFile> files = new ArrayList<>();
|
||||||
|
ArrayList<Image> 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) {
|
private static void onPictureReturnedFromGallery(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
|
||||||
try {
|
try {
|
||||||
List<UploadableFile> files = getFilesFromGalleryPictures(data, activity);
|
List<UploadableFile> files = getFilesFromGalleryPictures(data, activity);
|
||||||
|
|
@ -301,7 +359,7 @@ public class FilePicker implements Constants {
|
||||||
|
|
||||||
|
|
||||||
public enum ImageSource {
|
public enum ImageSource {
|
||||||
GALLERY, DOCUMENTS, CAMERA_IMAGE, CAMERA_VIDEO
|
GALLERY, DOCUMENTS, CAMERA_IMAGE, CAMERA_VIDEO, CUSTOM_SELECTOR
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Callbacks {
|
public interface Callbacks {
|
||||||
|
|
|
||||||
|
|
@ -24,19 +24,38 @@ import java.util.UUID;
|
||||||
|
|
||||||
import timber.log.Timber;
|
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) {
|
private static String getFolderName(@NonNull Context context) {
|
||||||
return FilePicker.configuration(context).getFolderName();
|
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) {
|
private static File tempImageDirectory(@NonNull Context context) {
|
||||||
File privateTempDir = new File(context.getCacheDir(), DEFAULT_FOLDER_NAME);
|
File privateTempDir = new File(context.getCacheDir(), DEFAULT_FOLDER_NAME);
|
||||||
if (!privateTempDir.exists()) privateTempDir.mkdirs();
|
if (!privateTempDir.exists()) privateTempDir.mkdirs();
|
||||||
return privateTempDir;
|
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) {
|
private static void writeToFile(InputStream in, File file) {
|
||||||
try {
|
try {
|
||||||
OutputStream out = new FileOutputStream(file);
|
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 {
|
private static void copyFile(File src, File dst) throws IOException {
|
||||||
InputStream in = new FileInputStream(src);
|
InputStream in = new FileInputStream(src);
|
||||||
writeToFile(in, dst);
|
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<UploadableFile> filesToCopy) {
|
static void copyFilesInSeparateThread(final Context context, final List<UploadableFile> filesToCopy) {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
List<File> copiedFiles = new ArrayList<>();
|
List<File> copiedFiles = new ArrayList<>();
|
||||||
|
|
@ -64,7 +96,9 @@ class PickedFiles implements Constants {
|
||||||
for (UploadableFile uploadableFile : filesToCopy) {
|
for (UploadableFile uploadableFile : filesToCopy) {
|
||||||
File fileToCopy = uploadableFile.getFile();
|
File fileToCopy = uploadableFile.getFile();
|
||||||
File dstDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), getFolderName(context));
|
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[] filenameSplit = fileToCopy.getName().split("\\.");
|
||||||
String extension = "." + filenameSplit[filenameSplit.length - 1];
|
String extension = "." + filenameSplit[filenameSplit.length - 1];
|
||||||
|
|
@ -84,12 +118,24 @@ class PickedFiles implements Constants {
|
||||||
}).run();
|
}).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* singleFileList.
|
||||||
|
* converts a single uploadableFile to list of uploadableFile.
|
||||||
|
* @param file uploadable file
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
static List<UploadableFile> singleFileList(UploadableFile file) {
|
static List<UploadableFile> singleFileList(UploadableFile file) {
|
||||||
List<UploadableFile> list = new ArrayList<>();
|
List<UploadableFile> list = new ArrayList<>();
|
||||||
list.add(file);
|
list.add(file);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ScanCopiedImages
|
||||||
|
* Scan copied images metadata using media scanner.
|
||||||
|
* @param context
|
||||||
|
* @param copiedImages copied images list.
|
||||||
|
*/
|
||||||
static void scanCopiedImages(Context context, List<File> copiedImages) {
|
static void scanCopiedImages(Context context, List<File> copiedImages) {
|
||||||
String[] paths = new String[copiedImages.size()];
|
String[] paths = new String[copiedImages.size()];
|
||||||
for (int i = 0; i < copiedImages.size(); i++) {
|
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);
|
InputStream pictureInputStream = context.getContentResolver().openInputStream(photoUri);
|
||||||
File directory = tempImageDirectory(context);
|
File directory = tempImageDirectory(context);
|
||||||
File photoFile = new File(directory, UUID.randomUUID().toString() + "." + getMimeType(context, photoUri));
|
File photoFile = new File(directory, UUID.randomUUID().toString() + "." + getMimeType(context, photoUri));
|
||||||
|
|
@ -116,6 +168,9 @@ class PickedFiles implements Constants {
|
||||||
return new UploadableFile(photoUri, photoFile);
|
return new UploadableFile(photoUri, photoFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getCameraPictureLocation
|
||||||
|
*/
|
||||||
static File getCameraPicturesLocation(@NonNull Context context) throws IOException {
|
static File getCameraPicturesLocation(@NonNull Context context) throws IOException {
|
||||||
File dir = tempImageDirectory(context);
|
File dir = tempImageDirectory(context);
|
||||||
return File.createTempFile(UUID.randomUUID().toString(), ".jpg", dir);
|
return File.createTempFile(UUID.randomUUID().toString(), ".jpg", dir);
|
||||||
|
|
@ -142,6 +197,11 @@ class PickedFiles implements Constants {
|
||||||
return extension;
|
return extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetUriToFile
|
||||||
|
* @param file get uri of file
|
||||||
|
* @return uri of requested file.
|
||||||
|
*/
|
||||||
static Uri getUriToFile(@NonNull Context context, @NonNull File file) {
|
static Uri getUriToFile(@NonNull Context context, @NonNull File file) {
|
||||||
String packageName = context.getApplicationContext().getPackageName();
|
String packageName = context.getApplicationContext().getPackageName();
|
||||||
String authority = packageName + ".provider";
|
String authority = packageName + ".provider";
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
||||||
pager.addOnPageChangeListener(this);
|
pager.addOnPageChangeListener(this);
|
||||||
|
|
||||||
adapter = new MediaDetailAdapter(getChildFragmentManager());
|
adapter = new MediaDetailAdapter(getChildFragmentManager());
|
||||||
|
((BaseActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
if (getActivity() != null) {
|
if (getActivity() != null) {
|
||||||
final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
|
final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ class FileProcessor @Inject constructor(
|
||||||
*
|
*
|
||||||
* @return tags to be redacted
|
* @return tags to be redacted
|
||||||
*/
|
*/
|
||||||
private fun getExifTagsToRedact(): Set<String> {
|
fun getExifTagsToRedact(): Set<String> {
|
||||||
val prefManageEXIFTags =
|
val prefManageEXIFTags =
|
||||||
defaultKvStore.getStringSet(Prefs.MANAGED_EXIF_TAGS) ?: emptySet()
|
defaultKvStore.getStringSet(Prefs.MANAGED_EXIF_TAGS) ?: emptySet()
|
||||||
val redactTags: Set<String> =
|
val redactTags: Set<String> =
|
||||||
|
|
@ -91,7 +91,7 @@ class FileProcessor @Inject constructor(
|
||||||
* @param exifInterface ExifInterface object
|
* @param exifInterface ExifInterface object
|
||||||
* @param redactTags tags to be redacted
|
* @param redactTags tags to be redacted
|
||||||
*/
|
*/
|
||||||
private fun redactExifTags(exifInterface: ExifInterface?, redactTags: Set<String>) {
|
fun redactExifTags(exifInterface: ExifInterface?, redactTags: Set<String>) {
|
||||||
compositeDisposable.add(
|
compositeDisposable.add(
|
||||||
Observable.fromIterable(redactTags)
|
Observable.fromIterable(redactTags)
|
||||||
.flatMap { Observable.fromArray(*FileMetadataUtils.getTagsFromPref(it)) }
|
.flatMap { Observable.fromArray(*FileMetadataUtils.getTagsFromPref(it)) }
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,12 @@ public class UploadItem {
|
||||||
private final BehaviorSubject<Integer> imageQuality;
|
private final BehaviorSubject<Integer> imageQuality;
|
||||||
private boolean hasInvalidLocation;
|
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")
|
@SuppressLint("CheckResult")
|
||||||
UploadItem(final Uri mediaUri,
|
UploadItem(final Uri mediaUri,
|
||||||
|
|
@ -31,7 +37,8 @@ public class UploadItem {
|
||||||
final ImageCoordinates gpsCoords,
|
final ImageCoordinates gpsCoords,
|
||||||
final Place place,
|
final Place place,
|
||||||
final long createdTimestamp,
|
final long createdTimestamp,
|
||||||
final String createdTimestampSource) {
|
final String createdTimestampSource,
|
||||||
|
final Uri contentUri) {
|
||||||
this.createdTimestampSource = createdTimestampSource;
|
this.createdTimestampSource = createdTimestampSource;
|
||||||
uploadMediaDetails = new ArrayList<>(Collections.singletonList(new UploadMediaDetail()));
|
uploadMediaDetails = new ArrayList<>(Collections.singletonList(new UploadMediaDetail()));
|
||||||
this.place = place;
|
this.place = place;
|
||||||
|
|
@ -39,6 +46,7 @@ public class UploadItem {
|
||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
this.gpsCoords = gpsCoords;
|
this.gpsCoords = gpsCoords;
|
||||||
this.createdTimestamp = createdTimestamp;
|
this.createdTimestamp = createdTimestamp;
|
||||||
|
this.contentUri = contentUri;
|
||||||
imageQuality = BehaviorSubject.createDefault(ImageUtils.IMAGE_WAIT);
|
imageQuality = BehaviorSubject.createDefault(ImageUtils.IMAGE_WAIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,8 +74,15 @@ public class UploadItem {
|
||||||
return imageQuality.getValue();
|
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) {
|
public void setImageQuality(final int imageQuality) {
|
||||||
this.imageQuality.onNext(imageQuality);
|
this.imageQuality.onNext(imageQuality);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,8 @@ public class UploadModel {
|
||||||
final UploadItem uploadItem = new UploadItem(
|
final UploadItem uploadItem = new UploadItem(
|
||||||
Uri.parse(uploadableFile.getFilePath()),
|
Uri.parse(uploadableFile.getFilePath()),
|
||||||
uploadableFile.getMimeType(context), imageCoordinates, place, fileCreatedDate,
|
uploadableFile.getMimeType(context), imageCoordinates, place, fileCreatedDate,
|
||||||
createdTimestampSource);
|
createdTimestampSource,
|
||||||
|
uploadableFile.getContentUri());
|
||||||
if (place != null) {
|
if (place != null) {
|
||||||
uploadItem.getUploadMediaDetails().set(0, new UploadMediaDetail(place));
|
uploadItem.getUploadMediaDetails().set(0, new UploadMediaDetail(place));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,22 @@ import fr.free.nrw.commons.auth.SessionManager
|
||||||
import fr.free.nrw.commons.contributions.ChunkInfo
|
import fr.free.nrw.commons.contributions.ChunkInfo
|
||||||
import fr.free.nrw.commons.contributions.Contribution
|
import fr.free.nrw.commons.contributions.Contribution
|
||||||
import fr.free.nrw.commons.contributions.ContributionDao
|
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.di.ApplicationlessInjection
|
||||||
import fr.free.nrw.commons.location.LatLng
|
import fr.free.nrw.commons.location.LatLng
|
||||||
import fr.free.nrw.commons.media.MediaClient
|
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.StashUploadState
|
||||||
import fr.free.nrw.commons.upload.UploadClient
|
import fr.free.nrw.commons.upload.UploadClient
|
||||||
import fr.free.nrw.commons.upload.UploadResult
|
import fr.free.nrw.commons.upload.UploadResult
|
||||||
import fr.free.nrw.commons.wikidata.WikidataEditService
|
import fr.free.nrw.commons.wikidata.WikidataEditService
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.MainScope
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
@ -51,12 +56,18 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var contributionDao: ContributionDao
|
lateinit var contributionDao: ContributionDao
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var uploadedStatusDao: UploadedStatusDao
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var uploadClient: UploadClient
|
lateinit var uploadClient: UploadClient
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var mediaClient: MediaClient
|
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_TAG = BuildConfig.APPLICATION_ID + " : upload_tag"
|
||||||
|
|
||||||
private val PROCESSING_UPLOADS_NOTIFICATION_ID = 101
|
private val PROCESSING_UPLOADS_NOTIFICATION_ID = 101
|
||||||
|
|
@ -417,6 +428,29 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
|
||||||
.blockingGet()
|
.blockingGet()
|
||||||
contributionFromUpload.dateModified=Date()
|
contributionFromUpload.dateModified=Date()
|
||||||
contributionDao.deleteAndSaveContribution(contribution, contributionFromUpload)
|
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 {
|
private fun findUniqueFileName(fileName: String): String {
|
||||||
|
|
|
||||||
4
app/src/main/res/drawable-ldpi/circle_shape.xml
Normal file
4
app/src/main/res/drawable-ldpi/circle_shape.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval" >
|
||||||
|
<solid android:color="@color/white" />
|
||||||
|
</shape>
|
||||||
62
app/src/main/res/drawable-ldpi/commons.xml
Normal file
62
app/src/main/res/drawable-ldpi/commons.xml
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="1639.375"
|
||||||
|
android:viewportHeight="1640">
|
||||||
|
<group android:translateX="514.6875"
|
||||||
|
android:translateY="410">
|
||||||
|
<path
|
||||||
|
android:pathData="M305,516m-100,0a100,100 0,1 1,200 0a100,100 0,1 1,-200 0"
|
||||||
|
android:fillColor="#900"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m294,696v118h22v-118"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m262,701l43,-75 43,75"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m169.943,635.501l-83.439,83.439l15.556,15.556l83.439,-83.439"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m143.78,616.409l83.439,-22.627 -22.627,83.439"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m125,505l-118,0l-0,22l118,0"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m120,473l75,43 -75,43"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m185.499,380.943l-83.439,-83.439l-15.556,15.556l83.439,83.439"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m204.591,354.78l22.627,83.439 -83.439,-22.627"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m424.501,651.057l83.439,83.439l15.556,-15.556l-83.439,-83.439"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m405.409,677.22l-22.627,-83.439 83.439,22.627"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m485,527l118,-0l0,-22l-118,-0"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m490,559l-75,-43 75,-43"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m440.057,396.499l83.439,-83.439l-15.556,-15.556l-83.439,83.439"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m466.22,415.591l-83.439,22.627 22.627,-83.439"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M123.981,334.981A256,256 0,1 0,486.019 334.981C415.309,264.27 308.536,300.332 287.322,144.769"
|
||||||
|
android:strokeWidth="84"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#069"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m282,1s-36,135 -80,185 116,-62 170,-5 -90,-180 -90,-180z"
|
||||||
|
android:fillColor="#069"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_arrow_back_black.xml
Normal file
10
app/src/main/res/drawable/ic_arrow_back_black.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/black"
|
||||||
|
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||||
|
</vector>
|
||||||
3
app/src/main/res/drawable/ic_custom_image_picker.xml
Normal file
3
app/src/main/res/drawable/ic_custom_image_picker.xml
Normal file
File diff suppressed because one or more lines are too long
10
app/src/main/res/drawable/ic_done_black.xml
Normal file
10
app/src/main/res/drawable/ic_done_black.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/black"
|
||||||
|
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/ic_done_white.xml
Normal file
5
app/src/main/res/drawable/ic_done_white.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
|
||||||
|
</vector>
|
||||||
19
app/src/main/res/layout/activity_custom_selector.xml
Normal file
19
app/src/main/res/layout/activity_custom_selector.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/custom_selector_toolbar"
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/fragment_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/toolbar_layout"/>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -77,5 +77,4 @@
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
133
app/src/main/res/layout/custom_selector_info_dialog.xml
Normal file
133
app/src/main/res/layout/custom_selector_info_dialog.xml
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_margin="@dimen/dimen_10">
|
||||||
|
<ScrollView
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_width="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="@string/welcome_custom_picture_selector_text"
|
||||||
|
android:textSize="@dimen/normal_text"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_margin="@dimen/dimen_10"
|
||||||
|
android:text="@string/custom_selector_info_text1"
|
||||||
|
android:textSize="@dimen/description_text_size"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_marginHorizontal="@dimen/dimen_10"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/dimen_150"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:padding="@dimen/dimen_2"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
app:cardCornerRadius="@dimen/dimen_6"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:cardElevation="@dimen/dimen_2"
|
||||||
|
android:id="@+id/view"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image_thumbnail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/welcome_image_example"/>
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:padding="@dimen/dimen_2"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:cardCornerRadius="@dimen/dimen_6"
|
||||||
|
app:cardElevation="@dimen/dimen_2"
|
||||||
|
android:id="@+id/view_uploaded"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image_thumbnail_uploaded"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/welcome_image_example"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/uploaded_overlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:alpha="0.15"
|
||||||
|
android:background="@color/black"
|
||||||
|
tools:layout_editor_absoluteX="0dp"
|
||||||
|
tools:layout_editor_absoluteY="203dp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/uploaded_overlay_icon"
|
||||||
|
android:layout_width="@dimen/dimen_72"
|
||||||
|
android:layout_height="@dimen/dimen_72"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:srcCompat="@drawable/commons"
|
||||||
|
/>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="@dimen/dimen_10"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/custom_selector_info_text2"
|
||||||
|
android:textSize="@dimen/description_text_size"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatButton
|
||||||
|
android:id="@+id/btn_ok"
|
||||||
|
android:layout_marginHorizontal="@dimen/dimen_40"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:text="@string/welcome_custom_selector_ok"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
56
app/src/main/res/layout/custom_selector_toolbar.xml
Normal file
56
app/src/main/res/layout/custom_selector_toolbar.xml
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/toolbar_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/back"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="@dimen/standard_gap"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
app:srcCompat="?attr/custom_selector_back"
|
||||||
|
android:contentDescription="@string/back" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/back"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/done"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:text="@string/custom_selector_title"
|
||||||
|
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/done"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/standard_gap"
|
||||||
|
app:srcCompat="?attr/custom_selector_done"
|
||||||
|
android:visibility="invisible"
|
||||||
|
android:contentDescription="@string/done" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</merge>
|
||||||
|
|
@ -69,6 +69,19 @@
|
||||||
app:fabSize="mini"
|
app:fabSize="mini"
|
||||||
app:srcCompat="@drawable/ic_photo_white_24dp" />
|
app:srcCompat="@drawable/ic_photo_white_24dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fab_custom_gallery"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:tint="@color/button_blue"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:backgroundTint="@color/main_background_light"
|
||||||
|
app:useCompatPadding="true"
|
||||||
|
app:elevation="@dimen/tiny_margin"
|
||||||
|
app:fabSize="mini"
|
||||||
|
app:srcCompat="@drawable/ic_custom_image_picker"
|
||||||
|
android:background="@drawable/commons"/>
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/fab_plus"
|
android:id="@+id/fab_plus"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
||||||
41
app/src/main/res/layout/fragment_custom_selector.xml
Normal file
41
app/src/main/res/layout/fragment_custom_selector.xml
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/selector_rv"
|
||||||
|
android:background="?attr/mainBackground"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_text"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:textSize="@dimen/normal_text"
|
||||||
|
android:padding="@dimen/standard_gap"
|
||||||
|
android:text="@string/custom_selector_empty_text"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"/>
|
||||||
|
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/loader"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
77
app/src/main/res/layout/item_custom_selector_folder.xml
Normal file
77
app/src/main/res/layout/item_custom_selector_folder.xml
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:padding="@dimen/dimen_2"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:cardCornerRadius="@dimen/dimen_6"
|
||||||
|
app:cardElevation="@dimen/dimen_2"
|
||||||
|
android:id="@+id/view"
|
||||||
|
app:layout_constraintDimensionRatio="H,1:1"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/folder_thumbnail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/album_overlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:alpha="0.15" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/folder_details"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="#4D000000"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/folder_name"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="@dimen/dimen_6"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:padding="@dimen/dimen_6"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/folder_count"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/v_album"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:constraint_referenced_ids="folder_details,album_overlay" />
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
88
app/src/main/res/layout/item_custom_selector_image.xml
Normal file
88
app/src/main/res/layout/item_custom_selector_image.xml
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:padding="@dimen/dimen_2"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
app:layout_constraintDimensionRatio="H,1:1"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:cardCornerRadius="@dimen/dimen_6"
|
||||||
|
app:cardElevation="@dimen/dimen_2"
|
||||||
|
android:id="@+id/view"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/image_thumbnail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/selected_overlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:alpha="0.25"
|
||||||
|
android:background="@color/divider_grey"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/selected_count"
|
||||||
|
android:layout_width="@dimen/dimen_20"
|
||||||
|
android:layout_height="@dimen/dimen_20"
|
||||||
|
app:layout_constraintDimensionRatio="H,1:1"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:layout_margin="@dimen/dimen_6"
|
||||||
|
android:gravity="center|center_vertical"
|
||||||
|
style="@style/TextAppearance.AppCompat.Small"
|
||||||
|
android:text="12"
|
||||||
|
android:background="@drawable/circle_shape"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/selected_group"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:constraint_referenced_ids="selected_overlay,selected_count"/>
|
||||||
|
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/uploaded_overlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:alpha="0.15"
|
||||||
|
android:background="@color/black"/>
|
||||||
|
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/uploaded_overlay_icon"
|
||||||
|
android:layout_width="@dimen/dimen_72"
|
||||||
|
android:layout_height="@dimen/dimen_72"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:srcCompat="@drawable/commons"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/uploaded_group"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:constraint_referenced_ids="uploaded_overlay,uploaded_overlay_icon"/>
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -49,6 +49,8 @@
|
||||||
<attr name="menu_item_tint" format="reference"/>
|
<attr name="menu_item_tint" format="reference"/>
|
||||||
<attr name="search_icon" format="reference"/>
|
<attr name="search_icon" format="reference"/>
|
||||||
<attr name="caption_description_text_color" format="reference" />
|
<attr name="caption_description_text_color" format="reference" />
|
||||||
|
<attr name="custom_selector_done" format="reference"/>
|
||||||
|
<attr name="custom_selector_back" format="reference"/>
|
||||||
|
|
||||||
<declare-styleable name="Badge">
|
<declare-styleable name="Badge">
|
||||||
<attr name="boundary" format="color"/>
|
<attr name="boundary" format="color"/>
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@
|
||||||
|
|
||||||
<!-- Commonly used dimensions -->
|
<!-- Commonly used dimensions -->
|
||||||
<dimen name="dimen_0">0dp</dimen>
|
<dimen name="dimen_0">0dp</dimen>
|
||||||
|
<dimen name="dimen_2">2dp</dimen>
|
||||||
<dimen name="dimen_6">6dp</dimen>
|
<dimen name="dimen_6">6dp</dimen>
|
||||||
<dimen name="dimen_10">10dp</dimen>
|
<dimen name="dimen_10">10dp</dimen>
|
||||||
<dimen name="dimen_20">20dp</dimen>
|
<dimen name="dimen_20">20dp</dimen>
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@
|
||||||
<string name="title_activity_settings">Settings</string>
|
<string name="title_activity_settings">Settings</string>
|
||||||
<string name="title_activity_signup">Sign Up</string>
|
<string name="title_activity_signup">Sign Up</string>
|
||||||
<string name="title_activity_featured_images">Featured Images</string>
|
<string name="title_activity_featured_images">Featured Images</string>
|
||||||
|
<string name="title_activity_custom_selector">Custom Selector</string>
|
||||||
<string name="title_activity_category_details">Category</string>
|
<string name="title_activity_category_details">Category</string>
|
||||||
<string name="title_activity_review">Peer Review</string>
|
<string name="title_activity_review">Peer Review</string>
|
||||||
<string name="menu_about">About</string>
|
<string name="menu_about">About</string>
|
||||||
|
|
@ -639,6 +640,15 @@ Upload your first media by tapping on the add button.</string>
|
||||||
The shadow of the image view of the location picker</string>
|
The shadow of the image view of the location picker</string>
|
||||||
<string name="image_location">Image Location</string>
|
<string name="image_location">Image Location</string>
|
||||||
<string name="check_whether_location_is_correct">Check whether location is correct</string>
|
<string name="check_whether_location_is_correct">Check whether location is correct</string>
|
||||||
|
<string name="custom_selector_title">Custom Selector</string>
|
||||||
|
<string name="custom_selector_empty_text">No Images</string>
|
||||||
|
<string name="done">Done</string>
|
||||||
|
<string name="back">Back</string>
|
||||||
|
<string name="welcome_custom_picture_selector_text">Welcome to Custom Picture Selector</string>
|
||||||
|
<string name="custom_selector_info_text1">This picker shows differently pictures that are already to Commons.</string>
|
||||||
|
<string name="custom_selector_info_text2">Unlike the picture on the left, the picture on the right has the Commons logo indicating it is already uploaded.</string>
|
||||||
|
<string name="welcome_custom_selector_ok">Awesome</string>
|
||||||
|
<string name="custom_selector_already_uploaded_image_text">This image has already been uploaded to Commons.</string>
|
||||||
<string name="place_state_wlm">WLM</string>
|
<string name="place_state_wlm">WLM</string>
|
||||||
<string name="wlm_upload_info">You are contributing to Wiki Loves Monuments Campaign. Related templates will be added accordingly.</string>
|
<string name="wlm_upload_info">You are contributing to Wiki Loves Monuments Campaign. Related templates will be added accordingly.</string>
|
||||||
<string name="display_monuments">Display monuments</string>
|
<string name="display_monuments">Display monuments</string>
|
||||||
|
|
@ -646,5 +656,4 @@ Upload your first media by tapping on the add button.</string>
|
||||||
<string name="learn_more">LEARN MORE</string>
|
<string name="learn_more">LEARN MORE</string>
|
||||||
<string name="wlm_campaign_title">Wiki Loves Monuments</string>
|
<string name="wlm_campaign_title">Wiki Loves Monuments</string>
|
||||||
<string name="wlm_campaign_description">Wiki Loves Monuments is an international photo contest for monuments organised by Wikimedia</string>
|
<string name="wlm_campaign_description">Wiki Loves Monuments is an international photo contest for monuments organised by Wikimedia</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@
|
||||||
<item name="contributionsListTextSecondary">@color/white</item>
|
<item name="contributionsListTextSecondary">@color/white</item>
|
||||||
<item name="menu_item_tint">@color/white</item>
|
<item name="menu_item_tint">@color/white</item>
|
||||||
<item name="search_icon">@drawable/ic_search_white_24dp</item>
|
<item name="search_icon">@drawable/ic_search_white_24dp</item>
|
||||||
|
<item name="custom_selector_done">@drawable/ic_done_white</item>
|
||||||
|
<item name="custom_selector_back">@drawable/ic_arrow_back_white</item>
|
||||||
<item name="android:windowEnableSplitTouch">false</item>
|
<item name="android:windowEnableSplitTouch">false</item>
|
||||||
<item name="android:splitMotionEvents">false</item>
|
<item name="android:splitMotionEvents">false</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -113,6 +115,8 @@
|
||||||
<item name="contributionsListTextSecondary">@color/disabled_button_text_color_dark</item>
|
<item name="contributionsListTextSecondary">@color/disabled_button_text_color_dark</item>
|
||||||
<item name="menu_item_tint">@color/primaryDarkColor</item>
|
<item name="menu_item_tint">@color/primaryDarkColor</item>
|
||||||
<item name="search_icon">@drawable/ic_search_blue_24dp</item>
|
<item name="search_icon">@drawable/ic_search_blue_24dp</item>
|
||||||
|
<item name="custom_selector_done">@drawable/ic_done_black</item>
|
||||||
|
<item name="custom_selector_back">@drawable/ic_arrow_back_black</item>
|
||||||
<item name="android:windowEnableSplitTouch">false</item>
|
<item name="android:windowEnableSplitTouch">false</item>
|
||||||
<item name="android:splitMotionEvents">false</item>
|
<item name="android:splitMotionEvents">false</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -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<Image>(listOf(folderImage1, folderImage2))
|
||||||
|
private val folderImageList1 = ArrayList<Image>(listOf(folderImage1))
|
||||||
|
private val folderImageList2 = ArrayList<Image>(listOf(folderImage2))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test folder list from images.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun folderListFromImages() {
|
||||||
|
val folderList = ArrayList<Folder>(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<Int>(listOf(0)), ImageHelper.getIndexList(mockImageList, folderImageList2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Folder>
|
||||||
|
|
||||||
|
@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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Image>
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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<String>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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<Boolean>
|
||||||
|
|
||||||
|
@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<Uri, String> = HashMap()
|
||||||
|
private var mapHolderImage : HashMap<ImageAdapter.ImageViewHolder, Image> = HashMap()
|
||||||
|
private var mapResult: HashMap<String, ImageLoader.Result> = HashMap()
|
||||||
|
private var mapModifiedImageSHA1: HashMap<Image, String> = 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -54,4 +54,14 @@ class FilePickerTest {
|
||||||
verify(activity).startActivityForResult(ArgumentMatchers.anyObject(), requestCodeCaptor?.capture()?.toInt()!!)
|
verify(activity).startActivityForResult(ArgumentMatchers.anyObject(), requestCodeCaptor?.capture()?.toInt()!!)
|
||||||
assertEquals(requestCodeCaptor?.value, RequestCodes.TAKE_PICTURE)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
0
app/src/test/resources/imageLoaderTestFile
Normal file
0
app/src/test/resources/imageLoaderTestFile
Normal file
Loading…
Add table
Add a link
Reference in a new issue