mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-29 13:53:54 +01:00
[GSoC 2022] Improve custom picker (all features) (#5035)
* Project Initiated by creating helper classes for database operations * Database created * Rest of the work and documentation * Requested changes done * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * [GSoC] Insert and Remove Images from not for upload (#4999) * Inserted and marked images as not for upload * Documentation added * Test delete * Implemented remove from not for upload * Test fixed * Requested changes done * Added tests for new lines in existing classes * [GSoC] Added Bubble Scroll (#5023) * Library added * Bubble scroll implemented * Left and right swipe * Requested changes * [GSoC] Hide/Unhide actioned pictures and change numbering (#5012) * Changed numbering of marked images * Hide Unhide implemented * Test fixed * Improved speed for database operation * Improved speed for database operation * Changed progress dialog * Improved hiding speed * Test fixed * Fixed bug * Fixed bug and improved performance * Fixed bug and improved performance * Test fixed * Bug fixed * Bug fixed * Bug fixed * Bug fixed * Bug fixed * Code clean up * Test hiding images * Test hiding images * Test hiding images * Code clean up and test fixed * Fixed layout * Fixed bug * Bug fixed * Renamed method * Documentation added explaining logic * Documentation added explaining logic * [GSoC] Full Screen Mode (#5032) * Gesture detection implemented * Left and right swipe * Selection implemented * onDown implemented * onDown implemented * FS mode implemented * OnSwipe doc * Scope cancel * Added label in Manifest * Merged two features * Requested changes * Image uploaded bug fixed * Increased DB version * Made requested changes * Made requested changes * Made requested changes * Made requested changes * Solved image flashing bug * Solved image flashing bug * Requested changes * Requested changes * Changed name of a function * Fixed transaction failure on large number of images * Tested with isIdentity * Tested with isIdentity * Increased the threshold * Added info dialog * Minor changes * ImageAdapter Test * CustomSelectorActivity Test * Requested changes * Test for ZoomableActivity * Test for ZoomableActivity * Test for ImageLoader * Test for OnSwipeTouchListener * Test for rest * Reverted some test changes * Added more tests for ImageAdapter * Added more tests for ImageAdapter and swipe gesture Co-authored-by: translatewiki.net <l10n-bot@translatewiki.net>
This commit is contained in:
parent
b5ffe7120c
commit
33679eb6b4
41 changed files with 2560 additions and 284 deletions
|
|
@ -1,92 +0,0 @@
|
|||
package fr.free.nrw.commons.media;
|
||||
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.controller.BaseControllerListener;
|
||||
import com.facebook.drawee.controller.ControllerListener;
|
||||
import com.facebook.drawee.drawable.ProgressBarDrawable;
|
||||
import com.facebook.drawee.drawable.ScalingUtils;
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchy;
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||
import com.facebook.drawee.interfaces.DraweeController;
|
||||
import com.facebook.imagepipeline.image.ImageInfo;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.media.zoomControllers.zoomable.DoubleTapGestureListener;
|
||||
import fr.free.nrw.commons.media.zoomControllers.zoomable.ZoomableDraweeView;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
public class ZoomableActivity extends AppCompatActivity {
|
||||
private Uri imageUri;
|
||||
|
||||
@BindView(R.id.zoomable)
|
||||
ZoomableDraweeView photo;
|
||||
@BindView(R.id.zoom_progress_bar)
|
||||
ProgressBar spinner;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
imageUri = getIntent().getData();
|
||||
if (null == imageUri) {
|
||||
throw new IllegalArgumentException("No data to display");
|
||||
}
|
||||
Timber.d("URl = " + imageUri);
|
||||
|
||||
setContentView(R.layout.activity_zoomable);
|
||||
ButterKnife.bind(this);
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Two types of loading indicators have been added to the zoom activity:
|
||||
* 1. An Indeterminate spinner for showing the time lapsed between dispatch of the image request
|
||||
* and starting to receiving the image.
|
||||
* 2. ProgressBarDrawable that reflects how much image has been downloaded
|
||||
*/
|
||||
private final ControllerListener loadingListener = new BaseControllerListener<ImageInfo>() {
|
||||
@Override
|
||||
public void onSubmit(String id, Object callerContext) {
|
||||
// Sometimes the spinner doesn't appear when rapidly switching between images, this fixes that
|
||||
spinner.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {
|
||||
spinner.setVisibility(View.GONE);
|
||||
}
|
||||
@Override
|
||||
public void onFinalImageSet(String id, @Nullable ImageInfo imageInfo, @Nullable Animatable animatable) {
|
||||
spinner.setVisibility(View.GONE);
|
||||
}
|
||||
};
|
||||
private void init() {
|
||||
if( imageUri != null ) {
|
||||
GenericDraweeHierarchy hierarchy = GenericDraweeHierarchyBuilder.newInstance(getResources())
|
||||
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
||||
.setProgressBarImage(new ProgressBarDrawable())
|
||||
.setProgressBarImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
||||
.build();
|
||||
photo.setHierarchy(hierarchy);
|
||||
photo.setAllowTouchInterceptionWhileZoomed(true);
|
||||
photo.setIsLongpressEnabled(false);
|
||||
photo.setTapListener(new DoubleTapGestureListener(photo));
|
||||
DraweeController controller = Fresco.newDraweeControllerBuilder()
|
||||
.setUri(imageUri)
|
||||
.setControllerListener(loadingListener)
|
||||
.build();
|
||||
photo.setController(controller);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
653
app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.kt
Normal file
653
app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.kt
Normal file
|
|
@ -0,0 +1,653 @@
|
|||
package fr.free.nrw.commons.media
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.Button
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.drawee.controller.BaseControllerListener
|
||||
import com.facebook.drawee.controller.ControllerListener
|
||||
import com.facebook.drawee.drawable.ProgressBarDrawable
|
||||
import com.facebook.drawee.drawable.ScalingUtils
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder
|
||||
import com.facebook.drawee.interfaces.DraweeController
|
||||
import com.facebook.imagepipeline.image.ImageInfo
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.customselector.database.NotForUploadStatus
|
||||
import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao
|
||||
import fr.free.nrw.commons.customselector.database.UploadedStatusDao
|
||||
import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants
|
||||
import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants.SHOULD_REFRESH
|
||||
import fr.free.nrw.commons.customselector.helper.ImageHelper
|
||||
import fr.free.nrw.commons.customselector.helper.OnSwipeTouchListener
|
||||
import fr.free.nrw.commons.customselector.model.CallbackStatus
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
import fr.free.nrw.commons.customselector.model.Result
|
||||
import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorViewModel
|
||||
import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorViewModelFactory
|
||||
import fr.free.nrw.commons.media.zoomControllers.zoomable.DoubleTapGestureListener
|
||||
import fr.free.nrw.commons.media.zoomControllers.zoomable.ZoomableDraweeView
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.upload.FileProcessor
|
||||
import fr.free.nrw.commons.upload.FileUtilsWrapper
|
||||
import fr.free.nrw.commons.utils.CustomSelectorUtils
|
||||
import kotlinx.coroutines.*
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
/**
|
||||
* Activity for helping to view an image in full-screen mode with some other features
|
||||
* like zoom, and swap gestures
|
||||
*/
|
||||
class ZoomableActivity : BaseActivity() {
|
||||
|
||||
private lateinit var imageUri: Uri
|
||||
|
||||
/**
|
||||
* View model.
|
||||
*/
|
||||
private lateinit var viewModel: CustomSelectorViewModel
|
||||
|
||||
/**
|
||||
* Pref for saving states.
|
||||
*/
|
||||
private lateinit var prefs: SharedPreferences
|
||||
|
||||
@JvmField
|
||||
@BindView(R.id.zoomable)
|
||||
var photo: ZoomableDraweeView? = null
|
||||
|
||||
@JvmField
|
||||
@BindView(R.id.zoom_progress_bar)
|
||||
var spinner: ProgressBar? = null
|
||||
|
||||
@JvmField
|
||||
@BindView(R.id.selection_count)
|
||||
var selectedCount: TextView? = null
|
||||
|
||||
/**
|
||||
* Total images present in folder
|
||||
*/
|
||||
private var images: ArrayList<Image>? = null
|
||||
|
||||
/**
|
||||
* Total selected images present in folder
|
||||
*/
|
||||
private var selectedImages: ArrayList<Image>? = null
|
||||
|
||||
/**
|
||||
* Present position of the image
|
||||
*/
|
||||
private var position = 0
|
||||
|
||||
/**
|
||||
* Present bucket ID
|
||||
*/
|
||||
private var bucketId: Long = 0L
|
||||
|
||||
/**
|
||||
* Determines whether the adapter should refresh
|
||||
*/
|
||||
private var shouldRefresh = false
|
||||
|
||||
/**
|
||||
* FileUtilsWrapper class to get imageSHA1 from uri
|
||||
*/
|
||||
@Inject
|
||||
lateinit var fileUtilsWrapper: FileUtilsWrapper
|
||||
|
||||
/**
|
||||
* FileProcessor to pre-process the file.
|
||||
*/
|
||||
@Inject
|
||||
lateinit var fileProcessor: FileProcessor
|
||||
|
||||
/**
|
||||
* NotForUploadStatus Dao class for database operations
|
||||
*/
|
||||
@Inject
|
||||
lateinit var notForUploadStatusDao: NotForUploadStatusDao
|
||||
|
||||
/**
|
||||
* UploadedStatus Dao class for database operations
|
||||
*/
|
||||
@Inject
|
||||
lateinit var uploadedStatusDao: UploadedStatusDao
|
||||
|
||||
/**
|
||||
* View Model Factory.
|
||||
*/
|
||||
@Inject
|
||||
lateinit var customSelectorViewModelFactory: CustomSelectorViewModelFactory
|
||||
|
||||
/**
|
||||
* Coroutine Dispatchers and Scope.
|
||||
*/
|
||||
private var defaultDispatcher : CoroutineDispatcher = Dispatchers.Default
|
||||
private var ioDispatcher : CoroutineDispatcher = Dispatchers.IO
|
||||
private val scope : CoroutineScope = MainScope()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_zoomable)
|
||||
ButterKnife.bind(this)
|
||||
|
||||
prefs = applicationContext.getSharedPreferences(
|
||||
ImageHelper.CUSTOM_SELECTOR_PREFERENCE_KEY,
|
||||
MODE_PRIVATE
|
||||
)
|
||||
|
||||
selectedImages = intent.getParcelableArrayListExtra(
|
||||
CustomSelectorConstants.TOTAL_SELECTED_IMAGES
|
||||
)
|
||||
position = intent.getIntExtra(CustomSelectorConstants.PRESENT_POSITION, 0)
|
||||
bucketId = intent.getLongExtra(CustomSelectorConstants.BUCKET_ID, 0L)
|
||||
viewModel = ViewModelProvider(this, customSelectorViewModelFactory).get(
|
||||
CustomSelectorViewModel::class.java
|
||||
)
|
||||
viewModel.fetchImages()
|
||||
viewModel.result.observe(this) {
|
||||
handleResult(it)
|
||||
}
|
||||
|
||||
if(prefs.getBoolean(CustomSelectorConstants.FULL_SCREEN_MODE_FIRST_LUNCH, true)) {
|
||||
// show welcome dialog on first launch
|
||||
showWelcomeDialog()
|
||||
prefs.edit().putBoolean(
|
||||
CustomSelectorConstants.FULL_SCREEN_MODE_FIRST_LUNCH,
|
||||
false
|
||||
).apply()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Full Screen Mode Welcome Dialog.
|
||||
*/
|
||||
private fun showWelcomeDialog() {
|
||||
val dialog = Dialog(this)
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
dialog.setContentView(R.layout.full_screen_mode_info_dialog)
|
||||
(dialog.findViewById(R.id.btn_ok) as Button).setOnClickListener { dialog.dismiss() }
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle view model result.
|
||||
*/
|
||||
private fun handleResult(result: Result){
|
||||
if(result.status is CallbackStatus.SUCCESS){
|
||||
val images = result.images
|
||||
if(images.isNotEmpty()) {
|
||||
this@ZoomableActivity.images = ImageHelper.filterImages(images, bucketId)
|
||||
imageUri = if (this@ZoomableActivity.images.isNullOrEmpty()) {
|
||||
intent.data as Uri
|
||||
} else {
|
||||
this@ZoomableActivity.images!![position].uri
|
||||
}
|
||||
Timber.d("URL = $imageUri")
|
||||
init(imageUri)
|
||||
onSwipe()
|
||||
}
|
||||
}
|
||||
spinner?.let {
|
||||
it.visibility = if (result.status is CallbackStatus.FETCHING) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle swap gestures. Ex. onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown
|
||||
*/
|
||||
private fun onSwipe() {
|
||||
val sharedPreferences: SharedPreferences =
|
||||
getSharedPreferences(ImageHelper.CUSTOM_SELECTOR_PREFERENCE_KEY, 0)
|
||||
val showAlreadyActionedImages =
|
||||
sharedPreferences.getBoolean(ImageHelper.SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY, true)
|
||||
|
||||
if (!images.isNullOrEmpty()) {
|
||||
photo!!.setOnTouchListener(object : OnSwipeTouchListener(this) {
|
||||
// Swipe left to view next image in the folder. (if available)
|
||||
override fun onSwipeLeft() {
|
||||
super.onSwipeLeft()
|
||||
onLeftSwiped(showAlreadyActionedImages)
|
||||
}
|
||||
|
||||
// Swipe right to view previous image in the folder. (if available)
|
||||
override fun onSwipeRight() {
|
||||
super.onSwipeRight()
|
||||
onRightSwiped(showAlreadyActionedImages)
|
||||
}
|
||||
|
||||
// Swipe up to select the picture (the equivalent of tapping it in non-fullscreen mode)
|
||||
// and show the next picture skipping pictures that have either already been uploaded or
|
||||
// marked as not for upload
|
||||
override fun onSwipeUp() {
|
||||
super.onSwipeUp()
|
||||
onUpSwiped()
|
||||
}
|
||||
|
||||
// Swipe down to mark that picture as "Not for upload" (the equivalent of selecting it then
|
||||
// tapping "Mark as not for upload" in non-fullscreen mode), and show the next picture.
|
||||
override fun onSwipeDown() {
|
||||
super.onSwipeDown()
|
||||
onDownSwiped()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles down swipe action
|
||||
*/
|
||||
private fun onDownSwiped() {
|
||||
if (photo?.zoomableController?.isIdentity == false)
|
||||
return
|
||||
|
||||
scope.launch {
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
images!![position].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
var isUploaded = uploadedStatusDao.findByImageSHA1(imageSHA1, true)
|
||||
if (isUploaded > 0) {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.this_image_is_already_uploaded),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![position],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper
|
||||
)
|
||||
isUploaded = uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true
|
||||
)
|
||||
if (isUploaded > 0) {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.this_image_is_already_uploaded),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
insertInNotForUpload(images!![position])
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.image_marked_as_not_for_upload),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
shouldRefresh = true
|
||||
if (position < images!!.size - 1) {
|
||||
position++
|
||||
init(images!![position].uri)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles up swipe action
|
||||
*/
|
||||
private fun onUpSwiped() {
|
||||
if (photo?.zoomableController?.isIdentity == false)
|
||||
return
|
||||
|
||||
scope.launch {
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
images!![position].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
var isNonActionable = notForUploadStatusDao.find(imageSHA1)
|
||||
if (isNonActionable > 0) {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.can_not_select_this_image_for_upload),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
isNonActionable =
|
||||
uploadedStatusDao.findByImageSHA1(imageSHA1, true)
|
||||
if (isNonActionable > 0) {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.this_image_is_already_uploaded),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![position],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper
|
||||
)
|
||||
isNonActionable = uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true
|
||||
)
|
||||
if (isNonActionable > 0) {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.this_image_is_already_uploaded),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
if (!selectedImages!!.contains(images!![position])) {
|
||||
selectedImages!!.add(images!![position])
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.image_selected),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
position = getNextActionableImage(position + 1)
|
||||
init(images!![position].uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles right swipe action
|
||||
*/
|
||||
private fun onRightSwiped(showAlreadyActionedImages: Boolean) {
|
||||
if (photo?.zoomableController?.isIdentity == false)
|
||||
return
|
||||
|
||||
if (showAlreadyActionedImages) {
|
||||
if (position > 0) {
|
||||
position--
|
||||
init(images!![position].uri)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
if (position > 0) {
|
||||
scope.launch {
|
||||
position = getPreviousActionableImage(position - 1)
|
||||
init(images!![position].uri)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles left swipe action
|
||||
*/
|
||||
private fun onLeftSwiped(showAlreadyActionedImages: Boolean) {
|
||||
if (photo?.zoomableController?.isIdentity == false)
|
||||
return
|
||||
|
||||
if (showAlreadyActionedImages) {
|
||||
if (position < images!!.size - 1) {
|
||||
position++
|
||||
init(images!![position].uri)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
if (position < images!!.size - 1) {
|
||||
scope.launch {
|
||||
position = getNextActionableImage(position + 1)
|
||||
init(images!![position].uri)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
getString(R.string.no_more_images_found),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets next actionable image.
|
||||
* Iterates from an index to the end of the folder and check whether the current image is
|
||||
* present in already uploaded table or in not for upload table,
|
||||
* and returns the first actionable image it can find.
|
||||
*/
|
||||
private suspend fun getNextActionableImage(index: Int): Int {
|
||||
var nextPosition = position
|
||||
for(i in index until images!!.size){
|
||||
nextPosition = i
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
images!![i].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
var isNonActionable = notForUploadStatusDao.find(imageSHA1)
|
||||
if (isNonActionable <= 0) {
|
||||
isNonActionable = uploadedStatusDao.findByImageSHA1(imageSHA1, true)
|
||||
if (isNonActionable <= 0) {
|
||||
val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![i],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper
|
||||
)
|
||||
isNonActionable = uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true
|
||||
)
|
||||
if (isNonActionable <= 0) {
|
||||
return i
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nextPosition
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets previous actionable image.
|
||||
* Iterates from an index to the first image of the folder and check whether the current image
|
||||
* is present in already uploaded table or in not for upload table,
|
||||
* and returns the first actionable image it can find
|
||||
*/
|
||||
private suspend fun getPreviousActionableImage(index: Int): Int {
|
||||
var previousPosition = position
|
||||
for(i in index downTo 0){
|
||||
previousPosition = i
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
images!![i].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
var isNonActionable = notForUploadStatusDao.find(imageSHA1)
|
||||
if (isNonActionable <= 0) {
|
||||
isNonActionable = uploadedStatusDao.findByImageSHA1(imageSHA1, true)
|
||||
if (isNonActionable <= 0) {
|
||||
val imageModifiedSHA1 = CustomSelectorUtils.generateModifiedSHA1(
|
||||
images!![i],
|
||||
defaultDispatcher,
|
||||
this@ZoomableActivity,
|
||||
fileProcessor,
|
||||
fileUtilsWrapper
|
||||
)
|
||||
isNonActionable = uploadedStatusDao.findByModifiedImageSHA1(
|
||||
imageModifiedSHA1,
|
||||
true
|
||||
)
|
||||
if (isNonActionable <= 0) {
|
||||
return i
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return previousPosition
|
||||
}
|
||||
|
||||
/**
|
||||
* Unselect item UI
|
||||
*/
|
||||
private fun itemUnselected() {
|
||||
selectedCount!!.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
/**
|
||||
* Select item UI
|
||||
*/
|
||||
private fun itemSelected(i: Int) {
|
||||
selectedCount!!.visibility = View.VISIBLE
|
||||
selectedCount!!.text = i.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get position of an image from list
|
||||
*/
|
||||
private fun getImagePosition(list: ArrayList<Image>?, image: Image): Int {
|
||||
return list!!.indexOf(image)
|
||||
}
|
||||
|
||||
/**
|
||||
* Two types of loading indicators have been added to the zoom activity:
|
||||
* 1. An Indeterminate spinner for showing the time lapsed between dispatch of the image request
|
||||
* and starting to receiving the image.
|
||||
* 2. ProgressBarDrawable that reflects how much image has been downloaded
|
||||
*/
|
||||
private val loadingListener: ControllerListener<ImageInfo?> =
|
||||
object : BaseControllerListener<ImageInfo?>() {
|
||||
override fun onSubmit(id: String, callerContext: Any) {
|
||||
// Sometimes the spinner doesn't appear when rapidly switching between images, this fixes that
|
||||
spinner!!.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onIntermediateImageSet(id: String, imageInfo: ImageInfo?) {
|
||||
spinner!!.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onFinalImageSet(
|
||||
id: String,
|
||||
imageInfo: ImageInfo?,
|
||||
animatable: Animatable?
|
||||
) {
|
||||
spinner!!.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun init(imageUri: Uri?) {
|
||||
if (imageUri != null) {
|
||||
val hierarchy = GenericDraweeHierarchyBuilder.newInstance(resources)
|
||||
.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
||||
.setProgressBarImage(ProgressBarDrawable())
|
||||
.setProgressBarImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
|
||||
.build()
|
||||
photo!!.hierarchy = hierarchy
|
||||
photo!!.setAllowTouchInterceptionWhileZoomed(true)
|
||||
photo!!.setIsLongpressEnabled(false)
|
||||
photo!!.setTapListener(DoubleTapGestureListener(photo))
|
||||
val controller: DraweeController = Fresco.newDraweeControllerBuilder()
|
||||
.setUri(imageUri)
|
||||
.setControllerListener(loadingListener)
|
||||
.build()
|
||||
photo!!.controller = controller
|
||||
|
||||
if (!images.isNullOrEmpty()) {
|
||||
val selectedIndex = getImagePosition(selectedImages, images!![position])
|
||||
val isSelected = selectedIndex != -1
|
||||
if (isSelected) {
|
||||
itemSelected(selectedImages!!.size)
|
||||
} else {
|
||||
itemUnselected()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an image in Not For Upload table
|
||||
*/
|
||||
private suspend fun insertInNotForUpload(it: Image) {
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
it.uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
notForUploadStatusDao.insert(
|
||||
NotForUploadStatus(
|
||||
imageSHA1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Send selected images in fragment
|
||||
*/
|
||||
override fun onBackPressed() {
|
||||
if (!images.isNullOrEmpty()) {
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putParcelableArrayListExtra(
|
||||
CustomSelectorConstants.NEW_SELECTED_IMAGES,
|
||||
selectedImages
|
||||
)
|
||||
returnIntent.putExtra(SHOULD_REFRESH, shouldRefresh)
|
||||
setResult(Activity.RESULT_OK, returnIntent)
|
||||
finish()
|
||||
}
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
scope.cancel()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue