GSoC 2021 custom picture selector (#4534)

* Initialised xmls, made folder and image item.

* xmls done

* xmls completed

* removed unwanted attribute

* Created models, adapters and view models (#4441)

* created models, adapters and view models

* Added Image Fragment

* back button linked

* Documentation and refractor

* spaces

* Butterknife annotation

* DiffUtil

* Added Examples

* Extended Custom selector From Base Activity

* made view model injectable

* [GSOC] Added Image Fetch (#4449)

* Added basic Fetch

* added permission request

* Folder count rectified

* Loaded thumbnail

* disabled overlay

* Added sha1 function

* Documented the code

* [GSoC] Image Selection (#4457)

* Localisation updates from https://translatewiki.net.

* Fixes #4357  After switching to different account, contributions screen shows pictures of previous account (#4421)

* Update UploadMediaDetailFragment.java

* Update LoginActivity.java

Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account

* Revert "Update UploadMediaDetailFragment.java"

This reverts commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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 commit b1b4257f20.

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:
Nicolas Raoul 2021-08-18 21:11:35 +09:00 committed by GitHub
parent 6588a6fd0e
commit c6f4649fcf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
68 changed files with 3854 additions and 35 deletions

View file

@ -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'

View file

@ -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"

View file

@ -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
) )
/** /**

View file

@ -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
*/ */

View file

@ -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;
} }
} }

View file

@ -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)
}
}

View file

@ -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
)

View file

@ -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
}
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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()
}

View file

@ -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
}
}

View file

@ -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)
}
}
}

View file

@ -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>) {
}

View file

@ -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))
}
}
}

View file

@ -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])
}
}
}

View file

@ -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)
}

View file

@ -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"
}
}

View file

@ -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()
}
}

View file

@ -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
}
}

View file

@ -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.
}
}

View file

@ -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).
*
*/
}

View file

@ -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()
}
}

View file

@ -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
}
}

View file

@ -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";

View file

@ -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;
} }

View file

@ -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();

View file

@ -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();

View file

@ -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();

View file

@ -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,

View file

@ -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);

View file

@ -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 {

View file

@ -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";

View file

@ -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();

View file

@ -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)) }

View file

@ -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);
} }
/** /**

View file

@ -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));
} }

View file

@ -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 {

View file

@ -0,0 +1,4 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<solid android:color="@color/white" />
</shape>

View 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>

View 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>

File diff suppressed because one or more lines are too long

View 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>

View 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>

View 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>

View file

@ -77,5 +77,4 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View 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>

View 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>

View file

@ -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"

View 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>

View 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>

View 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>

View file

@ -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"/>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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))
}
}

View file

@ -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)
}
}

View file

@ -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))
}
}

View file

@ -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)
}
}

View file

@ -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);
}
}

View file

@ -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)
}
}

View file

@ -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);
}
}

View file

@ -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)
}
}

View file

@ -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()
}
}

View file

@ -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)
}
} }