Skip to content

Commit

Permalink
fix sort on q (esafirm#369)
Browse files Browse the repository at this point in the history
add image generator helper for load test
  • Loading branch information
esafirm authored and Sarthak Mishra committed Sep 1, 2023
1 parent 7f51f1f commit 247171b
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,28 @@ class ImagePickerFragment : Fragment() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(ContentObserverTrigger(
requireActivity().contentResolver,
this::loadData
))
lifecycle.addObserver(
ContentObserverTrigger(
requireActivity().contentResolver,
this::loadData
)
)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
presenter = ImagePickerPresenter(DefaultImageFileLoader(requireContext()))

if (::interactionListener.isInitialized.not()) {
throw RuntimeException("ImagePickerFragment needs an " +
"ImagePickerInteractionListener. This will be set automatically if the " +
"activity implements ImagePickerInteractionListener, and can be set manually " +
"with fragment.setInteractionListener(listener).")
throw RuntimeException(
"ImagePickerFragment needs an " +
"ImagePickerInteractionListener. This will be set automatically if the " +
"activity implements ImagePickerInteractionListener, and can be set manually " +
"with fragment.setInteractionListener(listener)."
)
}

val interactionListener = this.interactionListener
Expand Down Expand Up @@ -114,7 +122,7 @@ class ImagePickerFragment : Fragment() {
}

val isEmpty = state.images.isEmpty()
if (isEmpty) {
if (isEmpty && state.isLoading.not()) {
showEmpty()
return@observe
}
Expand Down Expand Up @@ -173,7 +181,10 @@ class ImagePickerFragment : Fragment() {
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(STATE_KEY_RECYCLER, recyclerViewManager.recyclerState)
outState.putParcelableArrayList(STATE_KEY_SELECTED_IMAGES, recyclerViewManager.selectedImages as ArrayList<out Parcelable?>)
outState.putParcelableArrayList(
STATE_KEY_SELECTED_IMAGES,
recyclerViewManager.selectedImages as ArrayList<out Parcelable?>
)
}

/**
Expand Down Expand Up @@ -218,7 +229,10 @@ class ImagePickerFragment : Fragment() {
* Check permission
*/
private fun loadDataWithPermission() {
val rc = ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
val rc = ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
if (rc == PackageManager.PERMISSION_GRANTED) {
loadData()
} else {
Expand All @@ -236,7 +250,11 @@ class ImagePickerFragment : Fragment() {
private fun requestWriteExternalPermission() {
IpLogger.w("Write External permission is not granted. Requesting permission")
val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
if (ActivityCompat.shouldShowRequestPermissionRationale(requireActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
requireActivity(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
) {
requestPermissions(permissions, RC_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE)
} else {
val permission = ImagePickerPreferences.PREF_WRITE_EXTERNAL_STORAGE_REQUESTED
Expand All @@ -254,16 +272,22 @@ class ImagePickerFragment : Fragment() {
/**
* Handle permission results
*/
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
when (requestCode) {
RC_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
IpLogger.d("Write External permission granted")
loadData()
return
}
IpLogger.e("Permission not granted: results len = " + grantResults.size +
" Result code = " + if (grantResults.isNotEmpty()) grantResults[0] else "(empty)")
IpLogger.e(
"Permission not granted: results len = " + grantResults.size +
" Result code = " + if (grantResults.isNotEmpty()) grantResults[0] else "(empty)"
)
interactionListener.cancel()
}
else -> {
Expand All @@ -277,8 +301,10 @@ class ImagePickerFragment : Fragment() {
* Open app settings screen
*/
private fun openAppSettings() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", requireActivity().packageName, null))
val intent = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", requireActivity().packageName, null)
)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.esafirm.imagepicker.features.fileloader

import android.annotation.SuppressLint
import android.content.ContentResolver
import android.content.Context
import android.database.Cursor
Expand Down Expand Up @@ -81,6 +82,7 @@ class DefaultImageFileLoader(private val context: Context) : ImageFileLoader {
MediaStore.Images.Media.BUCKET_DISPLAY_NAME
)

@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) {
Expand All @@ -103,25 +105,29 @@ class DefaultImageFileLoader(private val context: Context) : ImageFileLoader {
if (useNewApi) {
val args = Bundle().apply {
// Sort function
putString(
ContentResolver.QUERY_ARG_SORT_COLUMNS,
MediaStore.Files.FileColumns.DATE_MODIFIED
)
putStringArray(
ContentResolver.QUERY_ARG_SORT_COLUMNS,
arrayOf(MediaStore.Files.FileColumns.DATE_MODIFIED)
)
putInt(
ContentResolver.QUERY_ARG_SORT_DIRECTION,
ContentResolver.QUERY_SORT_DIRECTION_DESCENDING
)
// Selection
putString(
ContentResolver.QUERY_ARG_SQL_SELECTION,
selection
)
// Limit
if (limit != null) {
putInt(ContentResolver.QUERY_ARG_LIMIT, limit)
}
}

return context.contentResolver.query(sourceUri, projection, args, null)
}

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

Expand Down Expand Up @@ -165,7 +171,7 @@ class DefaultImageFileLoader(private val context: Context) : ImageFileLoader {
val result: MutableList<Image> = ArrayList()
val folderMap: MutableMap<String, Folder> = mutableMapOf()

if (cursor.moveToLast()) {
if (cursor.moveToFirst()) {
do {
val image = cursorToImage(cursor)

Expand All @@ -190,7 +196,7 @@ class DefaultImageFileLoader(private val context: Context) : ImageFileLoader {
}
}

} while (cursor.moveToPrevious())
} while (cursor.moveToNext())
}
cursor.close()

Expand All @@ -204,7 +210,6 @@ class DefaultImageFileLoader(private val context: Context) : ImageFileLoader {
val isLoadDataAgain = cursor?.count == FIRST_LIMIT
processData(cursor)

Thread.sleep(5000)
if (isLoadDataAgain) {
processData(queryData())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.esafirm.sample.helper


import androidx.test.filters.LargeTest
import androidx.test.rule.ActivityTestRule
import androidx.test.rule.GrantPermissionRule
import androidx.test.runner.AndroidJUnit4
import com.esafirm.sample.MainActivity
import com.esafirm.sample.utils.ImageGenerator
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

/**
* Remove ignore for generating only
*/
@LargeTest
@Ignore
@RunWith(AndroidJUnit4::class)
class ImageGeneratorScenario {

@Rule
@JvmField
var testRule = ActivityTestRule(MainActivity::class.java)

@Rule
@JvmField
var grantPermissionRule = GrantPermissionRule.grant(
"android.permission.WRITE_EXTERNAL_STORAGE"
)

@Test
fun generateImages() {
val generator = ImageGenerator()
generator.generateImages(30_000)
}
}
117 changes: 117 additions & 0 deletions sample/src/androidTest/java/com/esafirm/sample/utils/ImageGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.esafirm.sample.utils

import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.net.Uri
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import androidx.test.core.app.ApplicationProvider
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.IOException
import java.io.OutputStream

class ImageGenerator {

companion object {
private const val DEFAULT_SIZE = 100
}

fun generateImages(count: Int) {
val context: Context = ApplicationProvider.getApplicationContext()
val listOfFiles = (0 until count).map {
val number = it.toString().padStart(5, '0')
"ip_generated_${number}.jpg"
}
val dir = context.getExternalFilesDir(Environment.DIRECTORY_DCIM)

if (dir?.exists()?.not() == true) {
dir.mkdirs()
}

listOfFiles.forEachIndexed { index, path ->
val file = File(dir, path)
if (file.exists().not()) {
file.createNewFile()
}
Log.d("Generator", "Generating image in ${file.path}")
generateBitmapOnGivenFile(file, index.toString())
addImageToGallery(context.contentResolver, file)
}
}

private fun addImageToGallery(cr: ContentResolver, imageFile: File): Uri? {
val values = ContentValues().apply {
put(MediaStore.Images.Media.TITLE, imageFile.name)
put(MediaStore.Images.Media.DISPLAY_NAME, imageFile.name)
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
put(MediaStore.Images.Media.DATA, imageFile.path)
}
return cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
}

@Throws(IOException::class)
private fun generateBitmapOnGivenFile(
file: File,
identifier: String,
width: Int = DEFAULT_SIZE,
height: Int = DEFAULT_SIZE
) {
val stream = file.outputStream()
val bmp = createBitmap(identifier, width, height)

writeBitmap(stream, bmp)
}

private fun createBitmap(identifier: String, width: Int, height: Int): Bitmap {
val conf = Bitmap.Config.ARGB_8888
val bmp = Bitmap.createBitmap(width, height, conf)

val canvas = Canvas(bmp)

val paint = Paint()
paint.color = Color.RED

val widthPart = width / 10
val heightPart = height / 10
canvas.drawRect(
widthPart.toFloat(),
heightPart.toFloat(),
(width - widthPart).toFloat(),
(height - heightPart).toFloat(),
paint
)

val textPaint = Paint()
textPaint.color = Color.BLACK
textPaint.textAlign = Paint.Align.CENTER

val xPos = canvas.width / 2
val yPos = (canvas.height / 2 - (textPaint.descent() + textPaint.ascent()) / 2).toInt()
//((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.

canvas.drawText(identifier, xPos.toFloat(), yPos.toFloat(), textPaint)

canvas.save()

return bmp
}

@Throws(IOException::class)
private fun writeBitmap(stream: OutputStream, bmp: Bitmap) {
val byteArrayOutputStream = ByteArrayOutputStream()
bmp.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream)
val bitmapData = byteArrayOutputStream.toByteArray()
stream.write(bitmapData)
stream.flush()
stream.close()
}
}

0 comments on commit 247171b

Please sign in to comment.