diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java index 1af160a5b..6f2538608 100644 --- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java +++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java @@ -42,7 +42,6 @@ import fr.free.nrw.commons.logging.LogUtils; import fr.free.nrw.commons.modifications.ModifierSequenceDao; import fr.free.nrw.commons.upload.FileUtils; import fr.free.nrw.commons.utils.ConfigUtils; -import fr.free.nrw.commons.utils.ContributionUtils; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; @@ -113,9 +112,6 @@ public class CommonsApplication extends Application { // TODO: Remove when we're able to initialize Fresco in test builds. } - // Empty temp directory in case some temp files are created and never removed. - ContributionUtils.emptyTemporaryDirectory(); - if (BuildConfig.DEBUG && !isRoboUnitTest()) { Stetho.initializeWithDefaults(this); } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java index 4b73e228d..79a659ee2 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java @@ -421,7 +421,7 @@ public class LoginActivity extends AccountAuthenticatorActivity { } public void startMainActivity() { - NavigationBaseActivity.startActivityWithFlags(this, MainActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP); + NavigationBaseActivity.startActivityWithFlags(this, MainActivity.class, Intent.FLAG_ACTIVITY_SINGLE_TOP); finish(); } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java index 564a89e0a..575749d0d 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java @@ -1,11 +1,11 @@ package fr.free.nrw.commons.contributions; import android.Manifest; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; -import android.support.v4.app.Fragment; import com.esafirm.imagepicker.features.ImagePicker; @@ -59,40 +59,40 @@ public class ContributionController { this.directKvStore = directKvStore; } - public void initiateCameraPick(Fragment fragment, + public void initiateCameraPick(Activity activity, int requestCode) { boolean useExtStorage = defaultKvStore.getBoolean("useExternalStorage", true); if (!useExtStorage) { - initiateCameraUpload(fragment, requestCode); + initiateCameraUpload(activity, requestCode); return; } - PermissionUtils.checkPermissionsAndPerformAction(fragment.getActivity(), + PermissionUtils.checkPermissionsAndPerformAction(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, - () -> initiateCameraUpload(fragment, requestCode), + () -> initiateCameraUpload(activity, requestCode), R.string.storage_permission_title, R.string.write_storage_permission_rationale); } - public void initiateGalleryPick(Fragment fragment, + public void initiateGalleryPick(Activity activity, int imageLimit, int requestCode) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) { - initiateGalleryUpload(fragment, imageLimit, requestCode); + initiateGalleryUpload(activity, imageLimit, requestCode); } else { - PermissionUtils.checkPermissionsAndPerformAction(fragment.getActivity(), + PermissionUtils.checkPermissionsAndPerformAction(activity, Manifest.permission.READ_EXTERNAL_STORAGE, - () -> initiateGalleryUpload(fragment, imageLimit, requestCode), + () -> initiateGalleryUpload(activity, imageLimit, requestCode), R.string.storage_permission_title, R.string.read_storage_permission_rationale); } } - private void initiateGalleryUpload(Fragment fragment, + private void initiateGalleryUpload(Activity activity, int imageLimit, int requestCode) { ImagePicker imagePicker = ImagePicker.ImagePickerWithFragment - .create(fragment) + .create(activity) .showCamera(false) .folderMode(true) .includeVideo(false) @@ -106,9 +106,9 @@ public class ContributionController { } } - private void initiateCameraUpload(Fragment fragment, int requestCode) { + private void initiateCameraUpload(Activity activity, int requestCode) { ImagePicker.cameraOnly() - .start(fragment, requestCode); + .start(activity, requestCode); } public Intent handleImagesPicked(ArrayList uriList, int requestCode) { diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java index 6c662e660..740062a31 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java @@ -16,11 +16,6 @@ import android.widget.ListAdapter; import android.widget.ProgressBar; import android.widget.TextView; -import com.esafirm.imagepicker.features.ImagePicker; -import com.esafirm.imagepicker.model.Image; - -import java.util.List; - import javax.inject.Inject; import javax.inject.Named; @@ -31,8 +26,6 @@ import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.utils.ConfigUtils; -import fr.free.nrw.commons.utils.ImageUtils; -import fr.free.nrw.commons.utils.IntentUtils; import static android.view.View.GONE; import static android.view.View.VISIBLE; @@ -104,8 +97,8 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment { private void setListeners() { fabPlus.setOnClickListener(view -> animateFAB(isFabOpen)); - fabCamera.setOnClickListener(view -> controller.initiateCameraPick(this, CAMERA_UPLOAD_REQUEST_CODE)); - fabGallery.setOnClickListener(view -> controller.initiateGalleryPick(this, MULTIPLE_UPLOAD_IMAGE_LIMIT, GALLERY_UPLOAD_REQUEST_CODE)); + fabCamera.setOnClickListener(view -> controller.initiateCameraPick(getActivity(), CAMERA_UPLOAD_REQUEST_CODE)); + fabGallery.setOnClickListener(view -> controller.initiateGalleryPick(getActivity(), MULTIPLE_UPLOAD_IMAGE_LIMIT, GALLERY_UPLOAD_REQUEST_CODE)); } private void animateFAB(boolean isFabOpen) { @@ -135,18 +128,6 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment { parentFragment.waitForContributionsListFragment.countDown(); } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (IntentUtils.shouldContributionsListHandle(requestCode, resultCode, data)) { - List images = ImagePicker.getImages(data); - Intent shareIntent = controller.handleImagesPicked(ImageUtils.getUriListFromImages(images), requestCode); - startActivity(shareIntent); - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - - /** * Responsible to set progress bar invisible and visible * @param isVisible True when contributions list should be hidden. diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index 0446007db..f2b3771f2 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -17,6 +17,11 @@ import android.view.MenuItem; import android.view.View; import android.widget.ImageView; +import com.esafirm.imagepicker.features.ImagePicker; +import com.esafirm.imagepicker.model.Image; + +import java.util.List; + import javax.inject.Inject; import javax.inject.Named; @@ -32,6 +37,8 @@ import fr.free.nrw.commons.nearby.NearbyFragment; import fr.free.nrw.commons.nearby.NearbyNotificationCardView; import fr.free.nrw.commons.notification.NotificationActivity; import fr.free.nrw.commons.upload.UploadService; +import fr.free.nrw.commons.utils.ImageUtils; +import fr.free.nrw.commons.utils.IntentUtils; import timber.log.Timber; import static android.content.ContentResolver.requestSync; @@ -41,6 +48,7 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag @Inject SessionManager sessionManager; + @Inject ContributionController controller; @BindView(R.id.tab_layout) TabLayout tabLayout; @BindView(R.id.pager) @@ -438,12 +446,13 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - ContributionsListFragment contributionsListFragment = - (ContributionsListFragment) contributionsActivityPagerAdapter - .getItem(0).getChildFragmentManager() - .findFragmentByTag(ContributionsFragment.CONTRIBUTION_LIST_FRAGMENT_TAG); - contributionsListFragment.onActivityResult(requestCode, resultCode, data); + if (IntentUtils.shouldContributionsListHandle(requestCode, resultCode, data)) { + List images = ImagePicker.getImages(data); + Intent shareIntent = controller.handleImagesPicked(ImageUtils.getUriListFromImages(images), requestCode); + startActivity(shareIntent); + } else { + super.onActivityResult(requestCode, resultCode, data); + } } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index e1cb86960..da73094e5 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -52,7 +52,6 @@ import fr.free.nrw.commons.category.QueryContinue; import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.notification.Notification; import fr.free.nrw.commons.notification.NotificationUtils; -import fr.free.nrw.commons.utils.ContributionUtils; import fr.free.nrw.commons.utils.DateUtils; import fr.free.nrw.commons.utils.ViewUtil; import in.yuvi.http.fluent.Http; @@ -910,8 +909,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { } return new UploadResult(resultStatus, errorCode); } else { - // If success we have to remove file from temp directory - ContributionUtils.removeTemporaryFile(fileUri); Date dateUploaded = parseMWDate(result.getString("/api/upload/imageinfo/@timestamp")); String canonicalFilename = "File:" + result.getString("/api/upload/@filename").replace("_", " "); // Title vs Filename String imageUrl = result.getString("/api/upload/imageinfo/@url"); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java index f3dabb07f..fda32c31f 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java @@ -10,8 +10,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.esafirm.imagepicker.features.ImagePicker; -import com.esafirm.imagepicker.model.Image; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; @@ -31,12 +29,9 @@ import fr.free.nrw.commons.contributions.ContributionController; import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.utils.ImageUtils; import fr.free.nrw.commons.utils.UriDeserializer; import timber.log.Timber; -import static fr.free.nrw.commons.utils.IntentUtils.shouldNearbyHandle; - public class NearbyListFragment extends DaggerFragment { private Bundle bundleForUpdates; // Carry information from activity about changed nearby places and current location @@ -134,17 +129,6 @@ public class NearbyListFragment extends DaggerFragment { return placeList; } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (shouldNearbyHandle(requestCode, resultCode, data)) { - List images = ImagePicker.getImages(data); - Intent shareIntent = controller.handleImagesPicked(ImageUtils.getUriListFromImages(images), requestCode); - startActivity(shareIntent); - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - /** * Sets bundles for updates in map. Ie. user is moved too much so we need to update nearby markers. * @param bundleForUpdates includes new calculated nearby places. diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java index 7e436a215..338696864 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java @@ -26,8 +26,6 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; -import com.esafirm.imagepicker.features.ImagePicker; -import com.esafirm.imagepicker.model.Image; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; @@ -868,7 +866,7 @@ public class NearbyMapFragment extends DaggerFragment { if (fabCamera.isShown()) { Timber.d("Camera button tapped. Place: %s", place.toString()); storeSharedPrefs(); - controller.initiateCameraPick(this, NEARBY_CAMERA_UPLOAD_REQUEST_CODE); + controller.initiateCameraPick(getActivity(), NEARBY_CAMERA_UPLOAD_REQUEST_CODE); } }); @@ -876,7 +874,7 @@ public class NearbyMapFragment extends DaggerFragment { if (fabGallery.isShown()) { Timber.d("Gallery button tapped. Place: %s", place.toString()); storeSharedPrefs(); - controller.initiateGalleryPick(this, NEARBY_UPLOAD_IMAGE_LIMIT, NEARBY_GALLERY_UPLOAD_REQUEST_CODE); + controller.initiateGalleryPick(getActivity(), NEARBY_UPLOAD_IMAGE_LIMIT, NEARBY_GALLERY_UPLOAD_REQUEST_CODE); } }); } @@ -886,17 +884,6 @@ public class NearbyMapFragment extends DaggerFragment { directKvStore.putJson(PLACE_OBJECT, place); } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (IntentUtils.shouldNearbyHandle(requestCode, resultCode, data)) { - List images = ImagePicker.getImages(data); - Intent shareIntent = controller.handleImagesPicked(ImageUtils.getUriListFromImages(images), requestCode); - startActivity(shareIntent); - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - private void openWebView(Uri link) { Utils.handleWebUrl(getContext(), link); } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java index 52c30013a..df961c6a5 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java @@ -144,7 +144,7 @@ public class PlaceRenderer extends Renderer { } else { Timber.d("Camera button tapped. Image title: " + place.getName() + "Image desc: " + place.getLongDescription()); storeSharedPrefs(); - controller.initiateCameraPick(fragment, NEARBY_CAMERA_UPLOAD_REQUEST_CODE); + controller.initiateCameraPick(fragment.getActivity(), NEARBY_CAMERA_UPLOAD_REQUEST_CODE); } }); @@ -164,7 +164,7 @@ public class PlaceRenderer extends Renderer { }else { Timber.d("Gallery button tapped. Image title: " + place.getName() + "Image desc: " + place.getLongDescription()); storeSharedPrefs(); - controller.initiateGalleryPick(fragment, NEARBY_UPLOAD_IMAGE_LIMIT, NEARBY_GALLERY_UPLOAD_REQUEST_CODE); + controller.initiateGalleryPick(fragment.getActivity(), NEARBY_UPLOAD_IMAGE_LIMIT, NEARBY_GALLERY_UPLOAD_REQUEST_CODE); } }); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java index 2e6752230..40eedcd1e 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java @@ -18,19 +18,14 @@ import android.support.annotation.Nullable; import java.io.BufferedReader; import java.io.File; -import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.OutputStreamWriter; import java.math.BigInteger; -import java.nio.channels.FileChannel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Date; import timber.log.Timber; @@ -94,256 +89,6 @@ public class FileUtils { } } - static String createCopyPathAndCopy(boolean useExternalStorage, - Uri uri, - ContentResolver contentResolver, - Context context) throws IOException { - return useExternalStorage ? createExternalCopyPathAndCopy(uri, contentResolver) : - createCopyPathAndCopy(uri, context); - } - - /** - * In older devices getPath() may fail depending on the source URI. Creating and using a copy of the file seems to work instead. - * - * @return path of copy - */ - @Nullable - private static String createExternalCopyPathAndCopy(Uri uri, ContentResolver contentResolver) throws IOException { - ParcelFileDescriptor parcelFileDescriptor = contentResolver.openFileDescriptor(uri, "r"); - if (parcelFileDescriptor == null) { - return null; - } - - FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); - String copyPath = Environment.getExternalStorageDirectory().toString() + "/CommonsApp/" + new Date().getTime() + "." + getFileExt(uri, contentResolver); - File newFile = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp"); - newFile.mkdir(); - FileUtils.copy(fileDescriptor, copyPath); - Timber.d("Filepath (copied): %s", copyPath); - return copyPath; - } - - /** - * In older devices getPath() may fail depending on the source URI. Creating and using a copy of the file seems to work instead. - * - * @return path of copy - */ - @Nullable - private static String createCopyPathAndCopy(Uri uri, Context context) throws IOException { - FileDescriptor fileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r").getFileDescriptor(); - String copyPath = context.getCacheDir().getAbsolutePath() + "/" + new Date().getTime() + "." + getFileExt(uri, context.getContentResolver()); - FileUtils.copy(fileDescriptor, copyPath); - Timber.d("Filepath (copied): %s", copyPath); - return copyPath; - } - - /** - * Get a file path from a Uri. This will get the the path for Storage Access - * Framework Documents, as well as the _data field for the MediaStore and - * other file-based ContentProviders. - * - * @param context The context. - * @param uri The Uri to query. - * @author paulburke - */ - // Can be safely suppressed, checks for isKitKat before running isDocumentUri - @SuppressLint("NewApi") - @Nullable - public static String getPath(Context context, - Uri uri, - boolean useExternalStorage) { - - String returnPath = null; - final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - - // DocumentProvider - if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { - // ExternalStorageProvider - if (isExternalStorageDocument(uri)) { - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - final String type = split[0]; - - if ("primary".equalsIgnoreCase(type)) { - returnPath = Environment.getExternalStorageDirectory() + "/" + split[1]; - } - } else if (isDownloadsDocument(uri)) { // DownloadsProvider - - final String id = DocumentsContract.getDocumentId(uri); - final Uri contentUri = ContentUris.withAppendedId( - Uri.parse("content://downloads/document"), Long.valueOf(id)); - - returnPath = getDataColumn(context, contentUri, null, null); - } else if (isMediaDocument(uri)) { // MediaProvider - - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - final String type = split[0]; - - Uri contentUri = null; - switch (type) { - case "image": - contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; - break; - case "video": - contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; - break; - case "audio": - contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; - break; - default: - break; - } - - final String selection = "_id=?"; - final String[] selectionArgs = new String[]{ - split[1] - }; - - returnPath = getDataColumn(context, contentUri, selection, selectionArgs); - } - } - // MediaStore (and general) - else if ("content".equalsIgnoreCase(uri.getScheme())) { - returnPath = getDataColumn(context, uri, null, null); - } - // File - else if ("file".equalsIgnoreCase(uri.getScheme())) { - returnPath = uri.getPath(); - } - - if (returnPath == null) { - //fetching path may fail depending on the source URI and all hope is lost - //so we will create and use a copy of the file, which seems to work - String copyPath = null; - try { - ParcelFileDescriptor descriptor - = context.getContentResolver().openFileDescriptor(uri, "r"); - if (descriptor != null) { - - if (useExternalStorage) { - copyPath = Environment.getExternalStorageDirectory().toString() - + "/CommonsApp/" + new Date().getTime() + ".jpg"; - File newFile = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp"); - newFile.mkdir(); - FileUtils.copy( - descriptor.getFileDescriptor(), - copyPath); - Timber.d("Filepath (copied): %s", copyPath); - return copyPath; - } - copyPath = context.getCacheDir().getAbsolutePath() - + "/" + new Date().getTime() + ".jpg"; - FileUtils.copy( - descriptor.getFileDescriptor(), - copyPath); - Timber.d("Filepath (copied): %s", copyPath); - return copyPath; - } - } catch (IOException e) { - Timber.w(e, "Error in file " + copyPath); - return null; - } - } else { - return returnPath; - } - - return null; - } - - /** - * Get the value of the data column for this Uri. This is useful for - * MediaStore Uris, and other file-based ContentProviders. - * - * @param context The context. - * @param uri The Uri to query. - * @param selection (Optional) Filter used in the query. - * @param selectionArgs (Optional) Selection arguments used in the query. - * @return The value of the _data column, which is typically a file path. - */ - @Nullable - private static String getDataColumn(Context context, Uri uri, String selection, - String[] selectionArgs) { - - Cursor cursor = null; - final String column = MediaStore.Images.ImageColumns.DATA; - final String[] projection = { - column - }; - - try { - cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); - if (cursor != null && cursor.moveToFirst()) { - final int column_index = cursor.getColumnIndexOrThrow(column); - return cursor.getString(column_index); - } - } catch (IllegalArgumentException e) { - Timber.d(e); - } finally { - if (cursor != null) { - cursor.close(); - } - } - return null; - } - - /** - * @param uri The Uri to check. - * @return Whether the Uri authority is ExternalStorageProvider. - */ - private static boolean isExternalStorageDocument(Uri uri) { - return "com.android.externalstorage.documents".equals(uri.getAuthority()); - } - - /** - * @param uri The Uri to check. - * @return Whether the Uri authority is DownloadsProvider. - */ - private static boolean isDownloadsDocument(Uri uri) { - return "com.android.providers.downloads.documents".equals(uri.getAuthority()); - } - - /** - * @param uri The Uri to check. - * @return Whether the Uri authority is MediaProvider. - */ - private static boolean isMediaDocument(Uri uri) { - return "com.android.providers.media.documents".equals(uri.getAuthority()); - } - - /** - * Check if the URI is owned by the current app. - */ - public static boolean isSelfOwned(Context context, Uri uri) { - return uri.getAuthority().equals(context.getPackageName() + ".provider"); - } - - /** - * Copy content from source file to destination file. - * - * @param source stream copied from - * @param destination stream copied to - * @throws IOException thrown when failing to read source or opening destination file - */ - public static void copy(@NonNull FileInputStream source, @NonNull FileOutputStream destination) - throws IOException { - FileChannel sourceChannel = source.getChannel(); - FileChannel destinationChannel = destination.getChannel(); - sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel); - } - - /** - * Copy content from source file to destination file. - * - * @param source file descriptor copied from - * @param destination file path copied to - * @throws IOException thrown when failing to read source or opening destination file - */ - private static void copy(@NonNull FileDescriptor source, @NonNull String destination) - throws IOException { - copy(new FileInputStream(source), new FileOutputStream(destination)); - } - /** * Read and return the content of a resource file as string. @@ -393,56 +138,6 @@ public class FileUtils { return deletedAll; } - public static File createAndGetAppLogsFile(String logs) { - try { - File commonsAppDirectory = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp"); - if (!commonsAppDirectory.exists()) { - commonsAppDirectory.mkdir(); - } - - File logsFile = new File(commonsAppDirectory, "logs.txt"); - if (logsFile.exists()) { - //old logs file is useless - logsFile.delete(); - } - - logsFile.createNewFile(); - - FileOutputStream outputStream = new FileOutputStream(logsFile); - OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); - outputStreamWriter.append(logs); - outputStreamWriter.close(); - outputStream.flush(); - outputStream.close(); - - return logsFile; - } catch (IOException ioe) { - Timber.e(ioe); - return null; - } - } - - public static String getFilename(Uri uri, ContentResolver contentResolver) { - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) - return ""; - String result = null; - if (uri.getScheme().equals("content")) { - try (Cursor cursor = contentResolver.query(uri, null, null, null, null)) { - if (cursor != null && cursor.moveToFirst()) { - result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); - } - } - } - if (result == null) { - result = uri.getPath(); - int cut = result.lastIndexOf('/'); - if (cut != -1) { - result = result.substring(cut + 1); - } - } - return result; - } - static String getFileExt(String fileName) { //Default file extension String extension = ".jpg"; @@ -454,10 +149,6 @@ public class FileUtils { return extension; } - private static String getFileExt(Uri uri, ContentResolver contentResolver) { - return getFileExt(getFilename(uri, contentResolver)); - } - public static FileInputStream getFileInputStream(String filePath) throws FileNotFoundException { return new FileInputStream(filePath); } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileUtilsWrapper.java b/app/src/main/java/fr/free/nrw/commons/upload/FileUtilsWrapper.java index 496d74d8d..5c20c75c2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileUtilsWrapper.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileUtilsWrapper.java @@ -20,16 +20,6 @@ public class FileUtilsWrapper { } - public String createCopyPathAndCopy(boolean useExtStorage, - Uri uri, - ContentResolver contentResolver, - Context context) throws IOException { - return FileUtils.createCopyPathAndCopy(useExtStorage, - uri, - contentResolver, - context); - } - public String getFileExt(String fileName) { return FileUtils.getFileExt(fileName); } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ImageProcessingService.java b/app/src/main/java/fr/free/nrw/commons/upload/ImageProcessingService.java new file mode 100644 index 000000000..93bbd4d50 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/ImageProcessingService.java @@ -0,0 +1,97 @@ +package fr.free.nrw.commons.upload; + +import javax.inject.Inject; +import javax.inject.Singleton; + +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; +import fr.free.nrw.commons.utils.ImageUtilsWrapper; +import fr.free.nrw.commons.utils.StringUtils; +import io.reactivex.Single; + +/** + * Methods for pre-processing images to be uploaded + */ +@Singleton +public class ImageProcessingService { + private final FileUtilsWrapper fileUtilsWrapper; + private final BitmapRegionDecoderWrapper bitmapRegionDecoderWrapper; + private final ImageUtilsWrapper imageUtilsWrapper; + private final MediaWikiApi mwApi; + + @Inject + public ImageProcessingService(FileUtilsWrapper fileUtilsWrapper, + BitmapRegionDecoderWrapper bitmapRegionDecoderWrapper, + ImageUtilsWrapper imageUtilsWrapper, + MediaWikiApi mwApi) { + this.fileUtilsWrapper = fileUtilsWrapper; + this.bitmapRegionDecoderWrapper = bitmapRegionDecoderWrapper; + + this.imageUtilsWrapper = imageUtilsWrapper; + this.mwApi = mwApi; + } + + /** + * Check image quality before upload + * - checks duplicate image + * - checks dark image + */ + public Single checkImageQuality(String filePath) { + return checkImageQuality(null, filePath); + } + + /** + * Check image quality before upload + * - checks duplicate image + * - checks dark image + * - checks geolocation for image + */ + public Single checkImageQuality(Place place, String filePath) { + return Single.zip( + checkDuplicateImage(filePath), + checkImageGeoLocation(place, filePath), + checkDarkImage(filePath), //Returns IMAGE_DARK or IMAGE_OK + (dupe, wrongGeo, dark) -> dupe | wrongGeo | dark); + } + + /** + * Checks for duplicate image + * @param filePath file to be checked + * @return IMAGE_DUPLICATE or IMAGE_OK + */ + private Single checkDuplicateImage(String filePath) { + return Single.fromCallable(() -> + fileUtilsWrapper.getFileInputStream(filePath)) + .map(fileUtilsWrapper::getSHA1) + .map(mwApi::existingFile) + .map(b -> b ? ImageUtils.IMAGE_DUPLICATE : ImageUtils.IMAGE_OK); + } + + /** + * Checks for dark image + * @param filePath file to be checked + * @return IMAGE_DARK or IMAGE_OK + */ + private Single checkDarkImage(String filePath) { + return Single.fromCallable(() -> + fileUtilsWrapper.getFileInputStream(filePath)) + .map(file -> bitmapRegionDecoderWrapper.newInstance(file, false)) + .flatMap(imageUtilsWrapper::checkIfImageIsTooDark); + } + + /** + * Checks for image geolocation + * @param filePath file to be checked + * @return IMAGE_GEOLOCATION_DIFFERENT or IMAGE_OK + */ + private Single checkImageGeoLocation(Place place, String filePath) { + if (place == null || StringUtils.isNullOrWhiteSpace(place.getWikiDataEntityId())) { + return Single.just(ImageUtils.IMAGE_OK); + } + return Single.fromCallable(() -> filePath) + .map(fileUtilsWrapper::getGeolocationOfFile) + .flatMap(geoLocation -> imageUtilsWrapper.checkImageGeolocationIsDifferent(geoLocation, place.getLocation())); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index b42b8cd69..43fa1a4ea 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -69,10 +69,7 @@ import timber.log.Timber; import static fr.free.nrw.commons.utils.ImageUtils.Result; import static fr.free.nrw.commons.utils.ImageUtils.getErrorMessageForResult; -import static fr.free.nrw.commons.wikidata.WikidataConstants.IS_DIRECT_UPLOAD; import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; -import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ENTITY_ID_PREF; -import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ITEM_LOCATION; public class UploadActivity extends AuthenticatedActivity implements UploadView, SimilarImageInterface { @Inject MediaWikiApi mwApi; @@ -643,12 +640,8 @@ public class UploadActivity extends AuthenticatedActivity implements UploadView, return; } - if (intent.hasExtra(PLACE_OBJECT)) { - Place place = intent.getParcelableExtra(PLACE_OBJECT); - presenter.receiveDirect(urisList.get(0), mimeType, source, place); - } else { - presenter.receive(urisList, mimeType, source); - } + Place place = intent.getParcelableExtra(PLACE_OBJECT); + presenter.receive(urisList, mimeType, source, place); resetDirectPrefs(); } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java index aff7429f5..3e2cf3301 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java @@ -7,7 +7,6 @@ import android.database.Cursor; import android.net.Uri; import android.support.annotation.Nullable; -import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -20,24 +19,17 @@ import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.contributions.Contribution; 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.settings.Prefs; -import fr.free.nrw.commons.utils.BitmapRegionDecoderWrapper; import fr.free.nrw.commons.utils.ImageUtils; -import fr.free.nrw.commons.utils.ImageUtilsWrapper; -import fr.free.nrw.commons.utils.StringUtils; import io.reactivex.Observable; -import io.reactivex.Single; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.BehaviorSubject; import timber.log.Timber; -import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK; - public class UploadModel { private MediaWikiApi mwApi; @@ -67,9 +59,8 @@ public class UploadModel { private SessionManager sessionManager; private Uri currentMediaUri; private FileUtilsWrapper fileUtilsWrapper; - private ImageUtilsWrapper imageUtilsWrapper; - private BitmapRegionDecoderWrapper bitmapRegionDecoderWrapper; private FileProcessor fileProcessor; + private final ImageProcessingService imageProcessingService; @Inject UploadModel(@Named("licenses") List licenses, @@ -79,13 +70,10 @@ public class UploadModel { MediaWikiApi mwApi, SessionManager sessionManager, FileUtilsWrapper fileUtilsWrapper, - ImageUtilsWrapper imageUtilsWrapper, - BitmapRegionDecoderWrapper bitmapRegionDecoderWrapper, - FileProcessor fileProcessor) { + FileProcessor fileProcessor, ImageProcessingService imageProcessingService) { this.licenses = licenses; this.basicKvStore = basicKvStore; this.license = basicKvStore.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3); - this.bitmapRegionDecoderWrapper = bitmapRegionDecoderWrapper; this.licensesByName = licensesByName; this.context = context; this.mwApi = mwApi; @@ -93,90 +81,53 @@ public class UploadModel { this.sessionManager = sessionManager; this.fileUtilsWrapper = fileUtilsWrapper; this.fileProcessor = fileProcessor; - this.imageUtilsWrapper = imageUtilsWrapper; + useExtStorage = this.basicKvStore.getBoolean("useExternalStorage", false); + this.imageProcessingService = imageProcessingService; useExtStorage = this.basicKvStore.getBoolean("useExternalStorage", false); } @SuppressLint("CheckResult") - void receive(List mediaUri, - String mimeType, - String source, - SimilarImageInterface similarImageInterface) { + Observable preProcessImages(List mediaUris, + String mimeType, + Place place, + String source, + SimilarImageInterface similarImageInterface) { initDefaultValues(); - Observable itemObservable = Observable.fromIterable(mediaUri) - .map(media -> { - currentMediaUri = media; - return cacheFileUpload(media); - }) - .map(filePath -> { + return Observable.fromIterable(mediaUris) + .map(mediaUri -> { + if (mediaUri == null || mediaUri.getPath() == null) { + return null; + } + String filePath = mediaUri.getPath(); long fileCreatedDate = getFileCreatedDate(currentMediaUri); - Uri uri = Uri.fromFile(new File(filePath)); + String fileExt = fileUtilsWrapper.getFileExt(filePath); + GPSExtractor gpsExtractor = fileProcessor.processFileCoordinates(similarImageInterface); + fileProcessor.initFileDetails(filePath, context.getContentResolver()); - UploadItem item = new UploadItem(uri, mimeType, source, fileProcessor.processFileCoordinates(similarImageInterface), - fileUtilsWrapper.getFileExt(filePath), null, fileCreatedDate); - checkImageQuality(null, null, filePath) - .observeOn(Schedulers.io()) + UploadItem item = new UploadItem(mediaUri, mimeType, source, gpsExtractor, + fileExt, place.getWikiDataEntityId(), fileCreatedDate); + imageProcessingService.checkImageQuality(place, filePath) + .subscribeOn(Schedulers.computation()) .subscribe(item.imageQuality::onNext, Timber::e); return item; }); - items = itemObservable.toList().blockingGet(); - items.get(0).selected = true; - items.get(0).first = true; } - @SuppressLint("CheckResult") - void receiveDirect(Uri media, String mimeType, String source, Place place, SimilarImageInterface similarImageInterface) { - initDefaultValues(); - long fileCreatedDate = getFileCreatedDate(media); - String filePath = this.cacheFileUpload(media); - Uri uri = Uri.fromFile(new File(filePath)); - fileProcessor.initFileDetails(filePath, context.getContentResolver()); - UploadItem item = new UploadItem(uri, mimeType, source, fileProcessor.processFileCoordinates(similarImageInterface), - fileUtilsWrapper.getFileExt(filePath), place.getWikiDataEntityId(), fileCreatedDate); - item.title.setTitleText(place.getName()); - item.descriptions.get(0).setDescriptionText(place.getLongDescription()); - //TODO figure out if default descriptions in other languages exist - item.descriptions.get(0).setLanguageCode("en"); - checkImageQuality(place.getWikiDataEntityId(), place.getLocation(), filePath) - .observeOn(Schedulers.io()) - .subscribe(item.imageQuality::onNext, Timber::e); - items.add(item); - items.get(0).selected = true; - items.get(0).first = true; - } - - private Single checkImageQuality(String wikiDataEntityId, LatLng latLng, String filePath) { - return Single.zip( - checkDuplicateFile(filePath), - checkImageCoordinates(wikiDataEntityId, latLng, filePath), - checkDarkImage(filePath), //Returns IMAGE_DARK or IMAGE_OK - (dupe, wrongGeo, dark) -> dupe | wrongGeo | dark); - } - - private Single checkDarkImage(String filePath) { - return Single.fromCallable(() -> - fileUtilsWrapper.getFileInputStream(filePath)) - .map(file -> bitmapRegionDecoderWrapper.newInstance(file, false)) - .map(imageUtilsWrapper::checkIfImageIsTooDark); - } - - private Single checkImageCoordinates(String wikiDataEntityId, LatLng latLng, String filePath) { - if (StringUtils.isNullOrWhiteSpace(wikiDataEntityId)) { - return Single.just(IMAGE_OK); + void onItemsProcessed(Place place, List uploadItems) { + items = uploadItems; + if (items.isEmpty()) { + return; + } + UploadItem uploadItem = items.get(0); + uploadItem.selected = true; + uploadItem.first = true; + if (place != null) { + uploadItem.title.setTitleText(place.getName()); + uploadItem.descriptions.get(0).setDescriptionText(place.getLongDescription()); + //TODO figure out if default descriptions in other languages exist + uploadItem.descriptions.get(0).setLanguageCode("en"); } - return Single.fromCallable(() -> filePath) - .map(fileUtilsWrapper::getGeolocationOfFile) - .map(geoLocation -> imageUtilsWrapper.checkImageGeolocationIsDifferent(geoLocation, latLng)) - .map(r -> r ? ImageUtils.IMAGE_GEOLOCATION_DIFFERENT : IMAGE_OK); - } - - private Single checkDuplicateFile(String filePath) { - return Single.fromCallable(() -> - fileUtilsWrapper.getFileInputStream(filePath)) - .map(fileUtilsWrapper::getSHA1) - .map(mwApi::existingFile) - .map(b -> b ? ImageUtils.IMAGE_DUPLICATE : IMAGE_OK); } private void initDefaultValues() { @@ -274,8 +225,10 @@ public class UploadModel { } public void next() { + Timber.d("UploadModel:next; Handling next"); if (badImageSubscription != null) badImageSubscription.dispose(); + Timber.d("UploadModel:next; disposing badImageSubscription"); markCurrentUploadVisited(); if (currentStepIndex < items.size() + 1) { currentStepIndex++; @@ -325,6 +278,7 @@ public class UploadModel { } private void updateItemState() { + Timber.d("Updating item state"); int count = items.size(); for (int i = 0; i < count; i++) { UploadItem item = items.get(i); @@ -334,6 +288,7 @@ public class UploadModel { } private void markCurrentUploadVisited() { + Timber.d("Marking current upload visited"); if (currentStepIndex < items.size() && currentStepIndex >= 0) { items.get(currentStepIndex).visited = true; } @@ -372,29 +327,6 @@ public class UploadModel { }); } - /** - * Copy files into local storage and return file path - * If somehow copy fails, it returns the original path - * @param media Uri of the file - * @return path of the enw file - */ - private String cacheFileUpload(Uri media) { - String finalFilePath; - try { - String copyFilePath = fileUtilsWrapper.createCopyPathAndCopy(useExtStorage, media, contentResolver, context); - Timber.i("Copied file path is %s", copyFilePath); - finalFilePath = copyFilePath; - } catch (Exception e) { - Timber.w(e, "Error in copying URI %s. Using original file path instead", media.getPath()); - finalFilePath = media.getPath(); - } - - if (StringUtils.isNullOrWhiteSpace(finalFilePath)) { - finalFilePath = media.getPath(); - } - return finalFilePath; - } - void keepPicture() { items.get(currentStepIndex).imageQuality.onNext(ImageUtils.IMAGE_KEEP); } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java index 7b03e1628..62e6fe54f 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java @@ -5,7 +5,6 @@ import android.net.Uri; import java.lang.reflect.Proxy; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import javax.inject.Inject; @@ -20,7 +19,6 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.settings.Prefs; import fr.free.nrw.commons.utils.ImageUtils; -import io.reactivex.Completable; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; @@ -64,11 +62,7 @@ public class UploadPresenter { this.mediaWikiApi = mediaWikiApi; } - void receive(Uri mediaUri, String mimeType, String source) { - receive(Collections.singletonList(mediaUri), mimeType, source); - } - - /** + /** * Passes the items received to {@link #uploadModel} and displays the items. * * @param media The Uri's of the media being uploaded. @@ -76,41 +70,30 @@ public class UploadPresenter { * @param source File source from {@link Contribution.FileSource} */ @SuppressLint("CheckResult") - void receive(List media, String mimeType, @Contribution.FileSource String source) { - Completable.fromRunnable(() -> uploadModel.receive(media, mimeType, source, similarImageInterface)) + void receive(List media, + String mimeType, + @Contribution.FileSource String source, + Place place) { + Observable uploadItemObservable = uploadModel + .preProcessImages(media, mimeType, place, source, similarImageInterface); + + uploadItemObservable + .toList() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(() -> { - updateCards(); - updateLicenses(); - updateContent(); - if (uploadModel.isShowingItem()) - uploadModel.subscribeBadPicture(this::handleBadPicture); - }, Timber::e); + .subscribe(uploadItems -> onImagesProcessed(uploadItems, place), + throwable -> Timber.e(throwable, "Error occurred in processing images")); } - /** - * Passes the direct upload item received to {@link #uploadModel} and displays the items. - * - * @param media The Uri's of the media being uploaded. - * @param mimeType the mimeType of the files. - * @param source File source from {@link Contribution.FileSource} - */ - @SuppressLint("CheckResult") - void receiveDirect(Uri media, String mimeType, - @Contribution.FileSource String source, - Place place) { - Completable.fromRunnable(() -> uploadModel.receiveDirect(media, mimeType, source, place, similarImageInterface)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(() -> { - updateCards(); - updateLicenses(); - updateContent(); - if (uploadModel.isShowingItem()) - uploadModel.subscribeBadPicture(this::handleBadPicture); - }, Timber::e); + private void onImagesProcessed(List uploadItems, Place place) { + uploadModel.onItemsProcessed(place, uploadItems); + updateCards(); + updateLicenses(); + updateContent(); + if (uploadModel.isShowingItem()) + uploadModel.subscribeBadPicture(this::handleBadPicture); } + /** * Sets the license to parameter and updates {@link UploadActivity} * @@ -129,10 +112,12 @@ public class UploadPresenter { @SuppressLint("CheckResult") void handleNext(Title title, List descriptions) { + Timber.e("Inside handleNext"); validateCurrentItemTitle() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(errorCode -> handleImage(errorCode, title, descriptions)); + .subscribe(errorCode -> handleImage(errorCode, title, descriptions), + throwable -> Timber.e(throwable, "Error occurred while handling image")); } /** @@ -151,33 +136,41 @@ public class UploadPresenter { private void handleImage(Integer errorCode, Title title, List descriptions) { switch (errorCode) { case EMPTY_TITLE: + Timber.d("Title is empty. Showing toast"); view.showErrorMessage(R.string.add_title_toast); break; case FILE_NAME_EXISTS: if(getCurrentItem().imageQuality.getValue().equals(IMAGE_KEEP)) { + Timber.d("Set title and desc; Show next uploaded item"); setTitleAndDescription(title, descriptions); nextUploadedItem(); } else { + Timber.d("Trying to show duplicate picture popup"); view.showDuplicatePicturePopup(); } break; case IMAGE_OK: + Timber.d("Image is OK. Proceeding"); default: + Timber.d("Default: Setting title and desc; Show next uploaded item"); setTitleAndDescription(title, descriptions); nextUploadedItem(); } } private void nextUploadedItem() { + Timber.d("Trying to show next uploaded item"); uploadModel.next(); updateContent(); if (uploadModel.isShowingItem()) { + Timber.d("Is showing item is true"); uploadModel.subscribeBadPicture(this::handleBadPicture); } view.dismissKeyboard(); } private void setTitleAndDescription(Title title, List descriptions) { + Timber.d("setTitleAndDescription: Setting title and desc"); uploadModel.setCurrentTitleAndDescriptions(title, descriptions); } diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ContributionUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/ContributionUtils.java deleted file mode 100644 index 9e9ac9cc5..000000000 --- a/app/src/main/java/fr/free/nrw/commons/utils/ContributionUtils.java +++ /dev/null @@ -1,155 +0,0 @@ -package fr.free.nrw.commons.utils; - -import android.content.Context; -import android.net.Uri; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Random; - -import timber.log.Timber; - -/** - * This class includes utility methods for uploading process of images. - */ - -public class ContributionUtils { - - private static String TEMP_EXTERNAL_DIRECTORY = - android.os.Environment.getExternalStorageDirectory().getPath()+ - File.separatorChar+"UploadingByCommonsApp"; - - /** - * Saves images temporarily to a fixed folder and use Uri of that file during upload process. - * Otherwise, temporary Uri provided by content provider sometimes points to a null space and - * consequently upload fails. See: issue #1400A and E. - * Not: Saved image will be deleted, our directory will be empty after upload process. - * @return URI of saved image - */ - public static Uri saveFileBeingUploadedTemporarily(Context context, Uri URIfromContentProvider) { - // TODO add exceptions for Google Drive URİ is needed - Uri result = null; - - if (checkIfDirectoryExists(TEMP_EXTERNAL_DIRECTORY)) { - String destinationFilename = decideTempDestinationFileName(); - result = saveFileFromURI(context, URIfromContentProvider, destinationFilename); - } else { // If directory doesn't exist, create it and recursive call current method to check again - - File file = new File(TEMP_EXTERNAL_DIRECTORY); - if (file.mkdirs()) { - Timber.d("saveFileBeingUploadedTemporarily() parameters: URI from Content Provider %s", URIfromContentProvider); - result = saveFileBeingUploadedTemporarily(context, URIfromContentProvider); // If directory is created - } else { //An error occurred to create directory - Timber.e("saveFileBeingUploadedTemporarily() parameters: URI from Content Provider %s", URIfromContentProvider); - } - } - return result; - } - - /** - * Removes temp file created during upload - * @param tempFileUri - */ - public static void removeTemporaryFile(Uri tempFileUri) { - //TODO: do I have to notify file system about deletion? - File tempFile = new File(tempFileUri.getPath()); - if (tempFile.exists()) { - boolean isDeleted = tempFile.delete(); - Timber.e("removeTemporaryFile() parameters: URI tempFileUri %s, deleted status %b", tempFileUri, isDeleted); - } - } - - /** - * Creates a temporary directory and returns pathname - * @return - */ - private static String decideTempDestinationFileName() { - int i = 0; - while (new File(TEMP_EXTERNAL_DIRECTORY + File.separatorChar + i + "_tmp").exists()) { - i++; - } - - // Use time stamp for file name, so that two temporary file never has same file name - // to prevent previous file reference bug - String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); - - // For multiple uploads, time randomisation should be combined with another random - // parameter, since they created at same time - int multipleUploadRandomParameter = new Random().nextInt(100); - return TEMP_EXTERNAL_DIRECTORY + File.separatorChar + timeStamp + multipleUploadRandomParameter + "_tmp"; - } - - /** - * Empties files in Temporary Directory - */ - public static void emptyTemporaryDirectory() { - File dir = new File(TEMP_EXTERNAL_DIRECTORY); - if (dir.isDirectory()) { - String[] children = dir.list(); - - if (children == null) return; - - for (String child : children) { - new File(dir, child).delete(); - } - } - } - - /** - * Saves file from source URI to destination. - * @param sourceUri Uri which points to file to be saved - * @param destinationFilename where file will be located at - * @return Uri points to file saved - */ - private static Uri saveFileFromURI(Context context, Uri sourceUri, String destinationFilename) { - File file = new File(destinationFilename); - if (file.exists()) { - file.delete(); - } - - InputStream in = null; - OutputStream out = null; - try { - in = context.getContentResolver().openInputStream(sourceUri); - out = new FileOutputStream(new File(destinationFilename)); - - byte[] buf = new byte[1024]; - int length; - while ((length = in.read(buf)) > 0) { - out.write(buf, 0, length); - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (out != null) out.close(); - } catch (IOException e) { - e.printStackTrace(); - } - - try { - if (in != null) in.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - return Uri.parse("file://" + destinationFilename); - } - - /** - * Checks if directory exists - * @param pathToCheck path of directory to check - * @return true if directory exists, false otherwise - */ - private static boolean checkIfDirectoryExists(String pathToCheck) { - File dir = new File(pathToCheck); - return dir.exists() && dir.isDirectory(); - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java index e9b42de78..620f52dcb 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java @@ -128,44 +128,42 @@ public class ImageUtils { int bitmapHeight = bitmap.getHeight(); int allPixelsCount = bitmapWidth * bitmapHeight; - int[] bitmapPixels = new int[allPixelsCount]; Timber.d("total %s", Integer.toString(allPixelsCount)); - - bitmap.getPixels(bitmapPixels,0,bitmapWidth,0,0,bitmapWidth,bitmapHeight); int numberOfBrightPixels = 0; int numberOfMediumBrightnessPixels = 0; - double brightPixelThreshold = 0.025*allPixelsCount; - double mediumBrightPixelThreshold = 0.3*allPixelsCount; + double brightPixelThreshold = 0.025 * allPixelsCount; + double mediumBrightPixelThreshold = 0.3 * allPixelsCount; - for (int pixel : bitmapPixels) { - int r = Color.red(pixel); - int g = Color.green(pixel); - int b = Color.blue(pixel); + for (int x = 0; x < bitmapWidth; x++) { + for (int y = 0; y < bitmapHeight; y++) { + int pixel = bitmap.getPixel(x, y); + int r = Color.red(pixel); + int g = Color.green(pixel); + int b = Color.blue(pixel); - int secondMax = r>g ? r:g; - double max = (secondMax>b ? secondMax:b)/255.0; + int secondMax = r > g ? r : g; + double max = (secondMax > b ? secondMax : b) / 255.0; - int secondMin = rmediumBrightnessLuminance){ - numberOfMediumBrightnessPixels++; + if (luminance < highBrightnessLuminance) { + if (luminance > mediumBrightnessLuminance) { + numberOfMediumBrightnessPixels++; + } + } else { + numberOfBrightPixels++; + } + + if (numberOfBrightPixels >= brightPixelThreshold || numberOfMediumBrightnessPixels >= mediumBrightPixelThreshold) { + return false; } } - else { - numberOfBrightPixels++; - } - - if (numberOfBrightPixels>=brightPixelThreshold || numberOfMediumBrightnessPixels>=mediumBrightPixelThreshold){ - return false; - } - } return true; } diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtilsWrapper.java b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtilsWrapper.java index df8a9cbb1..963c38183 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtilsWrapper.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtilsWrapper.java @@ -5,6 +5,8 @@ import android.graphics.BitmapRegionDecoder; import javax.inject.Inject; import javax.inject.Singleton; +import io.reactivex.Single; +import io.reactivex.schedulers.Schedulers; import fr.free.nrw.commons.location.LatLng; import static fr.free.nrw.commons.utils.ImageUtils.*; @@ -17,11 +19,18 @@ public class ImageUtilsWrapper { } - public @Result int checkIfImageIsTooDark(BitmapRegionDecoder bitmapRegionDecoder) { - return ImageUtils.checkIfImageIsTooDark(bitmapRegionDecoder); + public Single checkIfImageIsTooDark(BitmapRegionDecoder bitmapRegionDecoder) { + int isImageDark = ImageUtils.checkIfImageIsTooDark(bitmapRegionDecoder); + return Single.just(isImageDark) + .subscribeOn(Schedulers.computation()) + .observeOn(Schedulers.computation()); } - public boolean checkImageGeolocationIsDifferent(String geolocationOfFileString, LatLng latLng) { - return ImageUtils.checkImageGeolocationIsDifferent(geolocationOfFileString, latLng); + public Single checkImageGeolocationIsDifferent(String geolocationOfFileString, LatLng latLng) { + boolean isImageGeoLocationDifferent = ImageUtils.checkImageGeolocationIsDifferent(geolocationOfFileString, latLng); + return Single.just(isImageGeoLocationDifferent) + .subscribeOn(Schedulers.computation()) + .observeOn(Schedulers.computation()) + .map(isDifferent -> isDifferent ? ImageUtils.IMAGE_GEOLOCATION_DIFFERENT : ImageUtils.IMAGE_OK); } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelTest.kt index 4baabce42..52e3290fd 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelTest.kt @@ -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 = mutableListOf(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 = mutableListOf(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 = mutableListOf(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 = mutableListOf(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 = mutableListOf(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 = mutableListOf(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 = mutableListOf(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 = mutableListOf(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 = mutableListOf(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 = mutableListOf(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 = mutableListOf(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 = mutableListOf(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 diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt index df63ca8d2..f10e19664 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt @@ -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 = mutableListOf(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)) } } \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/FileUtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/FileUtilsTest.kt index f4d4ad51f..f9a4fd363 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/FileUtilsTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/FileUtilsTest.kt @@ -7,17 +7,6 @@ import org.junit.Test import java.io.* class FileUtilsTest { - @Test - fun copiedFileIsIdenticalToSource() { - val source = File.createTempFile("temp", "") - val dest = File.createTempFile("temp", "") - writeToFile(source, "Hello, World") - - FileUtils.copy(FileInputStream(source), FileOutputStream(dest)) - - assertEquals(getString(source), getString(dest)) - } - @Test fun deleteFile() { val file = File.createTempFile("testfile", "")