[GSoC] Added Bubble Scroll (#5023)

* Library added

* Bubble scroll implemented

* Left and right swipe

* Requested changes
This commit is contained in:
Ayan Sarkar 2022-08-03 19:15:59 +05:30 committed by GitHub
parent 40323be3a0
commit 5559282c1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 58 additions and 7 deletions

View file

@ -122,6 +122,7 @@ dependencies {
implementation "androidx.exifinterface:exifinterface:1.3.2" implementation "androidx.exifinterface:exifinterface:1.3.2"
implementation "androidx.core:core-ktx:$CORE_KTX_VERSION" implementation "androidx.core:core-ktx:$CORE_KTX_VERSION"
implementation "androidx.multidex:multidex:2.0.1" implementation "androidx.multidex:multidex:2.0.1"
compile 'com.simplecityapps:recyclerview-fastscroll:2.0.1'
//swipe_layout //swipe_layout
implementation 'com.daimajia.swipelayout:library:1.2.0@aar' implementation 'com.daimajia.swipelayout:library:1.2.0@aar'

View file

@ -41,7 +41,13 @@ data class Image(
/** /**
sha1 : sha1 of original image. sha1 : sha1 of original image.
*/ */
var sha1: String = "" var sha1: String = "",
/**
* date: Addition date of the image to show it inside the bubble during bubble scroll.
*/
var date: String = ""
) : Parcelable { ) : Parcelable {
/** /**
@ -54,6 +60,7 @@ data class Image(
parcel.readString()!!, parcel.readString()!!,
parcel.readLong(), parcel.readLong(),
parcel.readString()!!, parcel.readString()!!,
parcel.readString()!!,
parcel.readString()!! parcel.readString()!!
) )
@ -68,6 +75,7 @@ data class Image(
parcel.writeLong(bucketId) parcel.writeLong(bucketId)
parcel.writeString(bucketName) parcel.writeString(bucketName)
parcel.writeString(sha1) parcel.writeString(sha1)
parcel.writeString(date)
} }
/** /**

View file

@ -10,6 +10,7 @@ import androidx.constraintlayout.widget.Group
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.customselector.helper.ImageHelper import fr.free.nrw.commons.customselector.helper.ImageHelper
import fr.free.nrw.commons.customselector.listeners.ImageSelectListener import fr.free.nrw.commons.customselector.listeners.ImageSelectListener
@ -36,7 +37,7 @@ class ImageAdapter(
private var imageLoader: ImageLoader private var imageLoader: ImageLoader
): ):
RecyclerViewAdapter<ImageAdapter.ImageViewHolder>(context) { RecyclerViewAdapter<ImageAdapter.ImageViewHolder>(context), FastScrollRecyclerView.SectionedAdapter {
/** /**
* ImageSelectedOrUpdated payload class. * ImageSelectedOrUpdated payload class.
@ -278,4 +279,11 @@ class ImageAdapter(
} }
/**
* Returns the text for showing inside the bubble during bubble scroll.
*/
override fun getSectionName(position: Int): String {
return images[position].date
}
} }

View file

@ -3,10 +3,15 @@ package fr.free.nrw.commons.customselector.ui.selector
import android.content.ContentUris import android.content.ContentUris
import android.content.Context import android.content.Context
import android.provider.MediaStore import android.provider.MediaStore
import android.text.format.DateFormat
import fr.free.nrw.commons.customselector.listeners.ImageLoaderListener import fr.free.nrw.commons.customselector.listeners.ImageLoaderListener
import fr.free.nrw.commons.customselector.model.Image import fr.free.nrw.commons.customselector.model.Image
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.util.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
/** /**
@ -28,7 +33,9 @@ class ImageFileLoader(val context: Context) : CoroutineScope{
MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATA, MediaStore.Images.Media.DATA,
MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_ID,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME) MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
MediaStore.Images.Media.DATE_ADDED
)
/** /**
* Load Device Images under coroutine. * Load Device Images under coroutine.
@ -57,6 +64,7 @@ class ImageFileLoader(val context: Context) : CoroutineScope{
val dataColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATA) val dataColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
val bucketIdColumn = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID) val bucketIdColumn = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID)
val bucketNameColumn = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME) val bucketNameColumn = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME)
val dateColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED)
val images = arrayListOf<Image>() val images = arrayListOf<Image>()
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
@ -70,6 +78,7 @@ class ImageFileLoader(val context: Context) : CoroutineScope{
val path = cursor.getString(dataColumn) val path = cursor.getString(dataColumn)
val bucketId = cursor.getLong(bucketIdColumn) val bucketId = cursor.getLong(bucketIdColumn)
val bucketName = cursor.getString(bucketNameColumn) val bucketName = cursor.getString(bucketNameColumn)
val date = cursor.getLong(dateColumn)
val file = val file =
if (path == null || path.isEmpty()) { if (path == null || path.isEmpty()) {
@ -84,7 +93,22 @@ class ImageFileLoader(val context: Context) : CoroutineScope{
if (file != null && file.exists()) { if (file != null && file.exists()) {
if (name != null && path != null && bucketName != null) { if (name != null && path != null && bucketName != null) {
val uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id) val uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
val image = Image(id, name, uri, path, bucketId, bucketName)
val calendar = Calendar.getInstance()
calendar.timeInMillis = date * 1000L
val date: Date = calendar.time
val dateFormat = DateFormat.getMediumDateFormat(context)
val formattedDate = dateFormat.format(date)
val image = Image(
id,
name,
uri,
path,
bucketId,
bucketName,
date = (formattedDate)
)
images.add(image) images.add(image)
} }
} }

View file

@ -5,11 +5,18 @@
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.recyclerview.widget.RecyclerView <com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
android:id="@+id/selector_rv" android:id="@+id/selector_rv"
android:background="?attr/mainBackground" android:background="?attr/mainBackground"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:fastScrollPopupBgColor="@color/primaryColor"
app:fastScrollPopupTextColor="@android:color/primary_text_dark"
app:fastScrollPopupTextSize="@dimen/subheading_text_size"
app:fastScrollPopupBackgroundSize="@dimen/bubble_size"
app:fastScrollThumbColor="@color/primaryColor"
app:fastScrollTrackColor="@color/upload_overlay_background_light"
app:fastScrollPopupPosition="adjacent"
/> />
<TextView <TextView

View file

@ -41,6 +41,7 @@
<dimen name="progressbar_stroke">3dp</dimen> <dimen name="progressbar_stroke">3dp</dimen>
<dimen name="notification_width">110dp</dimen> <dimen name="notification_width">110dp</dimen>
<dimen name="notification_height">160dp</dimen> <dimen name="notification_height">160dp</dimen>
<dimen name="bubble_size">36dp</dimen>
<!-- Text sizes --> <!-- Text sizes -->
<dimen name="heading_text_size">24sp</dimen> <dimen name="heading_text_size">24sp</dimen>

View file

@ -64,7 +64,8 @@ class ImageFileLoaderTest {
MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATA, MediaStore.Images.Media.DATA,
MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_ID,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
MediaStore.Images.Media.DATE_ADDED
) )
Whitebox.setInternalState(imageFileLoader, "coroutineContext", coroutineContext) Whitebox.setInternalState(imageFileLoader, "coroutineContext", coroutineContext)
@ -103,6 +104,7 @@ class ImageFileLoaderTest {
anyOrNull(), anyOrNull(),
anyOrNull(), anyOrNull(),
anyOrNull(), anyOrNull(),
anyOrNull(),
anyOrNull() anyOrNull()
) )
} doReturn imageCursor; } doReturn imageCursor;