Skip to content

Commit

Permalink
Refactor launcher (#342)
Browse files Browse the repository at this point in the history
* Refactor launcher

* sample now use view binding

* fix test compile
  • Loading branch information
esafirm committed Mar 14, 2021
1 parent 9ac0f8a commit a09344f
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 161 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package com.esafirm.imagepicker.features
import android.content.Context
import android.content.Intent
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import com.esafirm.imagepicker.features.cameraonly.CameraOnlyConfig
import com.esafirm.imagepicker.features.common.BaseConfig
import com.esafirm.imagepicker.helper.ConfigUtils.checkConfig
import com.esafirm.imagepicker.helper.LocaleManager
import com.esafirm.imagepicker.model.Image
Expand All @@ -13,29 +16,42 @@ import com.esafirm.imagepicker.model.Image
/* > Ext */
/* --------------------------------------------------- */

typealias ImagePickerLauncher = ImagePickerConfig.() -> Unit
class ImagePickerLauncher(
private val context: Context,
private val resultLauncher: ActivityResultLauncher<Intent>
) {
fun launch(config: BaseConfig = ImagePickerConfig()) {
val finalConfig = if (config is ImagePickerConfig) checkConfig(config) else config
val intent = createImagePickerIntent(context, finalConfig)
resultLauncher.launch(intent)
}
}

typealias ImagePickerCallback = (List<Image>) -> Unit

fun Fragment.registerImagePicker(
callback: ImagePickerCallback
): ImagePickerLauncher {
val fragment = this
val launcher = createLauncher(callback)
return { launcher.launch(createImagePickerIntent(fragment.requireContext(), checkConfig(this))) }
return ImagePickerLauncher(requireContext(), createLauncher(callback))
}

fun ComponentActivity.registerImagePicker(
callback: ImagePickerCallback
): ImagePickerLauncher {
val context = this
val launcher = createLauncher(callback)
return { launcher.launch(createImagePickerIntent(context, checkConfig(this))) }
return ImagePickerLauncher(this, createLauncher(callback))
}

fun createImagePickerIntent(context: Context, config: ImagePickerConfig): Intent {
config.language?.run { LocaleManager.language = this }
fun createImagePickerIntent(context: Context, config: BaseConfig): Intent {
val intent = Intent(context, ImagePickerActivity::class.java)
intent.putExtra(ImagePickerConfig::class.java.simpleName, config)
when (config) {
is ImagePickerConfig -> {
config.language?.run { LocaleManager.language = this }
intent.putExtra(ImagePickerConfig::class.java.simpleName, config)
}
is CameraOnlyConfig -> {
intent.putExtra(CameraOnlyConfig::class.java.simpleName, config)
}
}
return intent
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ class ImagePickerSavePath(
val isRelative: Boolean = true
) : Parcelable {
companion object {
val DEFAULT = ImagePickerSavePath("Camera", false)
val DEFAULT = ImagePickerSavePath("Camera", true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ class DefaultCameraModule : CameraModule {
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
ImagePickerUtils.grantAppPermission(context, intent, uri)
currentUri = uri.toString()
return intent
}
return null
return intent
}

private fun prepareForNewIntent() {
Expand Down
8 changes: 6 additions & 2 deletions sample/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
final sdk = rootProject.ext.sdk

compileSdkVersion sdk.compileSdk

buildFeatures {
viewBinding true
}

defaultConfig {
minSdkVersion sdk.minSdk
targetSdkVersion sdk.targetSdk
Expand All @@ -18,12 +21,14 @@ android {

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
Expand All @@ -43,7 +48,6 @@ dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2'
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'
implementation "androidx.core:core-ktx:$core_ktx_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

/* UI Test */
final espressoVersion = '3.3.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class CameraOnlyTest {
@Rule
@JvmField
var grantPermissionRule = GrantPermissionRule.grant(
"android.permission.CAMERA",
"android.permission.WRITE_EXTERNAL_STORAGE"
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ object Views {
allOf(withId(R.id.button_pick_image), withText("PICK IMAGE"),
childAtPosition(
childAtPosition(
withId(R.id.ef_bottom_container),
withId(R.id.bottom_container),
0),
0),
isDisplayed()))
Expand Down
16 changes: 11 additions & 5 deletions sample/src/main/java/com/esafirm/sample/CustomUIActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ import com.esafirm.imagepicker.helper.IpLogger
import com.esafirm.imagepicker.helper.LocaleManager
import com.esafirm.imagepicker.helper.ViewUtils
import com.esafirm.imagepicker.model.Image
import kotlinx.android.synthetic.main.activity_custom_ui.*
import com.esafirm.sample.databinding.ActivityCustomUiBinding

/**
* This custom UI for ImagePicker puts the picker in the bottom half of the screen, and a preview of
* the last selected image in the top half.
*/
class CustomUIActivity : AppCompatActivity() {

private lateinit var binding: ActivityCustomUiBinding

private lateinit var actionBar: ActionBar
private lateinit var imagePickerFragment: ImagePickerFragment

Expand All @@ -41,6 +43,11 @@ class CustomUIActivity : AppCompatActivity() {

public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

binding = ActivityCustomUiBinding.inflate(layoutInflater).also {
setContentView(it.root)
}

setResult(RESULT_CANCELED)
config = intent.extras?.getParcelable(ImagePickerConfig::class.java.simpleName)
cameraOnlyConfig = intent.extras?.getParcelable(CameraOnlyConfig::class.java.simpleName)
Expand All @@ -50,7 +57,6 @@ class CustomUIActivity : AppCompatActivity() {
setTheme(theme)
}

setContentView(R.layout.activity_custom_ui)
setupView()

if (savedInstanceState != null) {
Expand Down Expand Up @@ -128,7 +134,7 @@ class CustomUIActivity : AppCompatActivity() {
}

private fun setupView() {
setSupportActionBar(toolbar as Toolbar)
setSupportActionBar(binding.toolbar as Toolbar)
checkNotNull(supportActionBar)

actionBar = supportActionBar!!
Expand Down Expand Up @@ -164,15 +170,15 @@ class CustomUIActivity : AppCompatActivity() {
if (imageList == null) error("Image list is null")

if (imageList.isEmpty()) {
photo_preview.setImageDrawable(null)
binding.photoPreview.setImageDrawable(null)
} else {
val imageUri = imageList[imageList.size - 1].uri
val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ImageDecoder.decodeBitmap(ImageDecoder.createSource(contentResolver, imageUri))
} else {
MediaStore.Images.Media.getBitmap(contentResolver, imageUri)
}
photo_preview.setImageBitmap(bitmap)
binding.photoPreview.setImageBitmap(bitmap)
}
}
}
Expand Down
57 changes: 33 additions & 24 deletions sample/src/main/java/com/esafirm/sample/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,57 @@ import android.os.Bundle
import android.os.Environment
import androidx.appcompat.app.AppCompatActivity
import com.esafirm.imagepicker.features.*
import com.esafirm.imagepicker.features.cameraonly.CameraOnlyConfig
import com.esafirm.imagepicker.features.imageloader.DefaultImageLoader
import com.esafirm.imagepicker.features.imageloader.ImageLoader
import com.esafirm.imagepicker.model.Image
import kotlinx.android.synthetic.main.activity_main.*
import com.esafirm.sample.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

private val images = arrayListOf<Image>()

private val imagePickerLauncher = registerImagePicker {
images.clear()
images.addAll(it)
printImages(images)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

button_pick_image.setOnClickListener { start() }
button_intent.setOnClickListener { startWithIntent() }
button_camera.setOnClickListener { captureImage() }
button_custom_ui.setOnClickListener { startCustomUI() }
button_launch_fragment.setOnClickListener {
binding = ActivityMainBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
setupButtons()
}

private fun setupButtons() = binding.run {
buttonPickImage.setOnClickListener { start() }
buttonIntent.setOnClickListener { startWithIntent() }
buttonCamera.setOnClickListener { captureImage() }
buttonCustomUi.setOnClickListener { startCustomUI() }
buttonLaunchFragment.setOnClickListener {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, MainFragment())
.commitAllowingStateLoss()
}
}

private fun captureImage() {
ImagePicker.cameraOnly().start(this)
imagePickerLauncher.launch(CameraOnlyConfig())
}

private fun createConfig(): ImagePickerConfig {
val returnAfterCapture = ef_switch_return_after_capture.isChecked
val isSingleMode = ef_switch_single.isChecked
val useCustomImageLoader = ef_switch_imageloader.isChecked
val folderMode = ef_switch_folder_mode.isChecked
val includeVideo = ef_switch_include_video.isChecked
val onlyVideo = ef_switch_only_video.isChecked
val isExclude = ef_switch_include_exclude.isChecked
val returnAfterCapture = binding.switchReturnAfterCapture.isChecked
val isSingleMode = binding.switchSingle.isChecked
val useCustomImageLoader = binding.switchImageloader.isChecked
val folderMode = binding.switchFolderMode.isChecked
val includeVideo = binding.switchIncludeVideo.isChecked
val onlyVideo = binding.switchOnlyVideo.isChecked
val isExclude = binding.switchIncludeExclude.isChecked

ImagePickerComponentsHolder.setInternalComponent(object : DefaultImagePickerComponents(this) {
override val imageLoader: ImageLoader
Expand Down Expand Up @@ -92,14 +107,8 @@ class MainActivity : AppCompatActivity() {
startActivityForResult(intent, IpCons.RC_IMAGE_PICKER)
}

private val startImagePicker = registerImagePicker {
images.clear()
images.addAll(it)
printImages(images)
}

private fun start() {
startImagePicker(createConfig())
imagePickerLauncher.launch(createConfig())
}

private fun startCustomUI() {
Expand All @@ -120,8 +129,8 @@ class MainActivity : AppCompatActivity() {

private fun printImages(images: List<Image>?) {
if (images == null) return
text_view.text = images.joinToString("\n")
text_view.setOnClickListener {
binding.textView.text = images.joinToString("\n")
binding.textView.setOnClickListener {
ImageViewerActivity.start(this@MainActivity, images)
}
}
Expand Down
48 changes: 24 additions & 24 deletions sample/src/main/java/com/esafirm/sample/MainFragment.kt
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
package com.esafirm.sample

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
import com.esafirm.imagepicker.features.ImagePickerConfig
import com.esafirm.imagepicker.features.ImagePickerMode
import com.esafirm.imagepicker.features.ReturnMode
import com.esafirm.imagepicker.features.registerImagePicker
import kotlinx.android.synthetic.main.fragment_main.*
import com.esafirm.sample.databinding.FragmentMainBinding

class MainFragment : Fragment() {
class MainFragment : Fragment(R.layout.fragment_main) {

private val startImagePicker = registerImagePicker {
private lateinit var binding: FragmentMainBinding

private val imagePickerLauncher = registerImagePicker {
val firstImage = it.firstOrNull() ?: return@registerImagePicker
Glide.with(img_fragment)
Glide.with(binding.imgFragment)
.load(firstImage.uri)
.into(img_fragment)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_main, container, false)
.into(binding.imgFragment)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

button_pick_fragment.setOnClickListener {
startImagePicker(ImagePickerConfig {
mode = ImagePickerMode.SINGLE
returnMode = ReturnMode.ALL // set whether pick action or camera action should return immediate result or not. Only works in single mode for image picker
isFolderMode = true // set folder mode (false by default)
folderTitle = "Folder" // folder selection title
imageTitle = "Tap to select" // image selection title
doneButtonText = "DONE" // done button text
})
binding = FragmentMainBinding.bind(view)

binding.buttonPickFragment.setOnClickListener {
imagePickerLauncher.launch(
ImagePickerConfig {
mode = ImagePickerMode.SINGLE
returnMode = ReturnMode.ALL // set whether pick action or camera action should return immediate result or not. Only works in single mode for image picker
isFolderMode = true // set folder mode (false by default)
folderTitle = "Folder" // folder selection title
imageTitle = "Tap to select" // image selection title
doneButtonText = "DONE" // done button text
}
)
}

button_close.setOnClickListener {
fragmentManager?.beginTransaction()
?.remove(this@MainFragment)
?.commitAllowingStateLoss()
binding.buttonClose.setOnClickListener {
parentFragmentManager.beginTransaction()
.remove(this@MainFragment)
.commitAllowingStateLoss()
}
}
}

0 comments on commit a09344f

Please sign in to comment.