mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 04:13:53 +01:00
[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
This commit is contained in:
parent
a6c51a75a8
commit
52912087d6
14 changed files with 579 additions and 115 deletions
|
|
@ -46,7 +46,10 @@
|
|||
android:finishOnTaskLaunch="true" />
|
||||
|
||||
<activity
|
||||
android:name=".media.ZoomableActivity" />
|
||||
android:name=".media.ZoomableActivity"
|
||||
android:label="Zoomable Activity"
|
||||
android:configChanges="screenSize|keyboard|orientation"
|
||||
android:parentActivityName=".customselector.ui.selector.CustomSelectorActivity" />
|
||||
|
||||
<activity android:name=".auth.LoginActivity">
|
||||
<intent-filter>
|
||||
|
|
|
|||
|
|
@ -66,4 +66,18 @@ abstract class UploadedStatusDao {
|
|||
suspend fun getUploadedFromImageSHA1(imageSHA1: String):UploadedStatus? {
|
||||
return getFromImageSHA1(imageSHA1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the imageSHA1 is present in database
|
||||
*/
|
||||
@Query("SELECT COUNT() FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) AND imageResult = (:imageResult) ")
|
||||
abstract suspend fun findByImageSHA1(imageSHA1 : String, imageResult: Boolean): Int
|
||||
|
||||
/**
|
||||
* Check whether the modifiedImageSHA1 is present in database
|
||||
*/
|
||||
@Query("SELECT COUNT() FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) AND modifiedImageResult = (:modifiedImageResult) ")
|
||||
abstract suspend fun findByModifiedImageSHA1(modifiedImageSHA1 : String,
|
||||
modifiedImageResult: Boolean): Int
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package fr.free.nrw.commons.customselector.helper
|
||||
|
||||
object CustomSelectorConstants {
|
||||
|
||||
const val TOTAL_IMAGES = "total_images"
|
||||
const val TOTAL_SELECTED_IMAGES = "total_selected_images"
|
||||
const val PRESENT_POSITION = "present_position"
|
||||
const val NEW_SELECTED_IMAGES = "new_selected_images"
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package fr.free.nrw.commons.customselector.helper
|
||||
|
||||
import android.content.Context
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* Class for detecting swipe gestures
|
||||
*/
|
||||
open class OnSwipeTouchListener(context: Context?) : View.OnTouchListener {
|
||||
|
||||
private val gestureDetector: GestureDetector
|
||||
|
||||
override fun onTouch(view: View?, motionEvent: MotionEvent?): Boolean {
|
||||
return gestureDetector.onTouchEvent(motionEvent)
|
||||
}
|
||||
|
||||
private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
|
||||
|
||||
private val SWIPE_THRESHOLD = 100
|
||||
private val SWIPE_VELOCITY_THRESHOLD = 100
|
||||
|
||||
override fun onDown(e: MotionEvent?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onFling(
|
||||
event1: MotionEvent,
|
||||
event2: MotionEvent,
|
||||
velocityX: Float,
|
||||
velocityY: Float
|
||||
): Boolean {
|
||||
try {
|
||||
val diffY: Float = event2.y - event1.y
|
||||
val diffX: Float = event2.x - event1.x
|
||||
if (abs(diffX) > abs(diffY)) {
|
||||
if (abs(diffX) > SWIPE_THRESHOLD && abs(velocityX) >
|
||||
SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (diffX > 0) {
|
||||
onSwipeRight()
|
||||
} else {
|
||||
onSwipeLeft()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (abs(diffY) > SWIPE_THRESHOLD && abs(velocityY) >
|
||||
SWIPE_VELOCITY_THRESHOLD) {
|
||||
if (diffY > 0) {
|
||||
onSwipeDown()
|
||||
} else {
|
||||
onSwipeUp()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
exception.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
open fun onSwipeRight() {}
|
||||
open fun onSwipeLeft() {}
|
||||
open fun onSwipeUp() {}
|
||||
open fun onSwipeDown() {}
|
||||
|
||||
init {
|
||||
gestureDetector = GestureDetector(context, GestureListener())
|
||||
}
|
||||
}
|
||||
|
|
@ -19,5 +19,9 @@ interface ImageSelectListener {
|
|||
* onLongPress
|
||||
* @param imageUri : uri of image
|
||||
*/
|
||||
fun onLongPress(imageUri: Uri)
|
||||
fun onLongPress(
|
||||
position: Int,
|
||||
images: ArrayList<Image>,
|
||||
selectedImages: ArrayList<Image>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package fr.free.nrw.commons.customselector.listeners
|
||||
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
|
||||
interface PassDataListener {
|
||||
fun passSelectedImages(selectedImages: ArrayList<Image>)
|
||||
}
|
||||
|
|
@ -228,7 +228,7 @@ class ImageAdapter(
|
|||
|
||||
// launch media preview on long click.
|
||||
holder.itemView.setOnLongClickListener {
|
||||
imageSelectListener.onLongPress(image.uri)
|
||||
imageSelectListener.onLongPress(position, images, selectedImages)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
@ -317,6 +317,13 @@ class ImageAdapter(
|
|||
diffResult.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new selected images
|
||||
*/
|
||||
fun setSelectedImages(newSelectedImages: ArrayList<Image>){
|
||||
selectedImages = ArrayList(newSelectedImages)
|
||||
imageSelectListener.onSelectedImagesChanged(selectedImages, 0)
|
||||
}
|
||||
/**
|
||||
* Refresh the data in the adapter
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import android.app.Activity
|
|||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
|
|
@ -16,6 +15,7 @@ import androidx.lifecycle.ViewModelProvider
|
|||
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.helper.CustomSelectorConstants
|
||||
import fr.free.nrw.commons.customselector.listeners.FolderClickListener
|
||||
import fr.free.nrw.commons.customselector.listeners.ImageSelectListener
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
|
|
@ -112,6 +112,18 @@ class CustomSelectorActivity: BaseActivity(), FolderClickListener, ImageSelectLi
|
|||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == 101) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
val selectedImages: ArrayList<Image> =
|
||||
data!!
|
||||
.getParcelableArrayListExtra(CustomSelectorConstants.NEW_SELECTED_IMAGES)!!
|
||||
imageFragment!!.passSelectedImages(selectedImages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Custom Selector Welcome Dialog.
|
||||
*/
|
||||
|
|
@ -305,9 +317,19 @@ class CustomSelectorActivity: BaseActivity(), FolderClickListener, ImageSelectLi
|
|||
* onLongPress
|
||||
* @param imageUri : uri of image
|
||||
*/
|
||||
override fun onLongPress(imageUri: Uri) {
|
||||
val intent = Intent(this, ZoomableActivity::class.java).setData(imageUri);
|
||||
startActivity(intent)
|
||||
override fun onLongPress(
|
||||
position: Int,
|
||||
images: ArrayList<Image>,
|
||||
selectedImages: ArrayList<Image>
|
||||
) {
|
||||
val intent = Intent(this, ZoomableActivity::class.java)
|
||||
intent.putExtra(CustomSelectorConstants.PRESENT_POSITION, position);
|
||||
intent.putParcelableArrayListExtra(CustomSelectorConstants.TOTAL_IMAGES, images)
|
||||
intent.putParcelableArrayListExtra(
|
||||
CustomSelectorConstants.TOTAL_SELECTED_IMAGES,
|
||||
selectedImages
|
||||
)
|
||||
startActivityForResult(intent, 101)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import fr.free.nrw.commons.R
|
|||
import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao
|
||||
import fr.free.nrw.commons.customselector.database.UploadedStatusDao
|
||||
import fr.free.nrw.commons.customselector.helper.ImageHelper
|
||||
import fr.free.nrw.commons.customselector.listeners.PassDataListener
|
||||
import fr.free.nrw.commons.customselector.helper.ImageHelper.CUSTOM_SELECTOR_PREFERENCE_KEY
|
||||
import fr.free.nrw.commons.customselector.helper.ImageHelper.SWITCH_STATE_PREFERENCE_KEY
|
||||
import fr.free.nrw.commons.customselector.listeners.ImageSelectListener
|
||||
|
|
@ -42,7 +43,7 @@ import kotlin.collections.ArrayList
|
|||
/**
|
||||
* Custom Selector Image Fragment.
|
||||
*/
|
||||
class ImageFragment: CommonsDaggerSupportFragment(), RefreshUIListener {
|
||||
class ImageFragment: CommonsDaggerSupportFragment(), RefreshUIListener, PassDataListener {
|
||||
|
||||
/**
|
||||
* Current bucketId.
|
||||
|
|
@ -293,6 +294,7 @@ class ImageFragment: CommonsDaggerSupportFragment(), RefreshUIListener {
|
|||
* notifyDataSetChanged, rebuild the holder views to account for deleted images.
|
||||
*/
|
||||
override fun onResume() {
|
||||
Log.d("haha", "onResume: ")
|
||||
imageAdapter.notifyDataSetChanged()
|
||||
super.onResume()
|
||||
}
|
||||
|
|
@ -327,4 +329,8 @@ class ImageFragment: CommonsDaggerSupportFragment(), RefreshUIListener {
|
|||
override fun refresh() {
|
||||
imageAdapter.refresh(filteredImages, allImages)
|
||||
}
|
||||
|
||||
override fun passSelectedImages(selectedImages: ArrayList<Image>){
|
||||
imageAdapter.setSelectedImages(selectedImages)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity;
|
|||
import fr.free.nrw.commons.description.DescriptionEditActivity;
|
||||
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity;
|
||||
import fr.free.nrw.commons.explore.SearchActivity;
|
||||
import fr.free.nrw.commons.media.ZoomableActivity;
|
||||
import fr.free.nrw.commons.notification.NotificationActivity;
|
||||
import fr.free.nrw.commons.profile.ProfileActivity;
|
||||
import fr.free.nrw.commons.review.ReviewActivity;
|
||||
|
|
@ -75,4 +76,7 @@ public abstract class ActivityBuilderModule {
|
|||
|
||||
@ContributesAndroidInjector
|
||||
abstract DescriptionEditActivity bindDescriptionEditActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ZoomableActivity bindZoomableActivity();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
387
app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.kt
Normal file
387
app/src/main/java/fr/free/nrw/commons/media/ZoomableActivity.kt
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
package fr.free.nrw.commons.media
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
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.OnSwipeTouchListener
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
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
|
||||
|
||||
class ZoomableActivity : BaseActivity() {
|
||||
|
||||
private lateinit var imageUri: Uri
|
||||
|
||||
@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
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* 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)
|
||||
images = intent.getParcelableArrayListExtra(
|
||||
CustomSelectorConstants.TOTAL_IMAGES
|
||||
)
|
||||
selectedImages = intent.getParcelableArrayListExtra(
|
||||
CustomSelectorConstants.TOTAL_SELECTED_IMAGES
|
||||
)
|
||||
position = intent.getIntExtra(CustomSelectorConstants.PRESENT_POSITION, 0)
|
||||
imageUri = if (images.isNullOrEmpty()) {
|
||||
intent.data as Uri
|
||||
} else {
|
||||
images!![position].uri
|
||||
}
|
||||
Timber.d("URl = $imageUri")
|
||||
setContentView(R.layout.activity_zoomable)
|
||||
ButterKnife.bind(this)
|
||||
init(imageUri)
|
||||
onSwap()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle swap gestures. Ex. onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown
|
||||
*/
|
||||
private fun onSwap() {
|
||||
if (!images.isNullOrEmpty()) {
|
||||
photo!!.setOnTouchListener(object : OnSwipeTouchListener(this) {
|
||||
override fun onSwipeLeft() {
|
||||
super.onSwipeLeft()
|
||||
if (position < images!!.size - 1) {
|
||||
position++
|
||||
init(images!![position].uri)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
"No more images found",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSwipeRight() {
|
||||
super.onSwipeRight()
|
||||
if (position > 0) {
|
||||
position--
|
||||
init(images!![position].uri)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
"No more images found",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSwipeUp() {
|
||||
super.onSwipeUp()
|
||||
scope.launch {
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
images!![position].uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
var isNonActionable = notForUploadStatusDao.find(imageSHA1)
|
||||
if (isNonActionable > 0) {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
"Can't select this image for upload", Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
isNonActionable =
|
||||
uploadedStatusDao.findByImageSHA1(imageSHA1, true)
|
||||
if (isNonActionable > 0) {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
"Can't select this image for upload", 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,
|
||||
"Can't select this image for upload",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
if (!selectedImages!!.contains(images!![position])) {
|
||||
selectedImages!!.add(images!![position])
|
||||
}
|
||||
position = getNextActionableImage(position + 1)
|
||||
init(images!![position].uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSwipeDown() {
|
||||
super.onSwipeDown()
|
||||
scope.launch {
|
||||
insertInNotForUpload(images!![position])
|
||||
if (position < images!!.size - 1) {
|
||||
position++
|
||||
init(images!![position].uri)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@ZoomableActivity,
|
||||
"No more images found",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets next actionable image
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 index from list
|
||||
*/
|
||||
private fun getIndex(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 = getIndex(selectedImages, images!![position])
|
||||
val isSelected = selectedIndex != -1
|
||||
if (isSelected) {
|
||||
itemSelected(selectedIndex + 1)
|
||||
} else {
|
||||
itemUnselected()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an image in Not For Upload Database
|
||||
*/
|
||||
private suspend fun insertInNotForUpload(it: Image) {
|
||||
val imageSHA1 = CustomSelectorUtils.getImageSHA1(
|
||||
it.uri,
|
||||
ioDispatcher,
|
||||
fileUtilsWrapper,
|
||||
contentResolver
|
||||
)
|
||||
notForUploadStatusDao.insert(
|
||||
NotForUploadStatus(
|
||||
imageSHA1,
|
||||
true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Send selected images in fragment
|
||||
*/
|
||||
override fun onBackPressed() {
|
||||
if (!images.isNullOrEmpty()) {
|
||||
val returnIntent = Intent()
|
||||
returnIntent.putParcelableArrayListExtra(
|
||||
CustomSelectorConstants.NEW_SELECTED_IMAGES,
|
||||
selectedImages
|
||||
)
|
||||
setResult(Activity.RESULT_OK, returnIntent)
|
||||
finish()
|
||||
}
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
scope.cancel()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import android.content.Context
|
|||
import android.net.Uri
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
import fr.free.nrw.commons.filepicker.PickedFiles
|
||||
import fr.free.nrw.commons.customselector.ui.selector.ImageLoader
|
||||
import fr.free.nrw.commons.filepicker.PickedFiles
|
||||
import fr.free.nrw.commons.media.MediaClient
|
||||
|
|
@ -26,17 +27,18 @@ class CustomSelectorUtils {
|
|||
/**
|
||||
* Get image sha1 from uri, used to retrieve the original image sha1.
|
||||
*/
|
||||
suspend fun getImageSHA1(uri: Uri,
|
||||
ioDispatcher : CoroutineDispatcher,
|
||||
fileUtilsWrapper: FileUtilsWrapper,
|
||||
contentResolver: ContentResolver
|
||||
suspend fun getImageSHA1(
|
||||
uri: Uri,
|
||||
ioDispatcher: CoroutineDispatcher,
|
||||
fileUtilsWrapper: FileUtilsWrapper,
|
||||
contentResolver: ContentResolver
|
||||
): String {
|
||||
return withContext(ioDispatcher) {
|
||||
|
||||
try {
|
||||
val result = fileUtilsWrapper.getSHA1(contentResolver.openInputStream(uri))
|
||||
result
|
||||
} catch (e: FileNotFoundException){
|
||||
} catch (e: FileNotFoundException) {
|
||||
e.printStackTrace()
|
||||
""
|
||||
}
|
||||
|
|
@ -44,16 +46,15 @@ class CustomSelectorUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Generate Modified SHA1 using present Exif settings.
|
||||
*
|
||||
* @return modified sha1
|
||||
* Generates modified SHA1 of an image
|
||||
*/
|
||||
suspend fun generateModifiedSHA1(image: Image,
|
||||
defaultDispatcher : CoroutineDispatcher,
|
||||
context: Context,
|
||||
fileProcessor: FileProcessor,
|
||||
fileUtilsWrapper: FileUtilsWrapper
|
||||
) : String {
|
||||
suspend fun generateModifiedSHA1(
|
||||
image: Image,
|
||||
defaultDispatcher: CoroutineDispatcher,
|
||||
context: Context,
|
||||
fileProcessor: FileProcessor,
|
||||
fileUtilsWrapper: FileUtilsWrapper
|
||||
): String {
|
||||
return withContext(defaultDispatcher) {
|
||||
val uploadableFile = PickedFiles.pickedExistingPicture(context, image.uri)
|
||||
val exifInterface: ExifInterface? = try {
|
||||
|
|
@ -64,7 +65,9 @@ class CustomSelectorUtils {
|
|||
}
|
||||
fileProcessor.redactExifTags(exifInterface, fileProcessor.getExifTagsToRedact())
|
||||
val sha1 =
|
||||
fileUtilsWrapper.getSHA1(fileUtilsWrapper.getFileInputStream(uploadableFile.filePath))
|
||||
fileUtilsWrapper.getSHA1(
|
||||
fileUtilsWrapper.getFileInputStream(uploadableFile.filePath)
|
||||
)
|
||||
uploadableFile.file.delete()
|
||||
sha1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,4 +23,21 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/selection_count"
|
||||
android:layout_width="@dimen/dimen_20"
|
||||
android:layout_height="@dimen/dimen_20"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/black"
|
||||
android:layout_margin="@dimen/dimen_6"
|
||||
android:gravity="center|center_vertical"
|
||||
style="@style/TextAppearance.AppCompat.Small"
|
||||
android:text="12"
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/circle_shape"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue