mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
* #3408 Refactoring the FileProcessor and GPSExtractor classes - refactor FileProcessor * #3408 Refactoring the FileProcessor and GPSExtractor classes - refactor and rename GpsExtractor * #3408 Refactoring the FileProcessor and GPSExtractor classes - convert ImageCoordinates to kotlin * #3408 Refactoring the FileProcessor and GPSExtractor classes - convert FileProcessor to kotlin * #3408 Refactoring the FileProcessor and GPSExtractor classes - minor reformatting * #3408 Refactoring the FileProcessor and GPSExtractor classes - fix compilation and naming issues * #3408 Refactoring the FileProcessor and GPSExtractor classes - remove empty test * #3408 Refactoring the FileProcessor and GPSExtractor classes - set coordinates for upload item if user chooses it
This commit is contained in:
parent
efc6fa6211
commit
fb51fc618a
17 changed files with 450 additions and 834 deletions
|
|
@ -2,26 +2,13 @@ package fr.free.nrw.commons.di;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import androidx.collection.LruCache;
|
||||
import androidx.room.Room;
|
||||
|
||||
import com.github.varunpant.quadtree.QuadTree;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.wikipedia.AppAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
|
|
@ -41,6 +28,14 @@ import fr.free.nrw.commons.wikidata.WikidataEditListenerImpl;
|
|||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.wikipedia.AppAdapter;
|
||||
|
||||
/**
|
||||
* The Dependency Provider class for Commons Android.
|
||||
|
|
@ -244,4 +239,9 @@ public class CommonsApplicationModule {
|
|||
public ContributionDao providesContributionsDao() {
|
||||
return appDatabase.getContributionDao();
|
||||
}
|
||||
|
||||
@Provides
|
||||
public ContentResolver providesContentResolver(Context context){
|
||||
return context.getContentResolver();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package fr.free.nrw.commons.repository;
|
||||
|
||||
import fr.free.nrw.commons.upload.ImageCoordinates;
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
|
@ -202,4 +203,8 @@ public class UploadRemoteDataSource {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void useSimilarPictureCoordinates(ImageCoordinates imageCoordinates, int uploadItemIndex) {
|
||||
uploadModel.useSimilarPictureCoordinates(imageCoordinates, uploadItemIndex);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package fr.free.nrw.commons.repository;
|
||||
|
||||
import fr.free.nrw.commons.upload.ImageCoordinates;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -271,4 +272,8 @@ public class UploadRepository {
|
|||
public Place checkNearbyPlaces(double decLatitude, double decLongitude) {
|
||||
return remoteDataSource.getNearbyPlaces(decLatitude, decLongitude);
|
||||
}
|
||||
|
||||
public void useSimilarPictureCoordinates(ImageCoordinates imageCoordinates, int uploadItemIndex) {
|
||||
remoteDataSource.useSimilarPictureCoordinates(imageCoordinates, uploadItemIndex);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,245 +0,0 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.caching.CacheController;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.mwapi.CategoryApi;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.upload.SimilarImageDialogFragment.Callback;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Processing of the image filePath that is about to be uploaded via ShareActivity is done here
|
||||
*/
|
||||
@Singleton
|
||||
public class FileProcessor implements Callback {
|
||||
|
||||
@Inject
|
||||
CacheController cacheController;
|
||||
@Inject
|
||||
GpsCategoryModel gpsCategoryModel;
|
||||
@Inject
|
||||
CategoryApi apiCall;
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
JsonKvStore defaultKvStore;
|
||||
private String filePath;
|
||||
private ContentResolver contentResolver;
|
||||
private GPSExtractor imageObj;
|
||||
private String decimalCoords;
|
||||
private ExifInterface exifInterface;
|
||||
private boolean haveCheckedForOtherImages = false;
|
||||
private GPSExtractor tempImageObj;
|
||||
private CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
|
||||
@Inject
|
||||
public FileProcessor() {
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
compositeDisposable.clear();
|
||||
}
|
||||
|
||||
void initFileDetails(@NonNull String filePath, ContentResolver contentResolver) {
|
||||
this.filePath = filePath;
|
||||
this.contentResolver = contentResolver;
|
||||
try {
|
||||
exifInterface = new ExifInterface(filePath);
|
||||
} catch (IOException e) {
|
||||
Timber.e(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes filePath coordinates, either from EXIF data or user location
|
||||
*/
|
||||
GPSExtractor processFileCoordinates(SimilarImageInterface similarImageInterface, Context context) {
|
||||
// Redact EXIF data as indicated in preferences.
|
||||
redactExifTags(exifInterface, getExifTagsToRedact(context));
|
||||
|
||||
Timber.d("Calling GPSExtractor");
|
||||
imageObj = new GPSExtractor(exifInterface);
|
||||
decimalCoords = imageObj.getCoords();
|
||||
if (decimalCoords == null || !imageObj.imageCoordsExists) {
|
||||
//Find other photos taken around the same time which has gps coordinates
|
||||
if (!haveCheckedForOtherImages)
|
||||
findOtherImages(similarImageInterface);// Do not do repeat the process
|
||||
} else {
|
||||
useImageCoords();
|
||||
}
|
||||
|
||||
return imageObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets EXIF Tags from preferences to be redacted.
|
||||
*
|
||||
* @param context application context
|
||||
* @return tags to be redacted
|
||||
*/
|
||||
private Set<String> getExifTagsToRedact(Context context) {
|
||||
Type setType = new TypeToken<Set<String>>() {}.getType();
|
||||
Set<String> prefManageEXIFTags = defaultKvStore.getStringSet(Prefs.MANAGED_EXIF_TAGS);
|
||||
|
||||
Set<String> redactTags = new HashSet<>(Arrays.asList(
|
||||
context.getResources().getStringArray(R.array.pref_exifTag_values)));
|
||||
Timber.d(redactTags.toString());
|
||||
|
||||
if (prefManageEXIFTags != null) redactTags.removeAll(prefManageEXIFTags);
|
||||
|
||||
return redactTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redacts EXIF metadata as indicated in preferences.
|
||||
*
|
||||
* @param exifInterface ExifInterface object
|
||||
* @param redactTags tags to be redacted
|
||||
*/
|
||||
public static void redactExifTags(ExifInterface exifInterface, Set<String> redactTags) {
|
||||
if(redactTags.isEmpty()) return;
|
||||
|
||||
Disposable disposable = Observable.fromIterable(redactTags)
|
||||
.flatMap(tag -> Observable.fromArray(FileMetadataUtils.getTagsFromPref(tag)))
|
||||
.forEach(tag -> {
|
||||
Timber.d("Checking for tag: %s", tag);
|
||||
String oldValue = exifInterface.getAttribute(tag);
|
||||
if (oldValue != null && !oldValue.isEmpty()) {
|
||||
Timber.d("Exif tag %s with value %s redacted.", tag, oldValue);
|
||||
exifInterface.setAttribute(tag, null);
|
||||
}
|
||||
});
|
||||
CompositeDisposable disposables = new CompositeDisposable();
|
||||
disposables.add(disposable);
|
||||
disposables.clear();
|
||||
|
||||
try {
|
||||
exifInterface.saveAttributes();
|
||||
} catch (IOException e) {
|
||||
Timber.w("EXIF redaction failed: %s", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find other images around the same location that were taken within the last 20 sec
|
||||
* @param similarImageInterface
|
||||
*/
|
||||
private void findOtherImages(SimilarImageInterface similarImageInterface) {
|
||||
Timber.d("filePath" + filePath);
|
||||
|
||||
long timeOfCreation = new File(filePath).lastModified();//Time when the original image was created
|
||||
File folder = new File(filePath.substring(0, filePath.lastIndexOf('/')));
|
||||
File[] files = folder.listFiles();
|
||||
Timber.d("folderTime Number:" + files.length);
|
||||
|
||||
|
||||
for (File file : files) {
|
||||
if (file.lastModified() - timeOfCreation <= (120 * 1000) && file.lastModified() - timeOfCreation >= -(120 * 1000)) {
|
||||
//Make sure the photos were taken within 20seconds
|
||||
Timber.d("fild date:" + file.lastModified() + " time of creation" + timeOfCreation);
|
||||
tempImageObj = null;//Temporary GPSExtractor to extract coords from these photos
|
||||
try {
|
||||
tempImageObj = new GPSExtractor(contentResolver.openInputStream(Uri.fromFile(file)));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (tempImageObj != null) {
|
||||
tempImageObj = new GPSExtractor(file.getAbsolutePath());
|
||||
}
|
||||
if (tempImageObj != null) {
|
||||
Timber.d("not null fild EXIF" + tempImageObj.imageCoordsExists + " coords" + tempImageObj.getCoords());
|
||||
if (tempImageObj.getCoords() != null && tempImageObj.imageCoordsExists) {
|
||||
// Current image has gps coordinates and it's not current gps locaiton
|
||||
Timber.d("This filePath has image coords:" + file.getAbsolutePath());
|
||||
similarImageInterface.showSimilarImageFragment(filePath, file.getAbsolutePath());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
haveCheckedForOtherImages = true; //Finished checking for other images
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates retrieval of image coordinates or user coordinates, and caching of coordinates.
|
||||
* Then initiates the calls to MediaWiki API through an instance of CategoryApi.
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private void useImageCoords() {
|
||||
if (decimalCoords != null) {
|
||||
Timber.d("Decimal coords of image: %s", decimalCoords);
|
||||
Timber.d("is EXIF data present:" + imageObj.imageCoordsExists + " from findOther image");
|
||||
|
||||
// Only set cache for this point if image has coords
|
||||
if (imageObj.imageCoordsExists) {
|
||||
double decLongitude = imageObj.getDecLongitude();
|
||||
double decLatitude = imageObj.getDecLatitude();
|
||||
cacheController.setQtPoint(decLongitude, decLatitude);
|
||||
}
|
||||
|
||||
List<String> displayCatList = cacheController.findCategory();
|
||||
boolean catListEmpty = displayCatList.isEmpty();
|
||||
|
||||
|
||||
// If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories
|
||||
if (catListEmpty) {
|
||||
compositeDisposable.add(apiCall.request(decimalCoords)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
gpsCategoryModel::setCategoryList,
|
||||
throwable -> {
|
||||
Timber.e(throwable);
|
||||
gpsCategoryModel.clear();
|
||||
}
|
||||
));
|
||||
Timber.d("displayCatList size 0, calling MWAPI %s", displayCatList);
|
||||
} else {
|
||||
Timber.d("Cache found, setting categoryList in model to %s", displayCatList);
|
||||
gpsCategoryModel.setCategoryList(displayCatList);
|
||||
}
|
||||
} else {
|
||||
Timber.d("EXIF: no coords");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositiveResponse() {
|
||||
imageObj = tempImageObj;
|
||||
decimalCoords = imageObj.getCoords();// Not necessary to use gps as image already ha EXIF data
|
||||
Timber.d("EXIF from tempImageObj");
|
||||
useImageCoords();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeResponse() {
|
||||
Timber.d("EXIF from imageObj");
|
||||
useImageCoords();
|
||||
}
|
||||
}
|
||||
191
app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.kt
Normal file
191
app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.kt
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.caching.CacheController
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.mwapi.CategoryApi
|
||||
import fr.free.nrw.commons.settings.Prefs
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
/**
|
||||
* Processing of the image filePath that is about to be uploaded via ShareActivity is done here
|
||||
*/
|
||||
class FileProcessor @Inject constructor(
|
||||
private val context: Context,
|
||||
private val contentResolver: ContentResolver,
|
||||
private val cacheController: CacheController,
|
||||
private val gpsCategoryModel: GpsCategoryModel,
|
||||
@param:Named("default_preferences") private val defaultKvStore: JsonKvStore,
|
||||
private val apiCall: CategoryApi
|
||||
) {
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
fun cleanup() {
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes filePath coordinates, either from EXIF data or user location
|
||||
*/
|
||||
fun processFileCoordinates(similarImageInterface: SimilarImageInterface, filePath: String?)
|
||||
: ImageCoordinates {
|
||||
val exifInterface: ExifInterface? = try {
|
||||
ExifInterface(filePath!!)
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
null
|
||||
}
|
||||
// Redact EXIF data as indicated in preferences.
|
||||
redactExifTags(exifInterface, getExifTagsToRedact())
|
||||
Timber.d("Calling GPSExtractor")
|
||||
val originalImageCoordinates = ImageCoordinates(exifInterface)
|
||||
if (originalImageCoordinates.decimalCoords == null) {
|
||||
//Find other photos taken around the same time which has gps coordinates
|
||||
findOtherImages(
|
||||
File(filePath),
|
||||
similarImageInterface
|
||||
)
|
||||
} else {
|
||||
useImageCoords(originalImageCoordinates)
|
||||
}
|
||||
return originalImageCoordinates
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets EXIF Tags from preferences to be redacted.
|
||||
*
|
||||
* @return tags to be redacted
|
||||
*/
|
||||
private fun getExifTagsToRedact(): Set<String> {
|
||||
val prefManageEXIFTags =
|
||||
defaultKvStore.getStringSet(Prefs.MANAGED_EXIF_TAGS) ?: emptySet()
|
||||
val redactTags: Set<String> =
|
||||
context.resources.getStringArray(R.array.pref_exifTag_values).toSet()
|
||||
return redactTags - prefManageEXIFTags
|
||||
}
|
||||
|
||||
/**
|
||||
* Redacts EXIF metadata as indicated in preferences.
|
||||
*
|
||||
* @param exifInterface ExifInterface object
|
||||
* @param redactTags tags to be redacted
|
||||
*/
|
||||
private fun redactExifTags(exifInterface: ExifInterface?, redactTags: Set<String>) {
|
||||
compositeDisposable.add(
|
||||
Observable.fromIterable(redactTags)
|
||||
.flatMap { Observable.fromArray(*FileMetadataUtils.getTagsFromPref(it)) }
|
||||
.subscribe(
|
||||
{ redactTag(exifInterface, it) },
|
||||
{ Timber.d(it) },
|
||||
{ save(exifInterface) }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun save(exifInterface: ExifInterface?) {
|
||||
try {
|
||||
exifInterface?.saveAttributes()
|
||||
} catch (e: IOException) {
|
||||
Timber.w("EXIF redaction failed: %s", e.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun redactTag(exifInterface: ExifInterface?, tag: String) {
|
||||
Timber.d("Checking for tag: %s", tag)
|
||||
exifInterface?.getAttribute(tag)
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?.let {
|
||||
exifInterface.setAttribute(tag, null).also {
|
||||
Timber.d("Exif tag $tag with value $it redacted.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find other images around the same location that were taken within the last 20 sec
|
||||
*
|
||||
* @param originalImageCoordinates
|
||||
* @param fileBeingProcessed
|
||||
* @param similarImageInterface
|
||||
*/
|
||||
private fun findOtherImages(
|
||||
fileBeingProcessed: File,
|
||||
similarImageInterface: SimilarImageInterface
|
||||
) {
|
||||
val oneHundredAndTwentySeconds = 120 * 1000L
|
||||
//Time when the original image was created
|
||||
val timeOfCreation = fileBeingProcessed.lastModified()
|
||||
LongRange
|
||||
val timeOfCreationRange =
|
||||
timeOfCreation - oneHundredAndTwentySeconds..timeOfCreation + oneHundredAndTwentySeconds
|
||||
fileBeingProcessed.parentFile
|
||||
.listFiles()
|
||||
.asSequence()
|
||||
.filter { it.lastModified() in timeOfCreationRange }
|
||||
.map { Pair(it, readImageCoordinates(it)) }
|
||||
.firstOrNull { it.second?.decimalCoords != null }
|
||||
?.let { fileCoordinatesPair ->
|
||||
similarImageInterface.showSimilarImageFragment(
|
||||
fileBeingProcessed.path,
|
||||
fileCoordinatesPair.first.absolutePath,
|
||||
fileCoordinatesPair.second
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readImageCoordinates(file: File) =
|
||||
try {
|
||||
ImageCoordinates(contentResolver.openInputStream(Uri.fromFile(file)))
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
try {
|
||||
ImageCoordinates(file.absolutePath)
|
||||
} catch (ex: IOException) {
|
||||
Timber.e(ex)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates retrieval of image coordinates or user coordinates, and caching of coordinates. Then
|
||||
* initiates the calls to MediaWiki API through an instance of CategoryApi.
|
||||
*
|
||||
* @param imageCoordinates
|
||||
*/
|
||||
fun useImageCoords(imageCoordinates: ImageCoordinates) {
|
||||
requireNotNull(imageCoordinates.decimalCoords)
|
||||
cacheController.setQtPoint(imageCoordinates.decLongitude, imageCoordinates.decLatitude)
|
||||
val displayCatList = cacheController.findCategory()
|
||||
|
||||
// If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories
|
||||
if (displayCatList.isEmpty()) {
|
||||
compositeDisposable.add(
|
||||
apiCall.request(imageCoordinates.decimalCoords)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
{ gpsCategoryModel.categoryList = it },
|
||||
{
|
||||
Timber.e(it)
|
||||
gpsCategoryModel.clear()
|
||||
}
|
||||
)
|
||||
)
|
||||
Timber.d("displayCatList size 0, calling MWAPI %s", displayCatList)
|
||||
} else {
|
||||
Timber.d("Cache found, setting categoryList in model to %s", displayCatList)
|
||||
gpsCategoryModel.categoryList = displayCatList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -68,9 +68,9 @@ public class FileUtils {
|
|||
|
||||
try {
|
||||
ExifInterface exifInterface = new ExifInterface(filePath);
|
||||
GPSExtractor imageObj = new GPSExtractor(exifInterface);
|
||||
if (imageObj.imageCoordsExists) { // If image has geolocation information in its EXIF
|
||||
return imageObj.getCoords();
|
||||
ImageCoordinates imageObj = new ImageCoordinates(exifInterface);
|
||||
if (imageObj.getDecimalCoords() != null) { // If image has geolocation information in its EXIF
|
||||
return imageObj.getDecimalCoords();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,152 +0,0 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Extracts geolocation to be passed to API for category suggestions. If a picture with geolocation
|
||||
* is uploaded, extract latitude and longitude from EXIF data of image.
|
||||
*/
|
||||
public class GPSExtractor {
|
||||
|
||||
static final GPSExtractor DUMMY= new GPSExtractor();
|
||||
private double decLatitude;
|
||||
private double decLongitude;
|
||||
public boolean imageCoordsExists;
|
||||
private String latitude;
|
||||
private String longitude;
|
||||
private String latitudeRef;
|
||||
private String longitudeRef;
|
||||
private String decimalCoords;
|
||||
|
||||
/**
|
||||
* Dummy constructor.
|
||||
*/
|
||||
private GPSExtractor(){
|
||||
|
||||
}
|
||||
/**
|
||||
* Construct from a stream.
|
||||
*/
|
||||
GPSExtractor(@NonNull InputStream stream) throws IOException {
|
||||
ExifInterface exif = new ExifInterface(stream);
|
||||
processCoords(exif);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct from the file path of the image.
|
||||
* @param path file path of the image
|
||||
*
|
||||
*/
|
||||
GPSExtractor(@NonNull String path) {
|
||||
try {
|
||||
ExifInterface exif = new ExifInterface(path);
|
||||
processCoords(exif);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Timber.w(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct from the file path of the image.
|
||||
* @param exif exif interface of the image
|
||||
*
|
||||
*/
|
||||
GPSExtractor(@NonNull ExifInterface exif){
|
||||
processCoords(exif);
|
||||
}
|
||||
|
||||
private void processCoords(ExifInterface exif){
|
||||
//If image has no EXIF data and user has enabled GPS setting, get user's location
|
||||
//Always return null as a temporary fix for #1599
|
||||
if (exif != null && exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) != null) {
|
||||
//If image has EXIF data, extract image coords
|
||||
imageCoordsExists = true;
|
||||
Timber.d("EXIF data has location info");
|
||||
|
||||
latitude = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
|
||||
latitudeRef = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
|
||||
longitude = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
|
||||
longitudeRef = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts geolocation (either of image from EXIF data, or of user)
|
||||
* @return coordinates as string (needs to be passed as a String in API query)
|
||||
*/
|
||||
@Nullable
|
||||
String getCoords() {
|
||||
if(decimalCoords!=null){
|
||||
return decimalCoords;
|
||||
}else if (latitude!=null && latitudeRef!=null && longitude!=null && longitudeRef!=null) {
|
||||
Timber.d("Latitude: %s %s", latitude, latitudeRef);
|
||||
Timber.d("Longitude: %s %s", longitude, longitudeRef);
|
||||
|
||||
decimalCoords = getDecimalCoords(latitude, latitudeRef, longitude, longitudeRef);
|
||||
return decimalCoords;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public double getDecLatitude() {
|
||||
return decLatitude;
|
||||
}
|
||||
|
||||
public double getDecLongitude() {
|
||||
return decLongitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts format of geolocation into decimal coordinates as required by MediaWiki API
|
||||
* @return the coordinates in decimals
|
||||
*/
|
||||
private String getDecimalCoords(String latitude, String latitude_ref, String longitude, String longitude_ref) {
|
||||
|
||||
if (latitude_ref.equals("N")) {
|
||||
decLatitude = convertToDegree(latitude);
|
||||
} else {
|
||||
decLatitude = 0 - convertToDegree(latitude);
|
||||
}
|
||||
|
||||
if (longitude_ref.equals("E")) {
|
||||
decLongitude = convertToDegree(longitude);
|
||||
} else {
|
||||
decLongitude = 0 - convertToDegree(longitude);
|
||||
}
|
||||
|
||||
String decimalCoords = decLatitude + "|" + decLongitude;
|
||||
Timber.d("Latitude and Longitude are %s", decimalCoords);
|
||||
return decimalCoords;
|
||||
}
|
||||
|
||||
private double convertToDegree(String stringDMS) {
|
||||
double result;
|
||||
String[] DMS = stringDMS.split(",", 3);
|
||||
|
||||
String[] stringD = DMS[0].split("/", 2);
|
||||
double d0 = Double.parseDouble(stringD[0]);
|
||||
double d1 = Double.parseDouble(stringD[1]);
|
||||
double degrees = d0/d1;
|
||||
|
||||
String[] stringM = DMS[1].split("/", 2);
|
||||
double m0 = Double.parseDouble(stringM[0]);
|
||||
double m1 = Double.parseDouble(stringM[1]);
|
||||
double minutes = m0/m1;
|
||||
|
||||
String[] stringS = DMS[2].split("/", 2);
|
||||
double s0 = Double.parseDouble(stringS[0]);
|
||||
double s1 = Double.parseDouble(stringS[1]);
|
||||
double seconds = s0/s1;
|
||||
|
||||
result = degrees + (minutes/60) + (seconds/3600);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* Extracts geolocation to be passed to API for category suggestions. If a picture with geolocation
|
||||
* is uploaded, extract latitude and longitude from EXIF data of image.
|
||||
*/
|
||||
class ImageCoordinates internal constructor(exif: ExifInterface?) {
|
||||
var decLatitude = 0.0
|
||||
var decLongitude = 0.0
|
||||
var imageCoordsExists = false
|
||||
/**
|
||||
* @return string of `"[decLatitude]|[decLongitude]"` or null if coordinates do not exist
|
||||
*/
|
||||
var decimalCoords: String? = null
|
||||
|
||||
/**
|
||||
* Construct from a stream.
|
||||
*/
|
||||
internal constructor(stream: InputStream) : this(ExifInterface(stream))
|
||||
|
||||
/**
|
||||
* Construct from the file path of the image.
|
||||
* @param path file path of the image
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
internal constructor(path: String) : this(ExifInterface(path))
|
||||
|
||||
|
||||
|
||||
init {
|
||||
//If image has no EXIF data and user has enabled GPS setting, get user's location
|
||||
//Always return null as a temporary fix for #1599
|
||||
if (exif != null) {
|
||||
val latitude = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE)
|
||||
val latitudeRef = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF)
|
||||
val longitude = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE)
|
||||
val longitudeRef = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF)
|
||||
if (latitude != null && longitude != null && latitudeRef != null && longitudeRef != null) {
|
||||
//If image has EXIF data, extract image coords
|
||||
imageCoordsExists = true
|
||||
Timber.d("EXIF data has location info")
|
||||
decLatitude =
|
||||
if (ExifInterface.LATITUDE_NORTH == latitudeRef) convertToDegree(latitude)
|
||||
else 0 - convertToDegree(latitude)
|
||||
decLongitude =
|
||||
if (ExifInterface.LONGITUDE_EAST == longitudeRef) convertToDegree(longitude)
|
||||
else 0 - convertToDegree(longitude)
|
||||
decimalCoords = "$decLatitude|$decLongitude"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to an accurate Degree
|
||||
*
|
||||
* @param degreeMinuteSecondString - template string "a/b,c/d,e/f" where the letters represent numbers
|
||||
* @return the degree accurate to the second
|
||||
*/
|
||||
private fun convertToDegree(degreeMinuteSecondString: String) =
|
||||
degreeMinuteSecondString.split(",").let {
|
||||
val degrees = evaluateExpression(it[0])
|
||||
val minutes = evaluateExpression(it[1])
|
||||
val seconds = evaluateExpression(it[2])
|
||||
degrees + minutes / 60 + seconds / 3600
|
||||
}
|
||||
|
||||
private fun evaluateExpression(dm: String) =
|
||||
dm.split("/").let { it[0].toDouble() / it[1].toDouble() }
|
||||
}
|
||||
|
|
@ -9,20 +9,16 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
|
||||
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import fr.free.nrw.commons.R;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Created by harisanker on 14/2/18.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
public interface SimilarImageInterface {
|
||||
void showSimilarImageFragment(String originalFilePath, String possibleFilePath);
|
||||
void showSimilarImageFragment(String originalFilePath, String possibleFilePath,
|
||||
ImageCoordinates similarImageCoordinates);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,7 @@ package fr.free.nrw.commons.upload;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
|
|
@ -31,26 +18,25 @@ import io.reactivex.Observable;
|
|||
import io.reactivex.Single;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import timber.log.Timber;
|
||||
|
||||
@Singleton
|
||||
public class UploadModel {
|
||||
|
||||
private static UploadItem DUMMY = new UploadItem(
|
||||
Uri.EMPTY, Uri.EMPTY,
|
||||
"",
|
||||
"",
|
||||
GPSExtractor.DUMMY,
|
||||
null,
|
||||
-1L, "") {
|
||||
};
|
||||
private final JsonKvStore store;
|
||||
private final List<String> licenses;
|
||||
private final Context context;
|
||||
private String license;
|
||||
private final Map<String, String> licensesByName;
|
||||
private List<UploadItem> items = new ArrayList<>();
|
||||
private int currentStepIndex = 0;
|
||||
private CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
|
||||
private SessionManager sessionManager;
|
||||
|
|
@ -95,19 +81,6 @@ public class UploadModel {
|
|||
this.selectedCategories = selectedCategories;
|
||||
}
|
||||
|
||||
/**
|
||||
* pre process a list of items
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
Observable<UploadItem> preProcessImages(List<UploadableFile> uploadableFiles,
|
||||
Place place,
|
||||
String source,
|
||||
SimilarImageInterface similarImageInterface) {
|
||||
return Observable.fromIterable(uploadableFiles)
|
||||
.map(uploadableFile -> getUploadItem(uploadableFile, place, source,
|
||||
similarImageInterface));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pre process a one item at a time
|
||||
|
|
@ -127,8 +100,6 @@ public class UploadModel {
|
|||
Place place,
|
||||
String source,
|
||||
SimilarImageInterface similarImageInterface) {
|
||||
fileProcessor.initFileDetails(Objects.requireNonNull(uploadableFile.getFilePath()),
|
||||
context.getContentResolver());
|
||||
UploadableFile.DateTimeWithSource dateTimeWithSource = uploadableFile
|
||||
.getFileCreatedDate(context);
|
||||
long fileCreatedDate = -1;
|
||||
|
|
@ -138,11 +109,11 @@ public class UploadModel {
|
|||
createdTimestampSource = dateTimeWithSource.getSource();
|
||||
}
|
||||
Timber.d("File created date is %d", fileCreatedDate);
|
||||
GPSExtractor gpsExtractor = fileProcessor
|
||||
.processFileCoordinates(similarImageInterface, context);
|
||||
ImageCoordinates imageCoordinates = fileProcessor
|
||||
.processFileCoordinates(similarImageInterface, uploadableFile.getFilePath());
|
||||
UploadItem uploadItem = new UploadItem(uploadableFile.getContentUri(),
|
||||
Uri.parse(uploadableFile.getFilePath()),
|
||||
uploadableFile.getMimeType(context), source, gpsExtractor, place, fileCreatedDate,
|
||||
uploadableFile.getMimeType(context), source, imageCoordinates, place, fileCreatedDate,
|
||||
createdTimestampSource);
|
||||
if (place != null) {
|
||||
uploadItem.title.setTitleText(place.name);
|
||||
|
|
@ -158,14 +129,6 @@ public class UploadModel {
|
|||
return uploadItem;
|
||||
}
|
||||
|
||||
int getCurrentStep() {
|
||||
return currentStepIndex + 1;
|
||||
}
|
||||
|
||||
int getStepCount() {
|
||||
return items.size() + 2;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return items.size();
|
||||
}
|
||||
|
|
@ -194,7 +157,7 @@ public class UploadModel {
|
|||
item.getFileName(),
|
||||
Description.formatList(item.descriptions), -1,
|
||||
null, null, sessionManager.getAuthorName(),
|
||||
CommonsApplication.DEFAULT_EDIT_SUMMARY, item.gpsCoords.getCoords());
|
||||
CommonsApplication.DEFAULT_EDIT_SUMMARY, item.gpsCoords.getDecimalCoords());
|
||||
if (item.place != null) {
|
||||
contribution.setWikiDataEntityId(item.place.getWikiDataEntityId());
|
||||
// If item already has an image, we need to know it. We don't want to override existing image later
|
||||
|
|
@ -244,6 +207,11 @@ public class UploadModel {
|
|||
uploadItem1.setTitle(uploadItem.title);
|
||||
}
|
||||
|
||||
public void useSimilarPictureCoordinates(ImageCoordinates imageCoordinates, int uploadItemIndex) {
|
||||
fileProcessor.useImageCoords(imageCoordinates);
|
||||
items.get(uploadItemIndex).setGpsCoords(imageCoordinates);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public static class UploadItem {
|
||||
|
||||
|
|
@ -251,22 +219,22 @@ public class UploadModel {
|
|||
private final Uri mediaUri;
|
||||
private final String mimeType;
|
||||
private final String source;
|
||||
private final GPSExtractor gpsCoords;
|
||||
private ImageCoordinates gpsCoords;
|
||||
|
||||
public void setGpsCoords(ImageCoordinates gpsCoords) {
|
||||
this.gpsCoords = gpsCoords;
|
||||
}
|
||||
|
||||
private boolean selected = false;
|
||||
private boolean first = false;
|
||||
private Title title;
|
||||
private List<Description> descriptions;
|
||||
private Place place;
|
||||
private boolean visited;
|
||||
private boolean error;
|
||||
private long createdTimestamp;
|
||||
private String createdTimestampSource;
|
||||
private BehaviorSubject<Integer> imageQuality;
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
UploadItem(Uri originalContentUri,
|
||||
Uri mediaUri, String mimeType, String source, GPSExtractor gpsCoords,
|
||||
Uri mediaUri, String mimeType, String source, ImageCoordinates gpsCoords,
|
||||
Place place,
|
||||
long createdTimestamp,
|
||||
String createdTimestampSource) {
|
||||
|
|
@ -287,38 +255,18 @@ public class UploadModel {
|
|||
return createdTimestampSource;
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public GPSExtractor getGpsCoords() {
|
||||
public ImageCoordinates getGpsCoords() {
|
||||
return gpsCoords;
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public boolean isFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public List<Description> getDescriptions() {
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
public boolean isVisited() {
|
||||
return visited;
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public long getCreatedTimestamp() {
|
||||
return createdTimestamp;
|
||||
}
|
||||
|
|
@ -373,10 +321,9 @@ public class UploadModel {
|
|||
|
||||
}
|
||||
|
||||
//Travis is complaining :P
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
return mediaUri.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package fr.free.nrw.commons.upload.mediaDetails;
|
||||
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.getErrorMessageForResult;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
|
@ -12,30 +14,17 @@ import android.view.ViewGroup;
|
|||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatButton;
|
||||
import androidx.appcompat.widget.AppCompatImageButton;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.github.chrisbanes.photoview.PhotoView;
|
||||
import com.jakewharton.rxbinding2.widget.RxTextView;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import com.github.chrisbanes.photoview.PhotoView;
|
||||
import com.jakewharton.rxbinding2.widget.RxTextView;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||
|
|
@ -45,6 +34,7 @@ import fr.free.nrw.commons.nearby.Place;
|
|||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.upload.Description;
|
||||
import fr.free.nrw.commons.upload.DescriptionsAdapter;
|
||||
import fr.free.nrw.commons.upload.ImageCoordinates;
|
||||
import fr.free.nrw.commons.upload.SimilarImageDialogFragment;
|
||||
import fr.free.nrw.commons.upload.Title;
|
||||
import fr.free.nrw.commons.upload.UploadBaseFragment;
|
||||
|
|
@ -54,10 +44,15 @@ import fr.free.nrw.commons.utils.DialogUtil;
|
|||
import fr.free.nrw.commons.utils.ImageUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.getErrorMessageForResult;
|
||||
|
||||
public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
||||
UploadMediaDetailsContract.View {
|
||||
|
||||
|
|
@ -231,14 +226,6 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
rvDescriptions.setAdapter(descriptionsAdapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the default locale value of the user's device
|
||||
* @return
|
||||
*/
|
||||
private String getUserDefaultLocale() {
|
||||
return getContext().getResources().getConfiguration().locale.getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* show dialog with info
|
||||
* @param titleStringID
|
||||
|
|
@ -267,12 +254,14 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public void showSimilarImageFragment(String originalFilePath, String possibleFilePath) {
|
||||
public void showSimilarImageFragment(String originalFilePath, String possibleFilePath,
|
||||
ImageCoordinates similarImageCoordinates) {
|
||||
SimilarImageDialogFragment newFragment = new SimilarImageDialogFragment();
|
||||
newFragment.setCallback(new SimilarImageDialogFragment.Callback() {
|
||||
@Override
|
||||
public void onPositiveResponse() {
|
||||
Timber.d("positive response from similar image fragment");
|
||||
presenter.useSimilarPictureCoordinates(similarImageCoordinates, callback.getIndexInViewFlipper(UploadMediaDetailFragment.this));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package fr.free.nrw.commons.upload.mediaDetails;
|
||||
|
||||
import fr.free.nrw.commons.upload.ImageCoordinates;
|
||||
import java.util.List;
|
||||
|
||||
import fr.free.nrw.commons.BasePresenter;
|
||||
|
|
@ -48,6 +49,9 @@ public interface UploadMediaDetailsContract {
|
|||
void setUploadItem(int index, UploadItem uploadItem);
|
||||
|
||||
void fetchPreviousTitleAndDescription(int indexInViewFlipper);
|
||||
|
||||
void useSimilarPictureCoordinates(ImageCoordinates imageCoordinates, int uploadItemIndex);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
package fr.free.nrw.commons.upload.mediaDetails;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.MAIN_THREAD;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.EMPTY_TITLE;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.FILE_NAME_EXISTS;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_KEEP;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import fr.free.nrw.commons.repository.UploadRepository;
|
||||
import fr.free.nrw.commons.upload.GPSExtractor;
|
||||
import fr.free.nrw.commons.upload.ImageCoordinates;
|
||||
import fr.free.nrw.commons.upload.SimilarImageInterface;
|
||||
import fr.free.nrw.commons.upload.UploadModel.UploadItem;
|
||||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailsContract.UserActionListener;
|
||||
|
|
@ -18,15 +20,11 @@ import io.reactivex.Observable;
|
|||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import java.lang.reflect.Proxy;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.MAIN_THREAD;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.EMPTY_TITLE;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.FILE_NAME_EXISTS;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_KEEP;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK;
|
||||
|
||||
public class UploadMediaPresenter implements UserActionListener, SimilarImageInterface {
|
||||
|
||||
private static final UploadMediaDetailsContract.View DUMMY = (UploadMediaDetailsContract.View) Proxy
|
||||
|
|
@ -81,10 +79,10 @@ public class UploadMediaPresenter implements UserActionListener, SimilarImageInt
|
|||
.subscribe(uploadItem ->
|
||||
{
|
||||
view.onImageProcessed(uploadItem, place);
|
||||
GPSExtractor gpsCoords = uploadItem.getGpsCoords();
|
||||
view.showMapWithImageCoordinates(gpsCoords != null && gpsCoords.imageCoordsExists);
|
||||
ImageCoordinates gpsCoords = uploadItem.getGpsCoords();
|
||||
view.showMapWithImageCoordinates(gpsCoords != null && gpsCoords.getImageCoordsExists());
|
||||
view.showProgress(false);
|
||||
if (gpsCoords != null && gpsCoords.imageCoordsExists) {
|
||||
if (gpsCoords != null && gpsCoords.getImageCoordsExists()) {
|
||||
checkNearbyPlaces(uploadItem);
|
||||
}
|
||||
},
|
||||
|
|
@ -159,7 +157,12 @@ public class UploadMediaPresenter implements UserActionListener, SimilarImageInt
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@Override
|
||||
public void useSimilarPictureCoordinates(ImageCoordinates imageCoordinates, int uploadItemIndex) {
|
||||
repository.useSimilarPictureCoordinates(imageCoordinates, uploadItemIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* handles image quality verifications
|
||||
*
|
||||
* @param imageResult
|
||||
|
|
@ -200,12 +203,15 @@ public class UploadMediaPresenter implements UserActionListener, SimilarImageInt
|
|||
|
||||
/**
|
||||
* notifies the user that a similar image exists
|
||||
*
|
||||
* @param originalFilePath
|
||||
* @param possibleFilePath
|
||||
* @param similarImageCoordinates
|
||||
*/
|
||||
@Override
|
||||
public void showSimilarImageFragment(String originalFilePath, String possibleFilePath) {
|
||||
view.showSimilarImageFragment(originalFilePath, possibleFilePath);
|
||||
public void showSimilarImageFragment(String originalFilePath, String possibleFilePath,
|
||||
ImageCoordinates similarImageCoordinates) {
|
||||
view.showSimilarImageFragment(originalFilePath, possibleFilePath,
|
||||
similarImageCoordinates
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import fr.free.nrw.commons.caching.CacheController
|
||||
import fr.free.nrw.commons.mwapi.CategoryApi
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
|
||||
class FileProcessorTest {
|
||||
|
||||
@Mock
|
||||
internal var cacheController: CacheController? = null
|
||||
@Mock
|
||||
internal var gpsCategoryModel: GpsCategoryModel? = null
|
||||
@Mock
|
||||
internal var apiCall: CategoryApi? = null
|
||||
@Mock
|
||||
@field:[Inject Named("default_preferences")]
|
||||
internal var prefs: SharedPreferences? = null
|
||||
|
||||
@InjectMocks
|
||||
var fileProcessor: FileProcessor? = null
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun processFileCoordinates() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method to verify redaction Exif metadata
|
||||
*/
|
||||
@Test
|
||||
fun redactExifTags() {
|
||||
/*
|
||||
val filePathRef: String? = "src/test/data/exif_redact_sample.jpg"
|
||||
val filePathTmp: String? = "" + System.getProperty("java.io.tmpdir") + "exif_redact_sample_tmp.jpg"
|
||||
|
||||
val inStream = FileInputStream(filePathRef)
|
||||
val outStream = FileOutputStream(filePathTmp)
|
||||
val inChannel = inStream.getChannel()
|
||||
val outChannel = outStream.getChannel()
|
||||
inChannel.transferTo(0, inChannel.size(), outChannel)
|
||||
inStream.close()
|
||||
outStream.close()
|
||||
|
||||
val redactTags = mutableSetOf("Author", "Copyright", "Location", "Camera Model",
|
||||
"Lens Model", "Serial Numbers", "Software")
|
||||
|
||||
val exifInterface : ExifInterface? = ExifInterface(filePathTmp.toString())
|
||||
|
||||
var nonEmptyTag = false
|
||||
for (redactTag in redactTags) {
|
||||
for (tag in FileMetadataUtils.getTagsFromPref(redactTag)) {
|
||||
val tagValue = exifInterface?.getAttribute(tag)
|
||||
if(tagValue != null) {
|
||||
nonEmptyTag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (nonEmptyTag) break
|
||||
}
|
||||
// all tags are empty, can't test redaction
|
||||
assert(nonEmptyTag)
|
||||
|
||||
FileProcessor.redactExifTags(exifInterface, redactTags)
|
||||
|
||||
for (redactTag in redactTags) {
|
||||
for (tag in FileMetadataUtils.getTagsFromPref(redactTag)) {
|
||||
val oldValue = exifInterface?.getAttribute(tag)
|
||||
assert(oldValue == null)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.filepicker.UploadableFile
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
import fr.free.nrw.commons.repository.UploadRepository
|
||||
|
|
@ -14,7 +16,6 @@ import org.junit.Test
|
|||
import org.mockito.ArgumentMatchers
|
||||
import org.mockito.ArgumentMatchers.eq
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
|
|
@ -24,31 +25,32 @@ import org.mockito.MockitoAnnotations
|
|||
*/
|
||||
class UploadMediaPresenterTest {
|
||||
@Mock
|
||||
internal var repository: UploadRepository? = null
|
||||
@Mock
|
||||
internal var view: UploadMediaDetailsContract.View? = null
|
||||
|
||||
private var uploadMediaPresenter: UploadMediaPresenter? = null
|
||||
internal lateinit var repository: UploadRepository
|
||||
|
||||
@Mock
|
||||
private var uploadableFile: UploadableFile? = null
|
||||
internal lateinit var view: UploadMediaDetailsContract.View
|
||||
|
||||
private lateinit var uploadMediaPresenter: UploadMediaPresenter
|
||||
|
||||
@Mock
|
||||
private var place: Place? = null
|
||||
private lateinit var uploadableFile: UploadableFile
|
||||
|
||||
@Mock
|
||||
private var uploadItem: UploadModel.UploadItem? = null
|
||||
private lateinit var place: Place
|
||||
|
||||
@Mock
|
||||
private var title: Title? = null
|
||||
private lateinit var uploadItem: UploadModel.UploadItem
|
||||
|
||||
@Mock
|
||||
private var descriptions: List<Description>? = null
|
||||
private lateinit var title: Title
|
||||
|
||||
private var testObservableUploadItem: Observable<UploadModel.UploadItem>? = null
|
||||
private var testSingleImageResult: Single<Int>? = null
|
||||
@Mock
|
||||
private lateinit var descriptions: List<Description>
|
||||
|
||||
private var testScheduler: TestScheduler? = null
|
||||
private lateinit var testObservableUploadItem: Observable<UploadModel.UploadItem>
|
||||
private lateinit var testSingleImageResult: Single<Int>
|
||||
|
||||
private lateinit var testScheduler: TestScheduler
|
||||
|
||||
/**
|
||||
* initial setup unit test environment
|
||||
|
|
@ -61,7 +63,7 @@ class UploadMediaPresenterTest {
|
|||
testSingleImageResult = Single.just(1)
|
||||
testScheduler = TestScheduler()
|
||||
uploadMediaPresenter = UploadMediaPresenter(repository, testScheduler, testScheduler)
|
||||
uploadMediaPresenter?.onAttachView(view)
|
||||
uploadMediaPresenter.onAttachView(view)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,12 +71,22 @@ class UploadMediaPresenterTest {
|
|||
*/
|
||||
@Test
|
||||
fun receiveImageTest() {
|
||||
Mockito.`when`(repository?.preProcessImage(ArgumentMatchers.any(UploadableFile::class.java), ArgumentMatchers.any(Place::class.java), ArgumentMatchers.anyString(), ArgumentMatchers.any(UploadMediaPresenter::class.java))).thenReturn(testObservableUploadItem)
|
||||
uploadMediaPresenter?.receiveImage(uploadableFile, ArgumentMatchers.anyString(), place)
|
||||
verify(view)?.showProgress(true)
|
||||
testScheduler?.triggerActions()
|
||||
verify(view)?.onImageProcessed(ArgumentMatchers.any(UploadModel.UploadItem::class.java), ArgumentMatchers.any(Place::class.java))
|
||||
verify(view)?.showProgress(false)
|
||||
whenever(
|
||||
repository.preProcessImage(
|
||||
ArgumentMatchers.any(UploadableFile::class.java),
|
||||
ArgumentMatchers.any(Place::class.java),
|
||||
ArgumentMatchers.anyString(),
|
||||
ArgumentMatchers.any(UploadMediaPresenter::class.java)
|
||||
)
|
||||
).thenReturn(testObservableUploadItem)
|
||||
uploadMediaPresenter.receiveImage(uploadableFile, ArgumentMatchers.anyString(), place)
|
||||
verify(view).showProgress(true)
|
||||
testScheduler.triggerActions()
|
||||
verify(view).onImageProcessed(
|
||||
ArgumentMatchers.any(UploadModel.UploadItem::class.java),
|
||||
ArgumentMatchers.any(Place::class.java)
|
||||
)
|
||||
verify(view).showProgress(false)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -82,12 +94,13 @@ class UploadMediaPresenterTest {
|
|||
*/
|
||||
@Test
|
||||
fun verifyImageQualityTest() {
|
||||
Mockito.`when`(repository?.getImageQuality(ArgumentMatchers.any(UploadModel.UploadItem::class.java))).thenReturn(testSingleImageResult)
|
||||
Mockito.`when`(uploadItem?.imageQuality).thenReturn(ArgumentMatchers.anyInt())
|
||||
uploadMediaPresenter?.verifyImageQuality(uploadItem)
|
||||
verify(view)?.showProgress(true)
|
||||
testScheduler?.triggerActions()
|
||||
verify(view)?.showProgress(false)
|
||||
whenever(repository.getImageQuality(ArgumentMatchers.any(UploadModel.UploadItem::class.java)))
|
||||
.thenReturn(testSingleImageResult)
|
||||
whenever(uploadItem.imageQuality).thenReturn(ArgumentMatchers.anyInt())
|
||||
uploadMediaPresenter.verifyImageQuality(uploadItem)
|
||||
verify(view).showProgress(true)
|
||||
testScheduler.triggerActions()
|
||||
verify(view).showProgress(false)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -96,21 +109,21 @@ class UploadMediaPresenterTest {
|
|||
@Test
|
||||
fun handleImageResult() {
|
||||
//Positive case test
|
||||
uploadMediaPresenter?.handleImageResult(IMAGE_KEEP)
|
||||
verify(view)?.onImageValidationSuccess()
|
||||
uploadMediaPresenter.handleImageResult(IMAGE_KEEP)
|
||||
verify(view).onImageValidationSuccess()
|
||||
|
||||
//Duplicate file name
|
||||
uploadMediaPresenter?.handleImageResult(FILE_NAME_EXISTS)
|
||||
verify(view)?.showDuplicatePicturePopup()
|
||||
uploadMediaPresenter.handleImageResult(FILE_NAME_EXISTS)
|
||||
verify(view).showDuplicatePicturePopup()
|
||||
|
||||
//Empty Title test
|
||||
uploadMediaPresenter?.handleImageResult(EMPTY_TITLE)
|
||||
verify(view)?.showMessage(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())
|
||||
uploadMediaPresenter.handleImageResult(EMPTY_TITLE)
|
||||
verify(view).showMessage(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())
|
||||
|
||||
//Bad Picture test
|
||||
//Empty Title test
|
||||
uploadMediaPresenter?.handleImageResult(-7)
|
||||
verify(view)?.showBadImagePopup(ArgumentMatchers.anyInt())
|
||||
uploadMediaPresenter.handleImageResult(-7)
|
||||
verify(view).showBadImagePopup(ArgumentMatchers.anyInt())
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -118,52 +131,54 @@ class UploadMediaPresenterTest {
|
|||
* Test fetch previous image title when there was one
|
||||
*/
|
||||
@Test
|
||||
fun fetchPreviousImageAndTitleTestPositive(){
|
||||
Mockito.`when`(repository?.getPreviousUploadItem(ArgumentMatchers.anyInt())).thenReturn(uploadItem)
|
||||
Mockito.`when`(uploadItem?.descriptions).thenReturn(descriptions)
|
||||
Mockito.`when`(uploadItem?.title).thenReturn(title)
|
||||
Mockito.`when`(title?.getTitleText()).thenReturn(ArgumentMatchers.anyString())
|
||||
fun fetchPreviousImageAndTitleTestPositive() {
|
||||
whenever(repository.getPreviousUploadItem(ArgumentMatchers.anyInt()))
|
||||
.thenReturn(uploadItem)
|
||||
whenever(uploadItem.descriptions).thenReturn(descriptions)
|
||||
whenever(uploadItem.title).thenReturn(title)
|
||||
whenever(title.getTitleText()).thenReturn(ArgumentMatchers.anyString())
|
||||
|
||||
uploadMediaPresenter?.fetchPreviousTitleAndDescription(0)
|
||||
verify(view)?.setTitleAndDescription(ArgumentMatchers.anyString(),ArgumentMatchers.any())
|
||||
uploadMediaPresenter.fetchPreviousTitleAndDescription(0)
|
||||
verify(view).setTitleAndDescription(ArgumentMatchers.anyString(), ArgumentMatchers.any())
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fetch previous image title when there was none
|
||||
*/
|
||||
@Test
|
||||
fun fetchPreviousImageAndTitleTestNegative(){
|
||||
Mockito.`when`(repository?.getPreviousUploadItem(ArgumentMatchers.anyInt())).thenReturn(null)
|
||||
uploadMediaPresenter?.fetchPreviousTitleAndDescription(0)
|
||||
verify(view)?.showMessage(ArgumentMatchers.anyInt(),ArgumentMatchers.anyInt())
|
||||
fun fetchPreviousImageAndTitleTestNegative() {
|
||||
whenever(repository.getPreviousUploadItem(ArgumentMatchers.anyInt()))
|
||||
.thenReturn(null)
|
||||
uploadMediaPresenter.fetchPreviousTitleAndDescription(0)
|
||||
verify(view).showMessage(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())
|
||||
}
|
||||
|
||||
/**
|
||||
* Test bad image invalid location
|
||||
*/
|
||||
@Test
|
||||
fun handleBadImageBaseTestInvalidLocation(){
|
||||
uploadMediaPresenter?.handleBadImage(8)
|
||||
verify(repository)?.saveValue(ArgumentMatchers.anyString(),eq(false))
|
||||
verify(view)?.showBadImagePopup(8)
|
||||
fun handleBadImageBaseTestInvalidLocation() {
|
||||
uploadMediaPresenter.handleBadImage(8)
|
||||
verify(repository).saveValue(ArgumentMatchers.anyString(), eq(false))
|
||||
verify(view).showBadImagePopup(8)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test bad image empty title
|
||||
*/
|
||||
@Test
|
||||
fun handleBadImageBaseTestEmptyTitle(){
|
||||
uploadMediaPresenter?.handleBadImage(-3)
|
||||
verify(view)?.showMessage(ArgumentMatchers.anyInt(),ArgumentMatchers.anyInt())
|
||||
fun handleBadImageBaseTestEmptyTitle() {
|
||||
uploadMediaPresenter.handleBadImage(-3)
|
||||
verify(view).showMessage(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())
|
||||
}
|
||||
|
||||
/**
|
||||
* Teste show file already exists
|
||||
*/
|
||||
@Test
|
||||
fun handleBadImageBaseTestFileNameExists(){
|
||||
uploadMediaPresenter?.handleBadImage(-4)
|
||||
verify(view)?.showDuplicatePicturePopup()
|
||||
fun handleBadImageBaseTestFileNameExists() {
|
||||
uploadMediaPresenter.handleBadImage(-4)
|
||||
verify(view).showDuplicatePicturePopup()
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -171,18 +186,19 @@ class UploadMediaPresenterTest {
|
|||
* Test show SimilarImageFragment
|
||||
*/
|
||||
@Test
|
||||
fun showSimilarImageFragmentTest(){
|
||||
uploadMediaPresenter?.showSimilarImageFragment(ArgumentMatchers.anyString(),ArgumentMatchers.anyString())
|
||||
verify(view)?.showSimilarImageFragment(ArgumentMatchers.anyString(),ArgumentMatchers.anyString())
|
||||
fun showSimilarImageFragmentTest() {
|
||||
val similar: ImageCoordinates = mock()
|
||||
uploadMediaPresenter.showSimilarImageFragment("original", "possible", similar)
|
||||
verify(view).showSimilarImageFragment("original", "possible", similar)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test set upload item
|
||||
*/
|
||||
@Test
|
||||
fun setUploadItemTest(){
|
||||
uploadMediaPresenter?.setUploadItem(0,uploadItem)
|
||||
verify(repository)?.updateUploadItem(0,uploadItem)
|
||||
fun setUploadItemTest() {
|
||||
uploadMediaPresenter.setUploadItem(0, uploadItem)
|
||||
verify(repository).updateUploadItem(0, uploadItem)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,132 +0,0 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.filepicker.UploadableFile
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
import fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK
|
||||
import io.reactivex.Single
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers.*
|
||||
import org.mockito.InjectMocks
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
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
|
||||
|
||||
|
||||
class UploadModelTest {
|
||||
|
||||
@Mock
|
||||
@field:[Inject Named("licenses")]
|
||||
internal var licenses: List<String>? = null
|
||||
@Mock
|
||||
@field:[Inject Named("default_preferences")]
|
||||
internal var prefs: JsonKvStore? = null
|
||||
@Mock
|
||||
@field:[Inject Named("licenses_by_name")]
|
||||
internal var licensesByName: Map<String, String>? = null
|
||||
@Mock
|
||||
internal var context: Context? = null
|
||||
@Mock
|
||||
internal var sessionManage: SessionManager? = null
|
||||
@Mock
|
||||
internal var fileUtilsWrapper: FileUtilsWrapper? = null
|
||||
@Mock
|
||||
internal var fileProcessor: FileProcessor? = null
|
||||
@Mock
|
||||
internal var imageProcessingService: ImageProcessingService? = null
|
||||
|
||||
@InjectMocks
|
||||
var uploadModel: UploadModel? = null
|
||||
|
||||
@Before
|
||||
@Throws(Exception::class)
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
`when`(context!!.applicationContext)
|
||||
.thenReturn(mock(Application::class.java))
|
||||
`when`(fileUtilsWrapper!!.getFileExt(anyString()))
|
||||
.thenReturn("jpg")
|
||||
`when`(fileUtilsWrapper!!.getSHA1(any(InputStream::class.java)))
|
||||
.thenReturn("sha")
|
||||
`when`(fileUtilsWrapper!!.getFileInputStream(anyString()))
|
||||
.thenReturn(mock(FileInputStream::class.java))
|
||||
`when`(fileUtilsWrapper!!.getGeolocationOfFile(anyString()))
|
||||
.thenReturn("")
|
||||
`when`(imageProcessingService!!.validateImage(any(UploadModel.UploadItem::class.java)))
|
||||
.thenReturn(Single.just(IMAGE_OK))
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
@Throws(Exception::class)
|
||||
fun tearDown() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun receive() {
|
||||
val preProcessImages = uploadModel!!.preProcessImages(getMediaList(), mock(Place::class.java), "external") { _, _ -> }
|
||||
preProcessImages.doOnComplete {
|
||||
assertTrue(uploadModel!!.items.size == 2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCurrentStep() {
|
||||
uploadModel!!.preProcessImages(getMediaList(), mock(Place::class.java), "external") { _, _ -> }
|
||||
assertTrue(uploadModel!!.currentStep == 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getStepCount() {
|
||||
val preProcessImages = uploadModel!!.preProcessImages(getMediaList(), mock(Place::class.java), "external") { _, _ -> }
|
||||
preProcessImages.doOnComplete {
|
||||
assertTrue(uploadModel!!.stepCount == 4)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCount() {
|
||||
val preProcessImages = uploadModel!!.preProcessImages(getMediaList(), mock(Place::class.java), "external") { _, _ -> }
|
||||
preProcessImages.doOnComplete {
|
||||
assertTrue(uploadModel!!.count == 2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getUploads() {
|
||||
val preProcessImages = uploadModel!!.preProcessImages(getMediaList(), mock(Place::class.java), "external") { _, _ -> }
|
||||
preProcessImages.doOnComplete {
|
||||
assertTrue(uploadModel!!.uploads.size == 2)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMediaList(): List<UploadableFile> {
|
||||
val element = getElement()
|
||||
val element2 = getElement()
|
||||
var uriList: List<UploadableFile> = mutableListOf(element, element2)
|
||||
return uriList
|
||||
}
|
||||
|
||||
private fun getElement(): UploadableFile {
|
||||
val mock = mock(UploadableFile::class.java)
|
||||
`when`(mock.filePath).thenReturn(UUID.randomUUID().toString() + "/filePath.jpg")
|
||||
return mock
|
||||
}
|
||||
|
||||
@Test
|
||||
fun buildContributions() {
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue