mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 04:43:54 +01:00
[GSoC] Added Bubble Scroll (#5023)
* Library added * Bubble scroll implemented * Left and right swipe * Requested changes
This commit is contained in:
parent
40323be3a0
commit
5559282c1a
7 changed files with 58 additions and 7 deletions
|
|
@ -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'
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue