Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select all button #438

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.esafirm.imagepicker.helper.diff.SimpleDiffUtilCallBack
import com.esafirm.imagepicker.listeners.OnImageClickListener
import com.esafirm.imagepicker.listeners.OnImageSelectedListener
import com.esafirm.imagepicker.model.Image
import kotlin.math.min

class ImagePickerAdapter(
context: Context,
Expand Down Expand Up @@ -130,6 +131,20 @@ class ImagePickerAdapter(
}
}

fun selectAll(limit: Int) {
mutateSelection {
selectedImages.clear()
val itemCount = min(limit, this.itemCount)
for (i in 0 until itemCount) {
val image = this.getItem(i)
if (image != null) {
selectedImages.add(image)
}
}
notifyDataSetChanged()
}
}

private fun mutateSelection(runnable: Runnable) {
runnable.run()
imageSelectedListener?.invoke(selectedImages)
Expand All @@ -147,4 +162,4 @@ class ImagePickerAdapter(
val fileTypeIndicator = binding.efItemFileTypeIndicator
val container = binding.root as FrameLayout
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ class ImagePickerActivity : AppCompatActivity(), ImagePickerInteractionListener
title = ConfigUtils.getDoneButtonText(this@ImagePickerActivity, config!!)
isVisible = imagePickerFragment.isShowDoneButton
}
menu.findItem(R.id.menu_select_all).apply {
title = ConfigUtils.getAllButtonText(this@ImagePickerActivity, config!!)
isVisible = imagePickerFragment.isShowAllButton
}
}
return super.onPrepareOptionsMenu(menu)
}
Expand All @@ -125,6 +129,10 @@ class ImagePickerActivity : AppCompatActivity(), ImagePickerInteractionListener
imagePickerFragment.onDone()
return true
}
if (id == R.id.menu_select_all) {
imagePickerFragment.onSelectAll()
return true
}
if (id == R.id.menu_camera) {
imagePickerFragment.captureImage()
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class ImagePickerConfig(
var folderTitle: String? = null,
var imageTitle: String? = null,
var doneButtonText: String? = null,
var allButtonText: String? = null,
var arrowColor: Int = NO_COLOR,
var limit: Int = IpCons.MAX_LIMIT,
@StyleRes var theme: Int = 0,
Expand All @@ -28,7 +29,8 @@ class ImagePickerConfig(
override var savePath: ImagePickerSavePath = ImagePickerSavePath.DEFAULT,
override var returnMode: ReturnMode = ReturnMode.NONE,
override var isSaveImage: Boolean = true,
var showDoneButtonAlways: Boolean = false
var showDoneButtonAlways: Boolean = false,
var showAllButton: Boolean = false,
) : BaseConfig(), Parcelable {

@IgnoredOnParcel
Expand All @@ -52,4 +54,4 @@ fun ImagePickerConfig.enableLog(isEnable: Boolean) {
IpLogger.setEnable(isEnable)
}

fun List<Image>.toFiles() = this.map { File(it.path) }
fun List<Image>.toFiles() = this.map { File(it.path) }
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,12 @@ class ImagePickerFragment : Fragment() {

private val permissions: Array<String> by lazy {
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ->
arrayOf(Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
if (config.isIncludeVideo)
arrayOf(Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO)
else
arrayOf(Manifest.permission.READ_MEDIA_IMAGES)
}

Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
|| Environment.isExternalStorageLegacy() -> arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
Expand Down Expand Up @@ -247,6 +251,14 @@ class ImagePickerFragment : Fragment() {
presenter.onDoneSelectImages(recyclerViewManager.selectedImages, config)
}

/**
* On select all images clicked
* Select all images in the current folder
*/
fun onSelectAll() {
recyclerViewManager.selectAllImages()
}

/**
* Config recyclerView when configuration changed
*/
Expand Down Expand Up @@ -355,6 +367,9 @@ class ImagePickerFragment : Fragment() {
val isShowDoneButton: Boolean
get() = recyclerViewManager.isShowDoneButton

val isShowAllButton: Boolean
get() = recyclerViewManager.isShowAllButton

override fun onAttach(context: Context) {
super.onAttach(context)
if (context is ImagePickerInteractionListener) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,14 @@ class DefaultImageFileLoader(private val context: Context) : ImageFileLoader {
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME
MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
MediaStore.Images.Media.MIME_TYPE,
)

@SuppressLint("InlinedApi")
private fun queryData(limit: Int? = null): Cursor? {
val useNewApi = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
val sourceUri = if (limit != null && useNewApi) {
val sourceUri = if (limit != null && !useNewApi) {
getSourceUri().buildUpon()
.appendQueryParameter(QUERY_LIMIT, limit.toString())
.build()
Expand Down Expand Up @@ -127,9 +128,7 @@ class DefaultImageFileLoader(private val context: Context) : ImageFileLoader {
return context.contentResolver.query(sourceUri, projection, args, null)
}

val sortOrder = "${MediaStore.Images.Media.DATE_MODIFIED} DESC".let {
if (limit != null) "$it LIMIT $limit" else it
}
val sortOrder = "${MediaStore.Images.Media.DATE_MODIFIED} DESC"

return context.contentResolver.query(
sourceUri, projection,
Expand All @@ -155,9 +154,10 @@ class DefaultImageFileLoader(private val context: Context) : ImageFileLoader {

val id = cursor.getLong(cursor.getColumnIndex(projection[0]))
val name = cursor.getString(cursor.getColumnIndex(projection[1]))
val mimeType = cursor.getString(cursor.getColumnIndex(projection[4]))

if (name != null) {
return Image(id, name, path)
return Image(id, name, path, mimeType)
}
return null
}
Expand Down Expand Up @@ -201,7 +201,7 @@ class DefaultImageFileLoader(private val context: Context) : ImageFileLoader {
cursor.close()

val folders = folderMap.values.toList()
listener.onImageLoaded(result, folders)
listener.onImageLoaded(result, folders.sortedBy { it.folderName })
}

override fun run() {
Expand All @@ -227,4 +227,4 @@ class DefaultImageFileLoader(private val context: Context) : ImageFileLoader {
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.esafirm.imagepicker.listeners.OnImageSelectedListener
import com.esafirm.imagepicker.model.Folder
import com.esafirm.imagepicker.model.Image
import com.esafirm.imagepicker.view.GridSpacingItemDecoration
import java.lang.Integer.min

class RecyclerViewManager(
private val recyclerView: RecyclerView,
Expand Down Expand Up @@ -196,9 +197,19 @@ class RecyclerViewManager(
return true
}

fun selectAllImages() {
if (imageAdapter.selectedImages.size == min(config.limit, imageAdapter.itemCount)) {
imageAdapter.removeAllSelectedSingleClick()
} else {
imageAdapter.selectAll(config.limit)
}
}

val isShowDoneButton: Boolean
get() = (!isDisplayingFolderView
&& (imageAdapter.selectedImages.isNotEmpty() || config.showDoneButtonAlways)
&& config.returnMode !== ReturnMode.ALL && config.returnMode !== ReturnMode.GALLERY_ONLY)

}
val isShowAllButton: Boolean
get() = (!isDisplayingFolderView && config.showAllButton && config.mode == ImagePickerMode.MULTIPLE)
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,9 @@ object ConfigUtils {
val doneButtonText = config.doneButtonText
return if (doneButtonText.isNullOrBlank()) context.getString(R.string.ef_done) else doneButtonText
}
}

fun getAllButtonText(context: Context, config: ImagePickerConfig): String {
val allButtonText = config.allButtonText
return if (allButtonText.isNullOrBlank()) context.getString(R.string.ef_all) else allButtonText
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,16 @@ object ImagePickerUtils {
}

fun isVideoFormat(image: Image): Boolean {
val extension = getExtension(image.path)
val mimeType =
if (TextUtils.isEmpty(extension)) URLConnection.guessContentTypeFromName(image.path) else MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(extension)
val mimeType = getMimeType(image.path)
return mimeType != null && mimeType.startsWith("video")
}

fun getMimeType(path: String): String? {
val extension = getExtension(path)
return if (TextUtils.isEmpty(extension)) URLConnection.guessContentTypeFromName(path) else MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(extension)
}

fun getVideoDurationLabel(context: Context?, uri: Uri): String {
try {
val retriever = MediaMetadataRetriever()
Expand Down Expand Up @@ -145,4 +148,4 @@ object ImagePickerUtils {
data.putParcelableArrayListExtra(IpCons.EXTRA_SELECTED_IMAGES, imageArrayList)
return data
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Image(
val id: Long,
val name: String,
val path: String,
val mimeType: String?,
) : Parcelable {

@IgnoredOnParcel
Expand Down Expand Up @@ -49,6 +50,7 @@ class Image(
result = 31 * result + name.hashCode()
result = 31 * result + path.hashCode()
result = 31 * result + (uriHolder?.hashCode() ?: 0)
result = 31 * result + (mimeType?.hashCode() ?: 0)
return result
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ object ImageFactory {
return listOf(Image(
id = ContentUris.parseId(uri),
name = ImagePickerUtils.getNameFromFilePath(path),
path = path
path = path,
mimeType = ImagePickerUtils.getMimeType(path),
))
}
}
}
4 changes: 4 additions & 0 deletions imagepicker/src/main/res/menu/ef_image_picker_menu_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />

<item
android:id="@+id/menu_select_all"
android:title="@string/ef_all"
app:showAsAction="always"/>
<item
android:id="@+id/menu_done"
android:title="@string/ef_done"
Expand Down
1 change: 1 addition & 0 deletions imagepicker/src/main/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@
<string name="ef_msg_no_write_external_permission">عليك إعطاء التطبيق الصلاحية لاستخدام الصور</string>
<string name="ef_msg_no_camera_permission">عليك إعطاء التطبيق الصلاحية لاستخدام الكاميرا لتتمكن من التصوير</string>
<string name="ef_msg_limit_images">تحديد إختيار الصور</string>
<string name="ef_all">الجميع</string>
</resources>
1 change: 1 addition & 0 deletions imagepicker/src/main/res/values-ca/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
<string name="ef_gif" translatable="false">GIF</string>
<string name="ef_content_desc_folder" translatable="false">Folder</string>
<string name="ef_content_desc_image" translatable="false">Image</string>
<string name="ef_all">Tot</string>

</resources>
3 changes: 2 additions & 1 deletion imagepicker/src/main/res/values-da/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@
<string name="ef_gif" translatable="false">GIF</string>
<string name="ef_content_desc_folder" translatable="false">Folder</string>
<string name="ef_content_desc_image" translatable="false">Image</string>
<string name="ef_all">Alle</string>

</resources>
</resources>
1 change: 1 addition & 0 deletions imagepicker/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@
<string name="ef_gif" translatable="false">GIF</string>
<string name="ef_content_desc_folder" translatable="false">Folder</string>
<string name="ef_content_desc_image" translatable="false">Image</string>
<string name="ef_all">Alle</string>

</resources>
1 change: 1 addition & 0 deletions imagepicker/src/main/res/values-es-rES/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
<string name="ef_gif" translatable="false">GIF</string>
<string name="ef_content_desc_folder" translatable="false">Folder</string>
<string name="ef_content_desc_image" translatable="false">Image</string>
<string name="ef_all">Todas</string>

</resources>
1 change: 1 addition & 0 deletions imagepicker/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@

<!--Don\'t Translate-->
<string name="ef_gif">GIF</string>
<string name="ef_all">Tout</string>

</resources>
1 change: 1 addition & 0 deletions imagepicker/src/main/res/values-in/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@
<string name="ef_msg_no_write_external_permission">Mohon berikan izin terhadap penyimpanan untuk memilih gambar</string>
<string name="ef_msg_no_camera_permission">Mohon berikan izin terhadap kamera untuk mengambil gambar</string>
<string name="ef_msg_limit_images">Gambar yang terpilih sudah mencapai batas maksimal</string>
<string name="ef_all">Semua</string>
</resources>
1 change: 1 addition & 0 deletions imagepicker/src/main/res/values-it/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
<string name="ef_msg_no_camera_permission">Dai il permesso per poter scattare una foto</string>
<string name="ef_msg_limit_images">Hai raggiunto il limite massimo di selezione</string>
<string name="ef_ltitle_permission_denied">Permesso negato</string>
<string name="ef_all">Tutto</string>

</resources>
1 change: 1 addition & 0 deletions imagepicker/src/main/res/values-ja/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@
<string name="ef_msg_no_camera_permission">端末のカメラへのアクセスを許可してください</string>
<string name="ef_msg_limit_images">選択できる画像の上限です</string>
<string name="ef_ltitle_permission_denied">権限がありません</string>
<string name="ef_all">全て</string>

</resources>
1 change: 1 addition & 0 deletions imagepicker/src/main/res/values-ko/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
<string name="ef_msg_no_write_external_permission">이미지를 불러오기 위해 저장공간 권한을 허용해주세요</string>
<string name="ef_msg_no_camera_permission">사진을 찍기위해 카메라 권한을 허용해주세요</string>
<string name="ef_msg_limit_images">이미지 선택 제한 초과</string>
<string name="ef_all">모두</string>

</resources>
33 changes: 33 additions & 0 deletions imagepicker/src/main/res/values-nl/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="ef_permission_write_external_rationale">Geef toegang tot externe opslag om afbeeldingen te
selecteren
</string>

<string name="ef_ok">OK</string>
<string name="ef_done">KLAAR</string>
<string name="ef_camera">CAMERA</string>

<string name="ef_title_folder">Album</string>
<string name="ef_title_select_image">Tik om afbeeldingen te selecteren</string>
<string name="ef_ltitle_permission_denied">Geen toestemming</string>

<string name="ef_selected">%d geselecteerd</string>
<string name="ef_selected_with_limit">%1$d/%2$d geselecteerd</string>

<string name="ef_error_create_image_file">Kan afbeeldingsbestand niet maken</string>
<string name="ef_error_no_camera">Geen camera gevonden</string>
<string name="ef_error_null_cursor">Oeps! Er is iets misgegaan!</string>

<string name="ef_msg_empty_images">Geen afbeeldingen gevonden</string>
<string name="ef_msg_no_write_external_permission">Geef toestemming voor opslag om afbeeldingen te selecteren</string>
<string name="ef_msg_no_camera_permission">Bitte erlaube Zugriff auf die Kamera um Bilder aufzunehmen</string>
<string name="ef_msg_limit_images">Limiet afbeeldingsselectie</string>

<!--Don't Translate-->
<string name="ef_gif" translatable="false">GIF</string>
<string name="ef_content_desc_folder" translatable="false">Folder</string>
<string name="ef_content_desc_image" translatable="false">Image</string>
<string name="ef_all">Alle</string>

</resources>
Loading