mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-30 22:34:02 +01:00 
			
		
		
		
	Fix app stuck and memory issues while uploading images (#2287)
* Do not use an image array to store all bitmap pixels at once * Extract image preprocessing to a different service and use computation thread * Add java docs * Cleanup code to remove temp file logic * Add logs in upload flow * Fix tests * Fix more tests
This commit is contained in:
		
							parent
							
								
									21f82dd346
								
							
						
					
					
						commit
						559127dfa3
					
				
					 21 changed files with 320 additions and 891 deletions
				
			
		|  | @ -1,25 +1,21 @@ | |||
| package fr.free.nrw.commons.upload | ||||
| 
 | ||||
| import android.app.Application | ||||
| import android.content.ContentResolver | ||||
| import android.content.Context | ||||
| import android.content.SharedPreferences | ||||
| import android.graphics.BitmapRegionDecoder | ||||
| import android.net.Uri | ||||
| import fr.free.nrw.commons.auth.SessionManager | ||||
| import fr.free.nrw.commons.kvstore.BasicKvStore | ||||
| import fr.free.nrw.commons.location.LatLng | ||||
| import fr.free.nrw.commons.mwapi.MediaWikiApi | ||||
| import fr.free.nrw.commons.nearby.Place | ||||
| import fr.free.nrw.commons.utils.BitmapRegionDecoderWrapper | ||||
| import fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK | ||||
| import fr.free.nrw.commons.utils.ImageUtilsWrapper | ||||
| import io.reactivex.Single | ||||
| import org.junit.After | ||||
| import org.junit.Assert.assertFalse | ||||
| import org.junit.Assert.assertTrue | ||||
| import org.junit.Before | ||||
| import org.junit.Test | ||||
| import org.mockito.ArgumentMatchers.* | ||||
| import org.mockito.ArgumentMatchers.any | ||||
| import org.mockito.ArgumentMatchers.anyString | ||||
| import org.mockito.InjectMocks | ||||
| import org.mockito.Mock | ||||
| import org.mockito.Mockito.`when` | ||||
|  | @ -27,6 +23,7 @@ import org.mockito.Mockito.mock | |||
| import org.mockito.MockitoAnnotations | ||||
| import java.io.FileInputStream | ||||
| import java.io.InputStream | ||||
| import java.util.* | ||||
| import javax.inject.Inject | ||||
| import javax.inject.Named | ||||
| 
 | ||||
|  | @ -51,11 +48,9 @@ class UploadModelTest { | |||
|     @Mock | ||||
|     internal var fileUtilsWrapper: FileUtilsWrapper? = null | ||||
|     @Mock | ||||
|     internal var imageUtilsWrapper: ImageUtilsWrapper? = null | ||||
|     @Mock | ||||
|     internal var bitmapRegionDecoderWrapper: BitmapRegionDecoderWrapper? = null | ||||
|     @Mock | ||||
|     internal var fileProcessor: FileProcessor? = null | ||||
|     @Mock | ||||
|     internal var imageProcessingService: ImageProcessingService? = null | ||||
| 
 | ||||
|     @InjectMocks | ||||
|     var uploadModel: UploadModel? = null | ||||
|  | @ -67,8 +62,6 @@ class UploadModelTest { | |||
| 
 | ||||
|         `when`(context!!.applicationContext) | ||||
|                 .thenReturn(mock(Application::class.java)) | ||||
|         `when`(fileUtilsWrapper!!.createCopyPathAndCopy(anyBoolean(), any(Uri::class.java), nullable(ContentResolver::class.java), any(Context::class.java))) | ||||
|                 .thenReturn("file.jpg") | ||||
|         `when`(fileUtilsWrapper!!.getFileExt(anyString())) | ||||
|                 .thenReturn("jpg") | ||||
|         `when`(fileUtilsWrapper!!.getSHA1(any(InputStream::class.java))) | ||||
|  | @ -77,12 +70,10 @@ class UploadModelTest { | |||
|                 .thenReturn(mock(FileInputStream::class.java)) | ||||
|         `when`(fileUtilsWrapper!!.getGeolocationOfFile(anyString())) | ||||
|                 .thenReturn("") | ||||
|         `when`(imageUtilsWrapper!!.checkIfImageIsTooDark(any(BitmapRegionDecoder::class.java))) | ||||
|                 .thenReturn(IMAGE_OK) | ||||
|         `when`(imageUtilsWrapper!!.checkImageGeolocationIsDifferent(anyString(), any(LatLng::class.java))) | ||||
|                 .thenReturn(false) | ||||
|         `when`(bitmapRegionDecoderWrapper!!.newInstance(any(FileInputStream::class.java), anyBoolean())) | ||||
|                 .thenReturn(mock(BitmapRegionDecoder::class.java)) | ||||
|         `when`(imageProcessingService!!.checkImageQuality(anyString())) | ||||
|                 .thenReturn(Single.just(IMAGE_OK)) | ||||
|         `when`(imageProcessingService!!.checkImageQuality(any(Place::class.java), anyString())) | ||||
|                 .thenReturn(Single.just(IMAGE_OK)) | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|  | @ -93,154 +84,99 @@ class UploadModelTest { | |||
| 
 | ||||
|     @Test | ||||
|     fun receive() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.items.size == 2) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun receiveDirect() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } | ||||
|         assertTrue(uploadModel!!.items.size == 1) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun verifyPreviousNotAvailableForDirectUpload() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } | ||||
|         assertFalse(uploadModel!!.isPreviousAvailable) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun verifyNextAvailableForDirectUpload() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } | ||||
|         assertTrue(uploadModel!!.isNextAvailable) | ||||
|         val preProcessImages = uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         preProcessImages.doOnComplete { | ||||
|             assertTrue(uploadModel!!.items.size == 2) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun verifyPreviousNotAvailable() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         assertFalse(uploadModel!!.isPreviousAvailable) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun verifyNextAvailable() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.isNextAvailable) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun isSubmitAvailable() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.isNextAvailable) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun isSubmitAvailableForDirectUpload() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } | ||||
|         assertTrue(uploadModel!!.isNextAvailable) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun getCurrentStepForDirectUpload() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } | ||||
|         assertTrue(uploadModel!!.currentStep == 1) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun getCurrentStep() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.currentStep == 1) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun getStepCount() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.stepCount == 4) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun getStepCountForDirectUpload() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } | ||||
|         assertTrue(uploadModel!!.stepCount == 3) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun getDirectCount() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } | ||||
|         assertTrue(uploadModel!!.count == 1) | ||||
|         val preProcessImages = uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         preProcessImages.doOnComplete { | ||||
|             assertTrue(uploadModel!!.stepCount == 4) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun getCount() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.count == 2) | ||||
|         val preProcessImages = uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         preProcessImages.doOnComplete { | ||||
|             assertTrue(uploadModel!!.count == 2) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun getUploads() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.uploads.size == 2) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun getDirectUploads() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } | ||||
|         assertTrue(uploadModel!!.uploads.size == 1) | ||||
|         val preProcessImages = uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         preProcessImages.doOnComplete { | ||||
|             assertTrue(uploadModel!!.uploads.size == 2) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun isTopCardState() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.isTopCardState) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun isTopCardStateForDirectUpload() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } | ||||
|         uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.isTopCardState) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun next() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.currentStep == 1) | ||||
|         uploadModel!!.next() | ||||
|         assertTrue(uploadModel!!.currentStep == 2) | ||||
|  | @ -248,10 +184,10 @@ class UploadModelTest { | |||
| 
 | ||||
|     @Test | ||||
|     fun previous() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.currentStep == 1) | ||||
|         uploadModel!!.next() | ||||
|         assertTrue(uploadModel!!.currentStep == 2) | ||||
|  | @ -261,11 +197,19 @@ class UploadModelTest { | |||
| 
 | ||||
|     @Test | ||||
|     fun isShowingItem() { | ||||
|         val element = mock(Uri::class.java) | ||||
|         val element2 = mock(Uri::class.java) | ||||
|         val element = getElement() | ||||
|         val element2 = getElement() | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadModel!!.receive(uriList, "image/jpeg", "external") { _, _ -> } | ||||
|         assertTrue(uploadModel!!.isShowingItem) | ||||
|         val preProcessImages = uploadModel!!.preProcessImages(uriList, "image/jpeg", mock(Place::class.java), "external") { _, _ -> } | ||||
|         preProcessImages.doOnComplete { | ||||
|             assertTrue(uploadModel!!.isShowingItem) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun getElement(): Uri { | ||||
|         val mock = mock(Uri::class.java) | ||||
|         `when`(mock.path).thenReturn(UUID.randomUUID().toString() + "/file.jpg") | ||||
|         return mock | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|  |  | |||
|  | @ -3,12 +3,12 @@ package fr.free.nrw.commons.upload | |||
| import android.net.Uri | ||||
| import fr.free.nrw.commons.mwapi.MediaWikiApi | ||||
| import fr.free.nrw.commons.nearby.Place | ||||
| import io.reactivex.Observable | ||||
| import org.junit.Before | ||||
| import org.junit.Test | ||||
| import org.mockito.InjectMocks | ||||
| import org.mockito.Mock | ||||
| import org.mockito.Mockito | ||||
| import org.mockito.MockitoAnnotations | ||||
| import org.mockito.* | ||||
| import org.mockito.Mockito.`when` | ||||
| import org.mockito.Mockito.mock | ||||
| 
 | ||||
| class UploadPresenterTest { | ||||
| 
 | ||||
|  | @ -26,6 +26,12 @@ class UploadPresenterTest { | |||
|     @Throws(Exception::class) | ||||
|     fun setUp() { | ||||
|         MockitoAnnotations.initMocks(this) | ||||
|         `when`(uploadModel!!.preProcessImages(ArgumentMatchers.anyListOf(Uri::class.java), | ||||
|                 ArgumentMatchers.anyString(), | ||||
|                 ArgumentMatchers.any(Place::class.java), | ||||
|                 ArgumentMatchers.anyString(), | ||||
|                 ArgumentMatchers.any(SimilarImageInterface::class.java))) | ||||
|                 .thenReturn(Observable.just(mock(UploadModel.UploadItem::class.java))) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|  | @ -33,18 +39,6 @@ class UploadPresenterTest { | |||
|         val element = Mockito.mock(Uri::class.java) | ||||
|         val element2 = Mockito.mock(Uri::class.java) | ||||
|         var uriList: List<Uri> = mutableListOf<Uri>(element, element2) | ||||
|         uploadPresenter!!.receive(uriList, "image/jpeg", "external") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun receiveSingleItem() { | ||||
|         val element = Mockito.mock(Uri::class.java) | ||||
|         uploadPresenter!!.receive(element, "image/jpeg", "external") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun receiveDirect() { | ||||
|         val element = Mockito.mock(Uri::class.java) | ||||
|         uploadModel!!.receiveDirect(element, "image/jpeg", "external", Mockito.mock(Place::class.java)) { _, _ -> } | ||||
|         uploadPresenter!!.receive(uriList, "image/jpeg", "external", mock(Place::class.java)) | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vivek Maskara
						Vivek Maskara