@@ -11,10 +11,7 @@ import fr.free.nrw.commons.filepicker.PickedFiles
11
11
import fr.free.nrw.commons.media.MediaClient
12
12
import fr.free.nrw.commons.upload.FileProcessor
13
13
import fr.free.nrw.commons.upload.FileUtilsWrapper
14
- import kotlinx.coroutines.CoroutineScope
15
- import kotlinx.coroutines.Dispatchers
16
- import kotlinx.coroutines.launch
17
- import kotlinx.coroutines.withContext
14
+ import kotlinx.coroutines.*
18
15
import timber.log.Timber
19
16
import java.io.IOException
20
17
import java.net.UnknownHostException
@@ -57,9 +54,17 @@ class ImageLoader @Inject constructor(
57
54
/* *
58
55
* Maps to facilitate image query.
59
56
*/
60
- private var mapImageSHA1 : HashMap <Image , String > = HashMap ()
57
+ private var mapModifiedImageSHA1 : HashMap <Image , String > = HashMap ()
61
58
private var mapHolderImage : HashMap <ImageViewHolder , Image > = HashMap ()
62
59
private var mapResult: HashMap <String , Result > = HashMap ()
60
+ private var mapImageSHA1: HashMap <Uri , String > = HashMap ()
61
+
62
+ /* *
63
+ * Coroutine Dispatchers and Scope.
64
+ */
65
+ private var defaultDispatcher = Dispatchers .Default
66
+ private var ioDispatcher = Dispatchers .IO
67
+ private val scope = MainScope ()
63
68
64
69
/* *
65
70
* Query image and setUp the view.
@@ -72,42 +77,43 @@ class ImageLoader @Inject constructor(
72
77
mapHolderImage[holder] = image
73
78
holder.itemNotUploaded()
74
79
75
- CoroutineScope ( Dispatchers . Main ) .launch {
80
+ scope .launch {
76
81
77
- var result : Result = Result .NOTFOUND
78
- withContext(Dispatchers .Default ) {
82
+ var result: Result = Result .NOTFOUND
79
83
84
+ if (mapHolderImage[holder] != image) {
85
+ return @launch
86
+ }
87
+
88
+ val imageSHA1 = getImageSHA1(image.uri)
89
+ val uploadedStatus = getFromUploaded(imageSHA1)
90
+
91
+ val sha1 = uploadedStatus?.let {
92
+ result = getResultFromUploadedStatus(uploadedStatus)
93
+ uploadedStatus.modifiedImageSHA1
94
+ } ? : run {
80
95
if (mapHolderImage[holder] == image) {
81
- val imageSHA1 = getImageSHA1(image.uri)
82
- val uploadedStatus = uploadedStatusDao.getUploadedFromImageSHA1(imageSHA1)
83
-
84
- val sha1 = uploadedStatus?.let {
85
- result = getResultFromUploadedStatus(uploadedStatus)
86
- uploadedStatus.modifiedImageSHA1
87
- } ? : run {
88
- if (mapHolderImage[holder] == image) {
89
- getSHA1(image)
90
- } else {
91
- " "
92
- }
93
- }
94
-
95
- if (mapHolderImage[holder] == image &&
96
- result in arrayOf(Result .NOTFOUND , Result .INVALID ) &&
97
- sha1.isNotEmpty()) {
98
- // Query original image.
99
- result = querySHA1(imageSHA1)
100
- if ( result is Result .TRUE ) {
101
- // Original image found.
102
- insertIntoUploaded(imageSHA1, sha1, result is Result .TRUE , false )
103
- }
104
- else {
105
- // Original image not found, query modified image.
106
- result = querySHA1(sha1)
107
- if (result != Result .ERROR ) {
108
- insertIntoUploaded(imageSHA1, sha1, false , result is Result .TRUE )
109
- }
110
- }
96
+ getSHA1(image)
97
+ } else {
98
+ " "
99
+ }
100
+ }
101
+
102
+ if (mapHolderImage[holder] != image) {
103
+ return @launch
104
+ }
105
+
106
+ if (result in arrayOf(Result .NOTFOUND , Result .INVALID ) && sha1.isNotEmpty()) {
107
+ // Query original image.
108
+ result = querySHA1(imageSHA1)
109
+ if (result is Result .TRUE ) {
110
+ // Original image found.
111
+ insertIntoUploaded(imageSHA1, sha1, result is Result .TRUE , false )
112
+ } else {
113
+ // Original image not found, query modified image.
114
+ result = querySHA1(sha1)
115
+ if (result != Result .ERROR ) {
116
+ insertIntoUploaded(imageSHA1, sha1, false , result is Result .TRUE )
111
117
}
112
118
}
113
119
}
@@ -122,25 +128,27 @@ class ImageLoader @Inject constructor(
122
128
*
123
129
* @return Query result.
124
130
*/
125
- private fun querySHA1 (SHA1 : String ): Result {
126
- mapResult[SHA1 ]?.let {
127
- return it
128
- }
129
- var result : Result = Result .FALSE
130
- try {
131
- if (mediaClient.checkFileExistsUsingSha(SHA1 ).blockingGet()) {
132
- mapResult[SHA1 ] = Result .TRUE
133
- result = Result .TRUE
131
+
132
+ private suspend fun querySHA1 (SHA1 : String ): Result {
133
+ return withContext(ioDispatcher) {
134
+ mapResult[SHA1 ]?.let {
135
+ return @withContext it
134
136
}
135
- } catch (e: Exception ) {
136
- if (e is UnknownHostException ) {
137
- // Handle no network connection.
138
- Timber .e(e, " Network Connection Error" )
137
+ var result: Result = Result .FALSE
138
+ try {
139
+ if (mediaClient.checkFileExistsUsingSha(SHA1 ).blockingGet()) {
140
+ mapResult[SHA1 ] = Result .TRUE
141
+ result = Result .TRUE
142
+ }
143
+ } catch (e: Exception ) {
144
+ if (e is UnknownHostException ) {
145
+ // Handle no network connection.
146
+ Timber .e(e, " Network Connection Error" )
147
+ }
148
+ result = Result .ERROR
149
+ e.printStackTrace()
139
150
}
140
- result = Result .ERROR
141
- e.printStackTrace()
142
- } finally {
143
- return result
151
+ result
144
152
}
145
153
}
146
154
@@ -149,27 +157,48 @@ class ImageLoader @Inject constructor(
149
157
*
150
158
* @return sha1 of the image
151
159
*/
152
- private fun getSHA1 (image : Image ): String {
153
- mapImageSHA1 [image]?.let {
160
+ private suspend fun getSHA1 (image : Image ): String {
161
+ mapModifiedImageSHA1 [image]?.let {
154
162
return it
155
163
}
156
164
val sha1 = generateModifiedSHA1(image);
157
- mapImageSHA1 [image] = sha1;
165
+ mapModifiedImageSHA1 [image] = sha1;
158
166
return sha1;
159
167
}
160
168
169
+ /* *
170
+ * Get the uploaded status entry from the database.
171
+ */
172
+ private suspend fun getFromUploaded (imageSha1 : String ): UploadedStatus ? {
173
+ return uploadedStatusDao.getUploadedFromImageSHA1(imageSha1)
174
+ }
175
+
161
176
/* *
162
177
* Insert into uploaded status table.
163
178
*/
164
- private fun insertIntoUploaded (imageSha1 : String , modifiedImageSha1 : String , imageResult : Boolean , modifiedImageResult : Boolean ){
165
- uploadedStatusDao.insertUploaded(UploadedStatus (imageSha1, modifiedImageSha1, imageResult, modifiedImageResult))
179
+ private suspend fun insertIntoUploaded (imageSha1 : String , modifiedImageSha1 : String , imageResult : Boolean , modifiedImageResult : Boolean ){
180
+ uploadedStatusDao.insertUploaded(
181
+ UploadedStatus (
182
+ imageSha1,
183
+ modifiedImageSha1,
184
+ imageResult,
185
+ modifiedImageResult
186
+ )
187
+ )
166
188
}
167
189
168
190
/* *
169
191
* Get image sha1 from uri, used to retrieve the original image sha1.
170
192
*/
171
- private fun getImageSHA1 (uri : Uri ): String {
172
- return fileUtilsWrapper.getSHA1(context.contentResolver.openInputStream(uri))
193
+ private suspend fun getImageSHA1 (uri : Uri ): String {
194
+ return withContext(ioDispatcher) {
195
+ mapImageSHA1[uri]?.let {
196
+ return @withContext it
197
+ }
198
+ val result = fileUtilsWrapper.getSHA1(context.contentResolver.openInputStream(uri))
199
+ mapImageSHA1[uri] = result
200
+ result
201
+ }
173
202
}
174
203
175
204
/* *
@@ -194,18 +223,28 @@ class ImageLoader @Inject constructor(
194
223
*
195
224
* @return modified sha1
196
225
*/
197
- private fun generateModifiedSHA1 (image : Image ) : String {
198
- val uploadableFile = PickedFiles .pickedExistingPicture(context, image.uri)
199
- val exifInterface: ExifInterface ? = try {
200
- ExifInterface (uploadableFile.file!! )
201
- } catch (e: IOException ) {
202
- Timber .e(e)
203
- null
226
+ private suspend fun generateModifiedSHA1 (image : Image ) : String {
227
+ return withContext(defaultDispatcher) {
228
+ val uploadableFile = PickedFiles .pickedExistingPicture(context, image.uri)
229
+ val exifInterface: ExifInterface ? = try {
230
+ ExifInterface (uploadableFile.file!! )
231
+ } catch (e: IOException ) {
232
+ Timber .e(e)
233
+ null
234
+ }
235
+ fileProcessor.redactExifTags(exifInterface, fileProcessor.getExifTagsToRedact())
236
+ val sha1 =
237
+ fileUtilsWrapper.getSHA1(fileUtilsWrapper.getFileInputStream(uploadableFile.filePath))
238
+ uploadableFile.file.delete()
239
+ sha1
204
240
}
205
- fileProcessor.redactExifTags(exifInterface, fileProcessor.getExifTagsToRedact())
206
- val sha1 = fileUtilsWrapper.getSHA1(fileUtilsWrapper.getFileInputStream(uploadableFile.filePath))
207
- uploadableFile.file.delete()
208
- return sha1
241
+ }
242
+
243
+ /* *
244
+ * CleanUp function.
245
+ */
246
+ fun cleanUP () {
247
+ scope.cancel()
209
248
}
210
249
211
250
/* *
0 commit comments