mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-29 22:03:55 +01:00
Issue-5662-kotlinstyle (#5833)
* *.kt: bulk correction of formatting using ktlint --format * *.kt: replace wildcard imports and second stage auto format ktlint --format * QuizQuestionTest.kt: modified property names to camel case to meet ktlint standard * LevelControllerTest.kt: modified property names to camel case to meet ktlint standard * QuizActivityUnitTest.kt: modified property names to camel case to meet ktlint standard * MediaDetailFragmentUnitTests.kt: modified property names to camel case to meet ktlint standard * UploadWorker.kt: modified property names to camel case to meet ktlint standard * UploadClient.kt: modified property names to camel case to meet ktlint standard * BasePagingPresenter.kt: modified property names to camel case to meet ktlint standard * DescriptionEditActivity.kt: modified property names to camel case to meet ktlint standard * OnSwipeTouchListener.kt: modified property names to camel case to meet ktlint standard * MediaDetailFragmentUnitTests.kt: corrected excessive line length to meet ktlint standard * DepictedItem.kt: corrected property name format and catch format to for ktlint standard * UploadCategoryAdapter.kt: corrected class definition format to meet ktlint standard * CustomSelectorActivity.kt: reformatted function names to first letter lowercase to meet ktlint standard * MediaDetailFragmentUnitTests.kt: fix string literal indentation to meet ktlint standard * NotForUploadDao.kt: file renamed to match class name, new file NotForUploadStatusDao.kt * UploadedDao.kt: file renamed to match class name, new file UploadedStatusDao.kt * Urls.kt: fixed excessive line length for ktLint standard * Snak_partial.kt & Statement_partial.kt: refactored to remove underscores in class names to meet ktLint standard * *.kt: fixed consecutive KDOC error for ktLint * PageableBaseDataSourceTest.kt & UploadPresenterTest.kt: fixed excessive line lengths to meet ktLint standard * CheckboxTriStatesTest.kt: renamed file to match class name to meet ktLint standard * .kt: resolved backing-property-naming error in ktLint, made matching properties public, matched names and refactored * TestConnectionFactory.kt: fixed property naming to adhere to ktLint standard
This commit is contained in:
parent
950539c55c
commit
2d82a430c4
405 changed files with 11032 additions and 9137 deletions
|
|
@ -1,4 +1,6 @@
|
|||
package fr.free.nrw.commons.media
|
||||
|
||||
data class IdAndCaptions(val id: String, val captions: Map<String, String>)
|
||||
|
||||
data class IdAndCaptions(
|
||||
val id: String,
|
||||
val captions: Map<String, String>,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import fr.free.nrw.commons.category.ContinuationClient
|
|||
import fr.free.nrw.commons.explore.media.MediaConverter
|
||||
import fr.free.nrw.commons.utils.CommonsDateUtil
|
||||
import fr.free.nrw.commons.wikidata.model.Entities
|
||||
import io.reactivex.Single
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse
|
||||
import java.util.*
|
||||
import io.reactivex.Single
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -22,199 +22,200 @@ const val RADIUS = 10000
|
|||
* Media Client to handle custom calls to Commons MediaWiki APIs
|
||||
*/
|
||||
@Singleton
|
||||
class MediaClient @Inject constructor(
|
||||
private val mediaInterface: MediaInterface,
|
||||
private val pageMediaInterface: PageMediaInterface,
|
||||
private val mediaDetailInterface: MediaDetailInterface,
|
||||
private val mediaConverter: MediaConverter
|
||||
) : ContinuationClient<MwQueryResponse, Media>() {
|
||||
class MediaClient
|
||||
@Inject
|
||||
constructor(
|
||||
private val mediaInterface: MediaInterface,
|
||||
private val pageMediaInterface: PageMediaInterface,
|
||||
private val mediaDetailInterface: MediaDetailInterface,
|
||||
private val mediaConverter: MediaConverter,
|
||||
) : ContinuationClient<MwQueryResponse, Media>() {
|
||||
fun getMediaById(id: String) = responseMapper(mediaInterface.getMediaById(id)).map { it.first() }
|
||||
|
||||
fun getMediaById(id: String) =
|
||||
responseMapper(mediaInterface.getMediaById(id)).map { it.first() }
|
||||
/**
|
||||
* Checks if a page exists on Commons
|
||||
* The same method can be used to check for file or talk page
|
||||
*
|
||||
* @param title File:Test.jpg or Commons:Deletion_requests/File:Test1.jpeg
|
||||
*/
|
||||
fun checkPageExistsUsingTitle(title: String?): Single<Boolean> =
|
||||
mediaInterface
|
||||
.checkPageExistsUsingTitle(title)
|
||||
.map { it.query()!!.firstPage()!!.pageId() > 0 }
|
||||
|
||||
/**
|
||||
* Checks if a page exists on Commons
|
||||
* The same method can be used to check for file or talk page
|
||||
*
|
||||
* @param title File:Test.jpg or Commons:Deletion_requests/File:Test1.jpeg
|
||||
*/
|
||||
fun checkPageExistsUsingTitle(title: String?): Single<Boolean> {
|
||||
return mediaInterface.checkPageExistsUsingTitle(title)
|
||||
.map { it.query()!!.firstPage()!!.pageId() > 0 }
|
||||
}
|
||||
/**
|
||||
* Take the fileSha and returns whether a file with a matching SHA exists or not
|
||||
*
|
||||
* @param fileSha SHA of the file to be checked
|
||||
*/
|
||||
fun checkFileExistsUsingSha(fileSha: String?): Single<Boolean> =
|
||||
mediaInterface
|
||||
.checkFileExistsUsingSha(fileSha)
|
||||
.map { it.query()!!.allImages().size > 0 }
|
||||
|
||||
/**
|
||||
* Take the fileSha and returns whether a file with a matching SHA exists or not
|
||||
*
|
||||
* @param fileSha SHA of the file to be checked
|
||||
*/
|
||||
fun checkFileExistsUsingSha(fileSha: String?): Single<Boolean> {
|
||||
return mediaInterface.checkFileExistsUsingSha(fileSha)
|
||||
.map { it.query()!!.allImages().size > 0 }
|
||||
}
|
||||
/**
|
||||
* This method takes the category as input and returns a list of Media objects filtered using image generator query
|
||||
* It uses the generator query API to get the images searched using a query, 10 at a time.
|
||||
*
|
||||
* @param category the search category. Must start with "Category:"
|
||||
* @return
|
||||
*/
|
||||
fun getMediaListFromCategory(category: String): Single<List<Media>> =
|
||||
continuationRequest(CATEGORY_CONTINUATION_PREFIX, category) {
|
||||
mediaInterface.getMediaListFromCategory(category, 10, it)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes the category as input and returns a list of Media objects filtered using image generator query
|
||||
* It uses the generator query API to get the images searched using a query, 10 at a time.
|
||||
*
|
||||
* @param category the search category. Must start with "Category:"
|
||||
* @return
|
||||
*/
|
||||
fun getMediaListFromCategory(category: String): Single<List<Media>> {
|
||||
return continuationRequest(CATEGORY_CONTINUATION_PREFIX, category) {
|
||||
mediaInterface.getMediaListFromCategory(category, 10, it)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This method takes the userName as input and returns a list of Media objects filtered using
|
||||
* allimages query It uses the allimages query API to get the images contributed by the userName,
|
||||
* 10 at a time.
|
||||
*
|
||||
* @param userName the username
|
||||
* @return
|
||||
*/
|
||||
fun getMediaListForUser(userName: String): Single<List<Media>> =
|
||||
continuationRequest("user_", userName) {
|
||||
mediaInterface.getMediaListForUser(userName, 10, it)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes the userName as input and returns a list of Media objects filtered using
|
||||
* allimages query It uses the allimages query API to get the images contributed by the userName,
|
||||
* 10 at a time.
|
||||
*
|
||||
* @param userName the username
|
||||
* @return
|
||||
*/
|
||||
fun getMediaListForUser(userName: String): Single<List<Media>> {
|
||||
return continuationRequest("user_", userName) {
|
||||
mediaInterface.getMediaListForUser(userName, 10, it)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This method takes a keyword as input and returns a list of Media objects filtered using image generator query
|
||||
* It uses the generator query API to get the images searched using a query, 10 at a time.
|
||||
*
|
||||
* @param keyword the search keyword
|
||||
* @param limit
|
||||
* @param offset
|
||||
* @return
|
||||
*/
|
||||
fun getMediaListFromSearch(
|
||||
keyword: String?,
|
||||
limit: Int,
|
||||
offset: Int,
|
||||
) = responseMapper(mediaInterface.getMediaListFromSearch(keyword, limit, offset))
|
||||
|
||||
/**
|
||||
* This method takes a keyword as input and returns a list of Media objects filtered using image generator query
|
||||
* It uses the generator query API to get the images searched using a query, 10 at a time.
|
||||
*
|
||||
* @param keyword the search keyword
|
||||
* @param limit
|
||||
* @param offset
|
||||
* @return
|
||||
*/
|
||||
fun getMediaListFromSearch(keyword: String?, limit: Int, offset: Int) =
|
||||
responseMapper(mediaInterface.getMediaListFromSearch(keyword, limit, offset))
|
||||
/**
|
||||
* This method takes coordinate as input and returns a list of Media objects.
|
||||
* It uses the generator query API to get the images searched using a query.
|
||||
*
|
||||
* @param coordinate coordinate
|
||||
* @return
|
||||
*/
|
||||
fun getMediaListFromGeoSearch(coordinate: String?) =
|
||||
responseMapper(mediaInterface.getMediaListFromGeoSearch(coordinate, LIMIT, RADIUS))
|
||||
|
||||
/**
|
||||
* This method takes coordinate as input and returns a list of Media objects.
|
||||
* It uses the generator query API to get the images searched using a query.
|
||||
*
|
||||
* @param coordinate coordinate
|
||||
* @return
|
||||
*/
|
||||
fun getMediaListFromGeoSearch(coordinate: String?) =
|
||||
responseMapper(mediaInterface.getMediaListFromGeoSearch(coordinate, LIMIT, RADIUS))
|
||||
|
||||
/**
|
||||
* @return list of images for a particular depict entity
|
||||
*/
|
||||
fun fetchImagesForDepictedItem(
|
||||
query: String,
|
||||
srlimit: Int,
|
||||
sroffset: Int
|
||||
): Single<List<Media>> {
|
||||
return responseMapper(
|
||||
mediaInterface.fetchImagesForDepictedItem(
|
||||
"haswbstatement:" + BuildConfig.DEPICTS_PROPERTY + "=" + query,
|
||||
srlimit.toString(),
|
||||
sroffset.toString()
|
||||
/**
|
||||
* @return list of images for a particular depict entity
|
||||
*/
|
||||
fun fetchImagesForDepictedItem(
|
||||
query: String,
|
||||
srlimit: Int,
|
||||
sroffset: Int,
|
||||
): Single<List<Media>> =
|
||||
responseMapper(
|
||||
mediaInterface.fetchImagesForDepictedItem(
|
||||
"haswbstatement:" + BuildConfig.DEPICTS_PROPERTY + "=" + query,
|
||||
srlimit.toString(),
|
||||
sroffset.toString(),
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches Media object from the imageInfo API
|
||||
*
|
||||
* @param titles the tiles to be searched for. Can be filename or template name
|
||||
* @return
|
||||
*/
|
||||
fun getMedia(titles: String?): Single<Media> {
|
||||
return responseMapper(mediaInterface.getMedia(titles))
|
||||
.map { it.first() }
|
||||
}
|
||||
/**
|
||||
* Fetches Media object from the imageInfo API
|
||||
*
|
||||
* @param titles the tiles to be searched for. Can be filename or template name
|
||||
* @return
|
||||
*/
|
||||
fun getMedia(titles: String?): Single<Media> =
|
||||
responseMapper(mediaInterface.getMedia(titles))
|
||||
.map { it.first() }
|
||||
|
||||
/**
|
||||
* Fetches Media object from the imageInfo API but suppress (known) errors
|
||||
*
|
||||
* @param titles the tiles to be searched for. Can be filename or template name
|
||||
* @return
|
||||
*/
|
||||
fun getMediaSuppressingErrors(titles: String?): Single<Media> {
|
||||
return responseMapper(mediaInterface.getMediaSuppressingErrors(titles))
|
||||
.map { it.first() }
|
||||
}
|
||||
/**
|
||||
* Fetches Media object from the imageInfo API but suppress (known) errors
|
||||
*
|
||||
* @param titles the tiles to be searched for. Can be filename or template name
|
||||
* @return
|
||||
*/
|
||||
fun getMediaSuppressingErrors(titles: String?): Single<Media> =
|
||||
responseMapper(mediaInterface.getMediaSuppressingErrors(titles))
|
||||
.map { it.first() }
|
||||
|
||||
/**
|
||||
* The method returns the picture of the day
|
||||
*
|
||||
* @return Media object corresponding to the picture of the day
|
||||
*/
|
||||
fun getPictureOfTheDay(): Single<Media> {
|
||||
val date = CommonsDateUtil.getIso8601DateFormatShort().format(Date())
|
||||
return responseMapper(mediaInterface.getMediaWithGenerator("Template:Potd/$date")).map { it.first() }
|
||||
}
|
||||
|
||||
fun getPageHtml(title: String?): Single<String> {
|
||||
return mediaInterface.getPageHtml(title)
|
||||
.map { obj: MwParseResponse -> obj.parse()?.text() ?: "" }
|
||||
}
|
||||
|
||||
fun getEntities(entityIds: List<String>): Single<Entities> {
|
||||
return if (entityIds.isEmpty())
|
||||
Single.error(Exception("empty list passed for ids"))
|
||||
else
|
||||
mediaDetailInterface.getEntity(entityIds.joinToString("|"))
|
||||
}
|
||||
|
||||
fun doesPageContainMedia(title: String?): Single<Boolean> {
|
||||
return pageMediaInterface.getMediaList(title)
|
||||
.map { it.items.isNotEmpty() }
|
||||
}
|
||||
|
||||
fun resetCategoryContinuation(category: String) {
|
||||
resetContinuation(CATEGORY_CONTINUATION_PREFIX, category)
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the resetUserContinuation method
|
||||
*
|
||||
* @param userName the username
|
||||
*/
|
||||
fun resetUserNameContinuation(userName: String) =
|
||||
resetUserContinuation("user_", userName)
|
||||
|
||||
/**
|
||||
* Get whole WikiText of required file
|
||||
* @param title : Name of the file
|
||||
* @return Observable<MwQueryResult>
|
||||
*/
|
||||
fun getCurrentWikiText(title: String): Single<String?> {
|
||||
return mediaDetailInterface.getWikiText(title).map {
|
||||
it.query()?.pages()?.get(0)?.revisions()?.get(0)?.content()
|
||||
/**
|
||||
* The method returns the picture of the day
|
||||
*
|
||||
* @return Media object corresponding to the picture of the day
|
||||
*/
|
||||
fun getPictureOfTheDay(): Single<Media> {
|
||||
val date = CommonsDateUtil.getIso8601DateFormatShort().format(Date())
|
||||
return responseMapper(mediaInterface.getMediaWithGenerator("Template:Potd/$date")).map { it.first() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun responseMapper(
|
||||
networkResult: Single<MwQueryResponse>,
|
||||
key: String?
|
||||
): Single<List<Media>> {
|
||||
return networkResult.map {
|
||||
handleContinuationResponse(it.continuation(), key)
|
||||
it.query()?.pages() ?: emptyList()
|
||||
}.flatMap(::mediaFromPageAndEntity)
|
||||
}
|
||||
fun getPageHtml(title: String?): Single<String> =
|
||||
mediaInterface
|
||||
.getPageHtml(title)
|
||||
.map { obj: MwParseResponse -> obj.parse()?.text() ?: "" }
|
||||
|
||||
private fun mediaFromPageAndEntity(pages: List<MwQueryPage>): Single<List<Media>> {
|
||||
return if (pages.isEmpty()) {
|
||||
Single.just(emptyList())
|
||||
} else {
|
||||
getEntities(pages.map { "$PAGE_ID_PREFIX${it.pageId()}" })
|
||||
fun getEntities(entityIds: List<String>): Single<Entities> =
|
||||
if (entityIds.isEmpty()) {
|
||||
Single.error(Exception("empty list passed for ids"))
|
||||
} else {
|
||||
mediaDetailInterface.getEntity(entityIds.joinToString("|"))
|
||||
}
|
||||
|
||||
fun doesPageContainMedia(title: String?): Single<Boolean> =
|
||||
pageMediaInterface
|
||||
.getMediaList(title)
|
||||
.map { it.items.isNotEmpty() }
|
||||
|
||||
fun resetCategoryContinuation(category: String) {
|
||||
resetContinuation(CATEGORY_CONTINUATION_PREFIX, category)
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the resetUserContinuation method
|
||||
*
|
||||
* @param userName the username
|
||||
*/
|
||||
fun resetUserNameContinuation(userName: String) = resetUserContinuation("user_", userName)
|
||||
|
||||
/**
|
||||
* Get whole WikiText of required file
|
||||
* @param title : Name of the file
|
||||
* @return Observable<MwQueryResult>
|
||||
*/
|
||||
fun getCurrentWikiText(title: String): Single<String?> =
|
||||
mediaDetailInterface.getWikiText(title).map {
|
||||
it
|
||||
.query()
|
||||
?.pages()
|
||||
?.get(0)
|
||||
?.revisions()
|
||||
?.get(0)
|
||||
?.content()
|
||||
}
|
||||
|
||||
override fun responseMapper(
|
||||
networkResult: Single<MwQueryResponse>,
|
||||
key: String?,
|
||||
): Single<List<Media>> =
|
||||
networkResult
|
||||
.map {
|
||||
pages.zip(it.entities().values)
|
||||
.mapNotNull { (page, entity) ->
|
||||
page.imageInfo()?.let {
|
||||
mediaConverter.convert(page, entity, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handleContinuationResponse(it.continuation(), key)
|
||||
it.query()?.pages() ?: emptyList()
|
||||
}.flatMap(::mediaFromPageAndEntity)
|
||||
|
||||
private fun mediaFromPageAndEntity(pages: List<MwQueryPage>): Single<List<Media>> =
|
||||
if (pages.isEmpty()) {
|
||||
Single.just(emptyList())
|
||||
} else {
|
||||
getEntities(pages.map { "$PAGE_ID_PREFIX${it.pageId()}" })
|
||||
.map {
|
||||
pages
|
||||
.zip(it.entities().values)
|
||||
.mapNotNull { (page, entity) ->
|
||||
page.imageInfo()?.let {
|
||||
mediaConverter.convert(page, entity, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ interface MediaDetailInterface {
|
|||
@GET("w/api.php?action=wbgetentities&props=labels&format=json&languagefallback=1&sites=commonswiki")
|
||||
fun fetchEntitiesByFileName(
|
||||
@Query("languages") language: String?,
|
||||
@Query("titles") filename: String?
|
||||
@Query("titles") filename: String?,
|
||||
): Observable<Entities>
|
||||
|
||||
/**
|
||||
|
|
@ -28,7 +28,9 @@ interface MediaDetailInterface {
|
|||
* @param entityId EntityId (Ex: Q81566) of the depict entity
|
||||
*/
|
||||
@GET("/w/api.php?format=json&action=wbgetentities&props=labels&languagefallback=1")
|
||||
fun getEntity(@Query("ids") entityId: String?): Single<Entities>
|
||||
fun getEntity(
|
||||
@Query("ids") entityId: String?,
|
||||
): Single<Entities>
|
||||
|
||||
/**
|
||||
* Fetches caption using wikibaseIdentifier
|
||||
|
|
@ -38,16 +40,16 @@ interface MediaDetailInterface {
|
|||
@GET("/w/api.php?action=wbgetentities&props=labels&format=json&languagefallback=1&sites=commonswiki")
|
||||
fun getEntityForImage(
|
||||
@Query("languages") language: String?,
|
||||
@Query("ids") wikibaseIdentifier: String?
|
||||
@Query("ids") wikibaseIdentifier: String?,
|
||||
): Observable<Entities>
|
||||
|
||||
/**
|
||||
* Fetches current wikitext
|
||||
* @param title file name
|
||||
* @return Single<MwQueryResponse>
|
||||
</MwQueryResponse> */
|
||||
</MwQueryResponse> */
|
||||
@GET(WikidataConstants.MW_API_PREFIX + "action=query&prop=revisions&rvprop=content|timestamp&rvlimit=1&converttitles=")
|
||||
fun getWikiText(
|
||||
@Query("titles") title: String?
|
||||
@Query("titles") title: String?,
|
||||
): Single<MwQueryResponse>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ interface MediaInterface {
|
|||
* @return
|
||||
*/
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2")
|
||||
fun checkPageExistsUsingTitle(@Query("titles") title: String?): Single<MwQueryResponse>
|
||||
fun checkPageExistsUsingTitle(
|
||||
@Query("titles") title: String?,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
/**
|
||||
* Check if file exists
|
||||
|
|
@ -28,7 +30,9 @@ interface MediaInterface {
|
|||
* @return
|
||||
*/
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2&list=allimages")
|
||||
fun checkFileExistsUsingSha(@Query("aisha1") aisha1: String?): Single<MwQueryResponse>
|
||||
fun checkFileExistsUsingSha(
|
||||
@Query("aisha1") aisha1: String?,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
/**
|
||||
* This method retrieves a list of Media objects filtered using image generator query
|
||||
|
|
@ -39,13 +43,13 @@ interface MediaInterface {
|
|||
* @return
|
||||
*/
|
||||
@GET(
|
||||
"w/api.php?action=query&format=json&formatversion=2" + //Basic parameters
|
||||
"&generator=categorymembers&gcmtype=file&gcmsort=timestamp&gcmdir=desc$MEDIA_PARAMS" //Category parameters
|
||||
"w/api.php?action=query&format=json&formatversion=2" + // Basic parameters
|
||||
"&generator=categorymembers&gcmtype=file&gcmsort=timestamp&gcmdir=desc$MEDIA_PARAMS", // Category parameters
|
||||
)
|
||||
fun getMediaListFromCategory(
|
||||
@Query("gcmtitle") category: String?,
|
||||
@Query("gcmlimit") itemLimit: Int,
|
||||
@QueryMap continuation: Map<String, String>
|
||||
@QueryMap continuation: Map<String, String>,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
/**
|
||||
|
|
@ -57,13 +61,13 @@ interface MediaInterface {
|
|||
* @return
|
||||
*/
|
||||
@GET(
|
||||
"w/api.php?action=query&format=json&formatversion=2" + //Basic parameters
|
||||
"&generator=allimages&gaisort=timestamp&gaidir=older$MEDIA_PARAMS"
|
||||
"w/api.php?action=query&format=json&formatversion=2" + // Basic parameters
|
||||
"&generator=allimages&gaisort=timestamp&gaidir=older$MEDIA_PARAMS",
|
||||
)
|
||||
fun getMediaListForUser(
|
||||
@Query("gaiuser") username: String?,
|
||||
@Query("gailimit") itemLimit: Int,
|
||||
@QueryMap(encoded = true) continuation: Map<String, String>
|
||||
@QueryMap(encoded = true) continuation: Map<String, String>,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
/**
|
||||
|
|
@ -75,12 +79,13 @@ interface MediaInterface {
|
|||
* @return
|
||||
*/
|
||||
@GET(
|
||||
"w/api.php?action=query&format=json&formatversion=2" + //Basic parameters
|
||||
"&generator=search&gsrwhat=text&gsrnamespace=6$MEDIA_PARAMS" //Search parameters
|
||||
"w/api.php?action=query&format=json&formatversion=2" + // Basic parameters
|
||||
"&generator=search&gsrwhat=text&gsrnamespace=6$MEDIA_PARAMS", // Search parameters
|
||||
)
|
||||
fun getMediaListFromSearch(
|
||||
@Query("gsrsearch") keyword: String?,
|
||||
@Query("gsrlimit") itemLimit: Int, @Query("gsroffset") offset: Int
|
||||
@Query("gsrlimit") itemLimit: Int,
|
||||
@Query("gsroffset") offset: Int,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
/**
|
||||
|
|
@ -91,13 +96,13 @@ interface MediaInterface {
|
|||
* @return
|
||||
*/
|
||||
@GET(
|
||||
"w/api.php?action=query&format=json&formatversion=2" + //Basic parameters
|
||||
"&generator=geosearch&ggsnamespace=6$MEDIA_PARAMS" //Search parameters
|
||||
"w/api.php?action=query&format=json&formatversion=2" + // Basic parameters
|
||||
"&generator=geosearch&ggsnamespace=6$MEDIA_PARAMS", // Search parameters
|
||||
)
|
||||
fun getMediaListFromGeoSearch(
|
||||
@Query("ggscoord") location: String?,
|
||||
@Query("ggslimit") itemLimit: Int,
|
||||
@Query("ggsradius") radius: Int
|
||||
@Query("ggsradius") radius: Int,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
/**
|
||||
|
|
@ -107,7 +112,9 @@ interface MediaInterface {
|
|||
* @return
|
||||
*/
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2$MEDIA_PARAMS_WITH_CATEGORY_DETAILS")
|
||||
fun getMedia(@Query("titles") title: String?): Single<MwQueryResponse>
|
||||
fun getMedia(
|
||||
@Query("titles") title: String?,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
/**
|
||||
* Fetches Media object from the imageInfo API but suppress (known) errors
|
||||
|
|
@ -117,7 +124,9 @@ interface MediaInterface {
|
|||
*/
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2$MEDIA_PARAMS_WITH_CATEGORY_DETAILS")
|
||||
@Headers(SUPPRESS_ERROR_LOG_HEADER)
|
||||
fun getMediaSuppressingErrors(@Query("titles") title: String?): Single<MwQueryResponse>
|
||||
fun getMediaSuppressingErrors(
|
||||
@Query("titles") title: String?,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
/**
|
||||
* Fetches Media object from the imageInfo API
|
||||
|
|
@ -127,7 +136,9 @@ interface MediaInterface {
|
|||
*/
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2$MEDIA_PARAMS")
|
||||
@Headers(SUPPRESS_ERROR_LOG_HEADER)
|
||||
fun getMediaById(@Query("pageids") pageIds: String?): Single<MwQueryResponse>
|
||||
fun getMediaById(
|
||||
@Query("pageids") pageIds: String?,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
/**
|
||||
* Fetches Media object from the imageInfo API
|
||||
|
|
@ -137,11 +148,15 @@ interface MediaInterface {
|
|||
* @return
|
||||
*/
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2&generator=images$MEDIA_PARAMS")
|
||||
fun getMediaWithGenerator(@Query("titles") title: String?): Single<MwQueryResponse>
|
||||
fun getMediaWithGenerator(
|
||||
@Query("titles") title: String?,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
@GET("w/api.php?format=json&action=parse&prop=text")
|
||||
@Headers(SUPPRESS_ERROR_LOG_HEADER)
|
||||
fun getPageHtml(@Query("page") title: String?): Single<MwParseResponse>
|
||||
fun getPageHtml(
|
||||
@Query("page") title: String?,
|
||||
): Single<MwParseResponse>
|
||||
|
||||
/**
|
||||
* Fetches caption using file name
|
||||
|
|
@ -151,7 +166,7 @@ interface MediaInterface {
|
|||
@GET("w/api.php?action=wbgetentities&props=labels&format=json&languagefallback=1")
|
||||
fun fetchCaptionByFilename(
|
||||
@Query("language") language: String?,
|
||||
@Query("titles") filename: String?
|
||||
@Query("titles") filename: String?,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
/**
|
||||
|
|
@ -161,12 +176,13 @@ interface MediaInterface {
|
|||
* @param sroffset number od depictions already fetched, this is useful in implementing pagination
|
||||
*/
|
||||
@GET(
|
||||
"w/api.php?action=query&format=json&formatversion=2" + //Basic parameters
|
||||
"&generator=search&gsrnamespace=6$MEDIA_PARAMS" //Search parameters
|
||||
"w/api.php?action=query&format=json&formatversion=2" + // Basic parameters
|
||||
"&generator=search&gsrnamespace=6$MEDIA_PARAMS", // Search parameters
|
||||
)
|
||||
fun fetchImagesForDepictedItem(
|
||||
@Query("gsrsearch") query: String?,
|
||||
@Query("gsrlimit") srlimit: String?, @Query("gsroffset") sroffset: String?
|
||||
@Query("gsrlimit") srlimit: String?,
|
||||
@Query("gsroffset") sroffset: String?,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -15,5 +15,7 @@ interface PageMediaInterface {
|
|||
* @param title the title of the page
|
||||
*/
|
||||
@GET("api/rest_v1/page/media-list/{title}")
|
||||
fun getMediaList(@Path("title") title: String?): Single<PageMediaListResponse>
|
||||
fun getMediaList(
|
||||
@Path("title") title: String?,
|
||||
): Single<PageMediaListResponse>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import fr.free.nrw.commons.Media
|
|||
import fr.free.nrw.commons.category.ContinuationClient
|
||||
import fr.free.nrw.commons.explore.media.MediaConverter
|
||||
import fr.free.nrw.commons.wikidata.model.Entities
|
||||
import io.reactivex.Single
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -15,79 +15,79 @@ import javax.inject.Singleton
|
|||
* Media Client to handle custom calls to Commons MediaWiki APIs of production server
|
||||
*/
|
||||
@Singleton
|
||||
class WikidataMediaClient @Inject constructor(
|
||||
private val wikidataMediaInterface: WikidataMediaInterface,
|
||||
private val mediaDetailInterface: MediaDetailInterface,
|
||||
private val mediaConverter: MediaConverter
|
||||
) : ContinuationClient<MwQueryResponse, Media>() {
|
||||
|
||||
/**
|
||||
* Fetch images for depict ID
|
||||
*
|
||||
* @param query depictionEntityId ex. "Q9394"
|
||||
* @param srlimit the number of items to fetch
|
||||
* @param sroffset number of depictions already fetched,
|
||||
* this is useful in implementing pagination
|
||||
* @return list of images for a particular depict ID
|
||||
*/
|
||||
fun fetchImagesForDepictedItem(
|
||||
query: String,
|
||||
srlimit: Int,
|
||||
sroffset: Int
|
||||
): Single<List<Media>> {
|
||||
return responseMapper(
|
||||
wikidataMediaInterface.fetchImagesForDepictedItem(
|
||||
"haswbstatement:" + BetaConstants.DEPICTS_PROPERTY + "=" + query,
|
||||
srlimit.toString(),
|
||||
sroffset.toString()
|
||||
class WikidataMediaClient
|
||||
@Inject
|
||||
constructor(
|
||||
private val wikidataMediaInterface: WikidataMediaInterface,
|
||||
private val mediaDetailInterface: MediaDetailInterface,
|
||||
private val mediaConverter: MediaConverter,
|
||||
) : ContinuationClient<MwQueryResponse, Media>() {
|
||||
/**
|
||||
* Fetch images for depict ID
|
||||
*
|
||||
* @param query depictionEntityId ex. "Q9394"
|
||||
* @param srlimit the number of items to fetch
|
||||
* @param sroffset number of depictions already fetched,
|
||||
* this is useful in implementing pagination
|
||||
* @return list of images for a particular depict ID
|
||||
*/
|
||||
fun fetchImagesForDepictedItem(
|
||||
query: String,
|
||||
srlimit: Int,
|
||||
sroffset: Int,
|
||||
): Single<List<Media>> =
|
||||
responseMapper(
|
||||
wikidataMediaInterface.fetchImagesForDepictedItem(
|
||||
"haswbstatement:" + BetaConstants.DEPICTS_PROPERTY + "=" + query,
|
||||
srlimit.toString(),
|
||||
sroffset.toString(),
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helps to map to the required data from the API response
|
||||
*
|
||||
* @param networkResult MwQueryResponse
|
||||
* @param key for handling continuation request, this is null in this case
|
||||
*/
|
||||
override fun responseMapper(
|
||||
networkResult: Single<MwQueryResponse>,
|
||||
key: String?
|
||||
): Single<List<Media>> {
|
||||
return networkResult.map {
|
||||
handleContinuationResponse(it.continuation(), key)
|
||||
it.query()?.pages() ?: emptyList()
|
||||
}.flatMap(::mediaFromPageAndEntity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets list of Media from MwQueryPage
|
||||
*/
|
||||
private fun mediaFromPageAndEntity(pages: List<MwQueryPage>): Single<List<Media>> {
|
||||
return if (pages.isEmpty())
|
||||
Single.just(emptyList())
|
||||
else
|
||||
getEntities(pages.map { "$PAGE_ID_PREFIX${it.pageId()}" })
|
||||
/**
|
||||
* Helps to map to the required data from the API response
|
||||
*
|
||||
* @param networkResult MwQueryResponse
|
||||
* @param key for handling continuation request, this is null in this case
|
||||
*/
|
||||
override fun responseMapper(
|
||||
networkResult: Single<MwQueryResponse>,
|
||||
key: String?,
|
||||
): Single<List<Media>> =
|
||||
networkResult
|
||||
.map {
|
||||
pages.zip(it.entities().values)
|
||||
.mapNotNull { (page, entity) ->
|
||||
page.imageInfo()?.let {
|
||||
mediaConverter.convert(page, entity, it)
|
||||
handleContinuationResponse(it.continuation(), key)
|
||||
it.query()?.pages() ?: emptyList()
|
||||
}.flatMap(::mediaFromPageAndEntity)
|
||||
|
||||
/**
|
||||
* Gets list of Media from MwQueryPage
|
||||
*/
|
||||
private fun mediaFromPageAndEntity(pages: List<MwQueryPage>): Single<List<Media>> =
|
||||
if (pages.isEmpty()) {
|
||||
Single.just(emptyList())
|
||||
} else {
|
||||
getEntities(pages.map { "$PAGE_ID_PREFIX${it.pageId()}" })
|
||||
.map {
|
||||
pages
|
||||
.zip(it.entities().values)
|
||||
.mapNotNull { (page, entity) ->
|
||||
page.imageInfo()?.let {
|
||||
mediaConverter.convert(page, entity, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Entities from IDs
|
||||
*
|
||||
* @param entityIds list of IDs of pages/entities ex. {"M4254154", "M11413343"}
|
||||
*/
|
||||
fun getEntities(entityIds: List<String>): Single<Entities> {
|
||||
return if (entityIds.isEmpty())
|
||||
Single.error(Exception("empty list passed for ids"))
|
||||
else
|
||||
mediaDetailInterface.getEntity(entityIds.joinToString("|"))
|
||||
/**
|
||||
* Gets Entities from IDs
|
||||
*
|
||||
* @param entityIds list of IDs of pages/entities ex. {"M4254154", "M11413343"}
|
||||
*/
|
||||
fun getEntities(entityIds: List<String>): Single<Entities> =
|
||||
if (entityIds.isEmpty()) {
|
||||
Single.error(Exception("empty list passed for ids"))
|
||||
} else {
|
||||
mediaDetailInterface.getEntity(entityIds.joinToString("|"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,14 @@ interface WikidataMediaInterface {
|
|||
* @param sroffset number of depictions already fetched,
|
||||
* this is useful in implementing pagination
|
||||
* @return Single<MwQueryResponse>
|
||||
</MwQueryResponse> */
|
||||
</MwQueryResponse> */
|
||||
@GET(
|
||||
"w/api.php?action=query&format=json&formatversion=2" + //Basic parameters
|
||||
"&generator=search&gsrnamespace=6$MEDIA_PARAMS" //Search parameters
|
||||
"w/api.php?action=query&format=json&formatversion=2" + // Basic parameters
|
||||
"&generator=search&gsrnamespace=6$MEDIA_PARAMS", // Search parameters
|
||||
)
|
||||
fun fetchImagesForDepictedItem(
|
||||
@Query("gsrsearch") query: String?,
|
||||
@Query("gsrlimit") srlimit: String?, @Query("gsroffset") sroffset: String?
|
||||
@Query("gsrlimit") srlimit: String?,
|
||||
@Query("gsroffset") sroffset: String?,
|
||||
): Single<MwQueryResponse>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,7 @@ import android.os.Bundle
|
|||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.Button
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.drawee.controller.BaseControllerListener
|
||||
|
|
@ -38,12 +35,16 @@ import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorViewModel
|
|||
import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorViewModelFactory
|
||||
import fr.free.nrw.commons.databinding.ActivityZoomableBinding
|
||||
import fr.free.nrw.commons.media.zoomControllers.zoomable.DoubleTapGestureListener
|
||||
import fr.free.nrw.commons.media.zoomControllers.zoomable.ZoomableDraweeView
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.upload.FileProcessor
|
||||
import fr.free.nrw.commons.upload.FileUtilsWrapper
|
||||
import fr.free.nrw.commons.utils.CustomSelectorUtils
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.ArrayList
|
||||
|
|
@ -53,7 +54,6 @@ import kotlin.collections.ArrayList
|
|||
* like zoom, and swap gestures
|
||||
*/
|
||||
class ZoomableActivity : BaseActivity() {
|
||||
|
||||
private lateinit var imageUri: Uri
|
||||
|
||||
/**
|
||||
|
|
@ -67,7 +67,7 @@ class ZoomableActivity : BaseActivity() {
|
|||
private lateinit var prefs: SharedPreferences
|
||||
|
||||
private lateinit var binding: ActivityZoomableBinding
|
||||
|
||||
|
||||
var photoBackgroundColor: Int? = null
|
||||
|
||||
/**
|
||||
|
|
@ -126,36 +126,39 @@ class ZoomableActivity : BaseActivity() {
|
|||
lateinit var customSelectorViewModelFactory: CustomSelectorViewModelFactory
|
||||
|
||||
/**
|
||||
* Coroutine Dispatchers and Scope.
|
||||
*/
|
||||
private var defaultDispatcher : CoroutineDispatcher = Dispatchers.Default
|
||||
private var ioDispatcher : CoroutineDispatcher = Dispatchers.IO
|
||||
private val scope : CoroutineScope = MainScope()
|
||||
* Coroutine Dispatchers and Scope.
|
||||
*/
|
||||
private var defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
|
||||
private var ioDispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
private val scope: CoroutineScope = MainScope()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityZoomableBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
prefs = applicationContext.getSharedPreferences(
|
||||
ImageHelper.CUSTOM_SELECTOR_PREFERENCE_KEY,
|
||||
MODE_PRIVATE
|
||||
)
|
||||
prefs =
|
||||
applicationContext.getSharedPreferences(
|
||||
ImageHelper.CUSTOM_SELECTOR_PREFERENCE_KEY,
|
||||
MODE_PRIVATE,
|
||||
)
|
||||
|
||||
selectedImages = intent.getParcelableArrayListExtra(
|
||||
CustomSelectorConstants.TOTAL_SELECTED_IMAGES
|
||||
)
|
||||
selectedImages =
|
||||
intent.getParcelableArrayListExtra(
|
||||
CustomSelectorConstants.TOTAL_SELECTED_IMAGES,
|
||||
)
|
||||
position = intent.getIntExtra(CustomSelectorConstants.PRESENT_POSITION, 0)
|
||||
bucketId = intent.getLongExtra(CustomSelectorConstants.BUCKET_ID, 0L)
|
||||
viewModel = ViewModelProvider(this, customSelectorViewModelFactory).get(
|
||||
CustomSelectorViewModel::class.java
|
||||
)
|
||||
viewModel =
|
||||
ViewModelProvider(this, customSelectorViewModelFactory).get(
|
||||
CustomSelectorViewModel::class.java,
|
||||
)
|
||||
viewModel.fetchImages()
|
||||
viewModel.result.observe(this) {
|
||||
handleResult(it)
|
||||
}
|
||||
|
||||
val origin = intent.getStringExtra(ZoomableActivityConstants.ORIGIN);
|
||||
val origin = intent.getStringExtra(ZoomableActivityConstants.ORIGIN)
|
||||
|
||||
/**
|
||||
* If origin is "null" it means that ZoomableActivity was created by the custom picker
|
||||
|
|
@ -166,15 +169,20 @@ class ZoomableActivity : BaseActivity() {
|
|||
if (prefs.getBoolean(CustomSelectorConstants.FULL_SCREEN_MODE_FIRST_LUNCH, true)) {
|
||||
// show welcome dialog on first launch
|
||||
showWelcomeDialog()
|
||||
prefs.edit().putBoolean(
|
||||
CustomSelectorConstants.FULL_SCREEN_MODE_FIRST_LUNCH,
|
||||
false
|
||||
).apply()
|
||||
prefs
|
||||
.edit()
|
||||
.putBoolean(
|
||||
CustomSelectorConstants.FULL_SCREEN_MODE_FIRST_LUNCH,
|
||||
false,
|
||||
).apply()
|
||||
}
|
||||
}
|
||||
|
||||
val backgroundColor = intent.getIntExtra(ZoomableActivityConstants.PHOTO_BACKGROUND_COLOR,
|
||||
MediaDetailFragment.DEFAULT_IMAGE_BACKGROUND_COLOR);
|
||||
|
||||
val backgroundColor =
|
||||
intent.getIntExtra(
|
||||
ZoomableActivityConstants.PHOTO_BACKGROUND_COLOR,
|
||||
MediaDetailFragment.DEFAULT_IMAGE_BACKGROUND_COLOR,
|
||||
)
|
||||
|
||||
if (backgroundColor != MediaDetailFragment.DEFAULT_IMAGE_BACKGROUND_COLOR) {
|
||||
photoBackgroundColor = backgroundColor
|
||||
|
|
@ -196,15 +204,16 @@ class ZoomableActivity : BaseActivity() {
|
|||
* Handle view model result.
|
||||
*/
|
||||
private fun handleResult(result: Result) {
|
||||
if(result.status is CallbackStatus.SUCCESS){
|
||||
if (result.status is CallbackStatus.SUCCESS) {
|
||||
val images = result.images
|
||||
if(images.isNotEmpty()) {
|
||||
if (images.isNotEmpty()) {
|
||||
this@ZoomableActivity.images = ImageHelper.filterImages(images, bucketId)
|
||||
imageUri = if (this@ZoomableActivity.images.isNullOrEmpty()) {
|
||||
intent.data as Uri
|
||||
} else {
|
||||
this@ZoomableActivity.images!![position].uri
|
||||
}
|
||||
imageUri =
|
||||
if (this@ZoomableActivity.images.isNullOrEmpty()) {
|
||||
intent.data as Uri
|
||||
} else {
|
||||
this@ZoomableActivity.images!![position].uri
|
||||
}
|
||||
Timber.d("URL = $imageUri")
|
||||
init(imageUri)
|
||||
onSwipe()
|
||||
|
|
@ -225,34 +234,36 @@ class ZoomableActivity : BaseActivity() {
|
|||
sharedPreferences.getBoolean(ImageHelper.SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY, true)
|
||||
|
||||
if (!images.isNullOrEmpty()) {
|
||||
binding.zoomable!!.setOnTouchListener(object : OnSwipeTouchListener(this) {
|
||||
// Swipe left to view next image in the folder. (if available)
|
||||
override fun onSwipeLeft() {
|
||||
super.onSwipeLeft()
|
||||
onLeftSwiped(showAlreadyActionedImages)
|
||||
}
|
||||
binding.zoomable!!.setOnTouchListener(
|
||||
object : OnSwipeTouchListener(this) {
|
||||
// Swipe left to view next image in the folder. (if available)
|
||||
override fun onSwipeLeft() {
|
||||
super.onSwipeLeft()
|
||||
onLeftSwiped(showAlreadyActionedImages)
|
||||
}
|
||||
|
||||
// Swipe right to view previous image in the folder. (if available)
|
||||
override fun onSwipeRight() {
|
||||
super.onSwipeRight()
|
||||
onRightSwiped(showAlreadyActionedImages)
|
||||
}
|
||||
// Swipe right to view previous image in the folder. (if available)
|
||||
override fun onSwipeRight() {
|
||||
super.onSwipeRight()
|
||||
onRightSwiped(showAlreadyActionedImages)
|
||||
}
|
||||
|
||||
// Swipe up to select the picture (the equivalent of tapping it in non-fullscreen mode)
|
||||
// and show the next picture skipping pictures that have either already been uploaded or
|
||||
// marked as not for upload
|
||||
override fun onSwipeUp() {
|
||||
super.onSwipeUp()
|
||||
onUpSwiped()
|
||||
}
|
||||
// Swipe up to select the picture (the equivalent of tapping it in non-fullscreen mode)
|
||||
// and show the next picture skipping pictures that have either already been uploaded or
|
||||
// marked as not for upload
|
||||
override fun onSwipeUp() {
|
||||
super.onSwipeUp()
|
||||
onUpSwiped()
|
||||
}
|
||||
|
||||
// Swipe down to mark that picture as "Not for upload" (the equivalent of selecting it then
|
||||
// tapping "Mark as not for upload" in non-fullscreen mode), and show the next picture.
|
||||
override fun onSwipeDown() {
|
||||
super.onSwipeDown()
|
||||
onDownSwiped()
|
||||
}
|
||||
})
|
||||
// Swipe down to mark that picture as "Not for upload" (the equivalent of selecting it then
|
||||
// tapping "Mark as not for upload" in non-fullscreen mode), and show the next picture.
|
||||
override fun onSwipeDown() {
|
||||
super.onSwipeDown()
|
||||
onDownSwiped()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -260,58 +271,66 @@ class ZoomableActivity : BaseActivity() {
|
|||
* Handles down swipe action
|
||||
*/
|
||||
private fun onDownSwiped() {
|
||||
if (binding.zoomable?.zoomableController?.isIdentity == false)
|
||||
if (binding.zoomable?.zoomableController?.isIdentity == false) {
|
||||
return
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
images!![position].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
val imageSHA1 =
|
||||
CustomSelectorUtils.getImageSHA1(
|
||||
images!![position].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver,
|
||||
)
|
||||
var isUploaded = uploadedStatusDao.findByImageSHA1(imageSHA1, true)
|
||||
if (isUploaded > 0) {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.this_image_is_already_uploaded),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![position],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper
|
||||
)
|
||||
isUploaded = uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true
|
||||
)
|
||||
if (isUploaded > 0) {
|
||||
Toast.makeText(
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.this_image_is_already_uploaded),
|
||||
Toast.LENGTH_SHORT
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
} else {
|
||||
val imageModifiedSHA1 =
|
||||
CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![position],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper,
|
||||
)
|
||||
isUploaded =
|
||||
uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true,
|
||||
)
|
||||
if (isUploaded > 0) {
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.this_image_is_already_uploaded),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
} else {
|
||||
insertInNotForUpload(images!![position])
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.image_marked_as_not_for_upload),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.image_marked_as_not_for_upload),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
shouldRefresh = true
|
||||
if (position < images!!.size - 1) {
|
||||
position++
|
||||
init(images!![position].uri)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -322,58 +341,66 @@ class ZoomableActivity : BaseActivity() {
|
|||
* Handles up swipe action
|
||||
*/
|
||||
private fun onUpSwiped() {
|
||||
if (binding.zoomable?.zoomableController?.isIdentity == false)
|
||||
if (binding.zoomable?.zoomableController?.isIdentity == false) {
|
||||
return
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
images!![position].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
val imageSHA1 =
|
||||
CustomSelectorUtils.getImageSHA1(
|
||||
images!![position].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver,
|
||||
)
|
||||
var isNonActionable = notForUploadStatusDao.find(imageSHA1)
|
||||
if (isNonActionable > 0) {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.can_not_select_this_image_for_upload),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.can_not_select_this_image_for_upload),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
} else {
|
||||
isNonActionable =
|
||||
uploadedStatusDao.findByImageSHA1(imageSHA1, true)
|
||||
if (isNonActionable > 0) {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.this_image_is_already_uploaded),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![position],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper
|
||||
)
|
||||
isNonActionable = uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true
|
||||
)
|
||||
if (isNonActionable > 0) {
|
||||
Toast.makeText(
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.this_image_is_already_uploaded),
|
||||
Toast.LENGTH_SHORT
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
} else {
|
||||
val imageModifiedSHA1 =
|
||||
CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![position],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper,
|
||||
)
|
||||
isNonActionable =
|
||||
uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true,
|
||||
)
|
||||
if (isNonActionable > 0) {
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.this_image_is_already_uploaded),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
} else {
|
||||
if (!selectedImages!!.contains(images!![position])) {
|
||||
selectedImages!!.add(images!![position])
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.image_selected),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.image_selected),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
position = getNextActionableImage(position + 1)
|
||||
init(images!![position].uri)
|
||||
|
|
@ -387,19 +414,21 @@ class ZoomableActivity : BaseActivity() {
|
|||
* Handles right swipe action
|
||||
*/
|
||||
private fun onRightSwiped(showAlreadyActionedImages: Boolean) {
|
||||
if (binding.zoomable?.zoomableController?.isIdentity == false)
|
||||
if (binding.zoomable?.zoomableController?.isIdentity == false) {
|
||||
return
|
||||
}
|
||||
|
||||
if (showAlreadyActionedImages) {
|
||||
if (position > 0) {
|
||||
position--
|
||||
init(images!![position].uri)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
if (position > 0) {
|
||||
|
|
@ -408,11 +437,12 @@ class ZoomableActivity : BaseActivity() {
|
|||
init(images!![position].uri)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -421,19 +451,21 @@ class ZoomableActivity : BaseActivity() {
|
|||
* Handles left swipe action
|
||||
*/
|
||||
private fun onLeftSwiped(showAlreadyActionedImages: Boolean) {
|
||||
if (binding.zoomable?.zoomableController?.isIdentity == false)
|
||||
if (binding.zoomable?.zoomableController?.isIdentity == false) {
|
||||
return
|
||||
}
|
||||
|
||||
if (showAlreadyActionedImages) {
|
||||
if (position < images!!.size - 1) {
|
||||
position++
|
||||
init(images!![position].uri)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
if (position < images!!.size - 1) {
|
||||
|
|
@ -442,11 +474,12 @@ class ZoomableActivity : BaseActivity() {
|
|||
init(images!![position].uri)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -459,29 +492,32 @@ class ZoomableActivity : BaseActivity() {
|
|||
*/
|
||||
private suspend fun getNextActionableImage(index: Int): Int {
|
||||
var nextPosition = position
|
||||
for(i in index until images!!.size){
|
||||
for (i in index until images!!.size) {
|
||||
nextPosition = i
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
images!![i].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
val imageSHA1 =
|
||||
CustomSelectorUtils.getImageSHA1(
|
||||
images!![i].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver,
|
||||
)
|
||||
var isNonActionable = notForUploadStatusDao.find(imageSHA1)
|
||||
if (isNonActionable <= 0) {
|
||||
isNonActionable = uploadedStatusDao.findByImageSHA1(imageSHA1, true)
|
||||
if (isNonActionable <= 0) {
|
||||
val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![i],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper
|
||||
)
|
||||
isNonActionable = uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true
|
||||
)
|
||||
val imageModifiedSHA1 =
|
||||
CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![i],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper,
|
||||
)
|
||||
isNonActionable =
|
||||
uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true,
|
||||
)
|
||||
if (isNonActionable <= 0) {
|
||||
return i
|
||||
} else {
|
||||
|
|
@ -505,29 +541,32 @@ class ZoomableActivity : BaseActivity() {
|
|||
*/
|
||||
private suspend fun getPreviousActionableImage(index: Int): Int {
|
||||
var previousPosition = position
|
||||
for(i in index downTo 0){
|
||||
for (i in index downTo 0) {
|
||||
previousPosition = i
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
images!![i].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
val imageSHA1 =
|
||||
CustomSelectorUtils.getImageSHA1(
|
||||
images!![i].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver,
|
||||
)
|
||||
var isNonActionable = notForUploadStatusDao.find(imageSHA1)
|
||||
if (isNonActionable <= 0) {
|
||||
isNonActionable = uploadedStatusDao.findByImageSHA1(imageSHA1, true)
|
||||
if (isNonActionable <= 0) {
|
||||
val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![i],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper
|
||||
)
|
||||
isNonActionable = uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true
|
||||
)
|
||||
val imageModifiedSHA1 =
|
||||
CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![i],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper,
|
||||
)
|
||||
isNonActionable =
|
||||
uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true,
|
||||
)
|
||||
if (isNonActionable <= 0) {
|
||||
return i
|
||||
} else {
|
||||
|
|
@ -561,9 +600,10 @@ class ZoomableActivity : BaseActivity() {
|
|||
/**
|
||||
* Get position of an image from list
|
||||
*/
|
||||
private fun getImagePosition(list: ArrayList<Image>?, image: Image): Int {
|
||||
return list!!.indexOf(image)
|
||||
}
|
||||
private fun getImagePosition(
|
||||
list: ArrayList<Image>?,
|
||||
image: Image,
|
||||
): Int = list!!.indexOf(image)
|
||||
|
||||
/**
|
||||
* Two types of loading indicators have been added to the zoom activity:
|
||||
|
|
@ -573,19 +613,25 @@ class ZoomableActivity : BaseActivity() {
|
|||
*/
|
||||
private val loadingListener: ControllerListener<ImageInfo?> =
|
||||
object : BaseControllerListener<ImageInfo?>() {
|
||||
override fun onSubmit(id: String, callerContext: Any) {
|
||||
override fun onSubmit(
|
||||
id: String,
|
||||
callerContext: Any,
|
||||
) {
|
||||
// Sometimes the spinner doesn't appear when rapidly switching between images, this fixes that
|
||||
binding.zoomProgressBar.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onIntermediateImageSet(id: String, imageInfo: ImageInfo?) {
|
||||
override fun onIntermediateImageSet(
|
||||
id: String,
|
||||
imageInfo: ImageInfo?,
|
||||
) {
|
||||
binding.zoomProgressBar.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFinalImageSet(
|
||||
id: String,
|
||||
imageInfo: ImageInfo?,
|
||||
animatable: Animatable?
|
||||
animatable: Animatable?,
|
||||
) {
|
||||
binding.zoomProgressBar.visibility = View.GONE
|
||||
}
|
||||
|
|
@ -593,23 +639,27 @@ class ZoomableActivity : BaseActivity() {
|
|||
|
||||
private fun init(imageUri: Uri?) {
|
||||
if (imageUri != null) {
|
||||
val hierarchy = GenericDraweeHierarchyBuilder.newInstance(resources)
|
||||
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
||||
.setProgressBarImage(ProgressBarDrawable())
|
||||
.setProgressBarImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
||||
.build()
|
||||
val hierarchy =
|
||||
GenericDraweeHierarchyBuilder
|
||||
.newInstance(resources)
|
||||
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
||||
.setProgressBarImage(ProgressBarDrawable())
|
||||
.setProgressBarImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
||||
.build()
|
||||
with(binding.zoomable!!) {
|
||||
setHierarchy(hierarchy)
|
||||
setAllowTouchInterceptionWhileZoomed(true)
|
||||
setIsLongpressEnabled(false)
|
||||
setTapListener(DoubleTapGestureListener(this))
|
||||
}
|
||||
val controller: DraweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setUri(imageUri)
|
||||
.setControllerListener(loadingListener)
|
||||
.build()
|
||||
val controller: DraweeController =
|
||||
Fresco
|
||||
.newDraweeControllerBuilder()
|
||||
.setUri(imageUri)
|
||||
.setControllerListener(loadingListener)
|
||||
.build()
|
||||
binding.zoomable!!.controller = controller
|
||||
|
||||
|
||||
if (photoBackgroundColor != null) {
|
||||
binding.zoomable!!.setBackgroundColor(photoBackgroundColor!!)
|
||||
}
|
||||
|
|
@ -630,16 +680,17 @@ class ZoomableActivity : BaseActivity() {
|
|||
* Inserts an image in Not For Upload table
|
||||
*/
|
||||
private suspend fun insertInNotForUpload(it: Image) {
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
it.uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
val imageSHA1 =
|
||||
CustomSelectorUtils.getImageSHA1(
|
||||
it.uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver,
|
||||
)
|
||||
notForUploadStatusDao.insert(
|
||||
NotForUploadStatus(
|
||||
imageSHA1
|
||||
)
|
||||
imageSHA1,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -651,7 +702,7 @@ class ZoomableActivity : BaseActivity() {
|
|||
val returnIntent = Intent()
|
||||
returnIntent.putParcelableArrayListExtra(
|
||||
CustomSelectorConstants.NEW_SELECTED_IMAGES,
|
||||
selectedImages
|
||||
selectedImages,
|
||||
)
|
||||
returnIntent.putExtra(SHOULD_REFRESH, shouldRefresh)
|
||||
setResult(Activity.RESULT_OK, returnIntent)
|
||||
|
|
@ -665,14 +716,14 @@ class ZoomableActivity : BaseActivity() {
|
|||
super.onDestroy()
|
||||
}
|
||||
|
||||
object ZoomableActivityConstants {
|
||||
object ZoomableActivityConstants {
|
||||
/**
|
||||
* Key for Accessing Intent Data Named "Origin", The value indicates what fragment
|
||||
* ZoomableActivity was created by. It is null if ZoomableActivity was created by
|
||||
* the custom picker.
|
||||
*/
|
||||
const val ORIGIN = "Origin";
|
||||
|
||||
const val ORIGIN = "Origin"
|
||||
|
||||
const val PHOTO_BACKGROUND_COLOR = "photo_background_color"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ package fr.free.nrw.commons.media.model
|
|||
data class PageMediaListResponse(
|
||||
val revision: String,
|
||||
val tid: String,
|
||||
val items: List<PageMediaListItem>
|
||||
val items: List<PageMediaListItem>,
|
||||
)
|
||||
|
||||
data class PageMediaListItem(val title: String)
|
||||
data class PageMediaListItem(
|
||||
val title: String,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue