From 4706b8f6a8d08a6961da6ab34d15881b63356d79 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Sun, 23 Jul 2017 13:13:36 -0700 Subject: Update kpref-activity's min-sdk and other minor changes (#11) * Move some resources to public * Lower kpref minsdk * Remove excess kauUtils annotations * Allow nullable throwable * Do not throw null throwable * Make image picker base abstract again * Migrate about strings to private * Update readme * Update readme * Update sample tagging * Update adapter readme --- .../ca/allanwang/kau/imagepicker/ImageItem.kt | 4 +- .../kau/imagepicker/ImagePickerActivity.kt | 200 --------------------- .../kau/imagepicker/ImagePickerActivityBase.kt | 200 +++++++++++++++++++++ .../allanwang/kau/imagepicker/ImagePickerBinder.kt | 6 +- imagepicker/src/main/res-public/values/colors.xml | 6 + imagepicker/src/main/res-public/values/dimens.xml | 6 + imagepicker/src/main/res-public/values/public.xml | 4 +- imagepicker/src/main/res-public/values/styles.xml | 7 + imagepicker/src/main/res/values/colors.xml | 6 - imagepicker/src/main/res/values/dimens.xml | 6 - imagepicker/src/main/res/values/styles.xml | 7 - 11 files changed, 226 insertions(+), 226 deletions(-) delete mode 100644 imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivity.kt create mode 100644 imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivityBase.kt create mode 100644 imagepicker/src/main/res-public/values/colors.xml create mode 100644 imagepicker/src/main/res-public/values/dimens.xml create mode 100644 imagepicker/src/main/res-public/values/styles.xml delete mode 100644 imagepicker/src/main/res/values/colors.xml delete mode 100644 imagepicker/src/main/res/values/dimens.xml delete mode 100644 imagepicker/src/main/res/values/styles.xml (limited to 'imagepicker/src') diff --git a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageItem.kt b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageItem.kt index d258822..2bfc57f 100644 --- a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageItem.kt +++ b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageItem.kt @@ -72,13 +72,13 @@ class ImageItem(val data: ImageModel) .sizePx(sizePx) .paddingPx(sizePx / 3) .color(Color.WHITE)) - imageBase.setBackgroundColor(ImagePickerActivity.accentColor) + imageBase.setBackgroundColor(ImagePickerActivityBase.accentColor) imageForeground.gone() } private fun computeViewSize(context: Context): Int { val screenWidthPx = context.resources.displayMetrics.widthPixels - return screenWidthPx / ImagePickerActivity.computeColumnCount(context) + return screenWidthPx / ImagePickerActivityBase.computeColumnCount(context) } override fun unbindView(holder: ViewHolder) { diff --git a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivity.kt b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivity.kt deleted file mode 100644 index 814cde4..0000000 --- a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivity.kt +++ /dev/null @@ -1,200 +0,0 @@ -package ca.allanwang.kau.imagepicker - -import android.Manifest -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.database.Cursor -import android.os.Bundle -import android.provider.MediaStore -import android.support.design.widget.AppBarLayout -import android.support.design.widget.CoordinatorLayout -import android.support.design.widget.FloatingActionButton -import android.support.v4.app.LoaderManager -import android.support.v4.content.CursorLoader -import android.support.v4.content.Loader -import android.support.v7.app.AppCompatActivity -import android.support.v7.widget.GridLayoutManager -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.RecyclerView -import android.support.v7.widget.Toolbar -import android.widget.TextView -import ca.allanwang.kau.animators.FadeScaleAnimatorAdd -import ca.allanwang.kau.animators.KauAnimator -import ca.allanwang.kau.permissions.kauRequestPermissions -import ca.allanwang.kau.utils.* -import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter -import com.mikepenz.google_material_typeface_library.GoogleMaterial - - -/** - * Created by Allan Wang on 2017-07-04. - * - * Base activity for selecting images from storage - */ -open class ImagePickerActivity : AppCompatActivity(), LoaderManager.LoaderCallbacks { - - val imageAdapter = FastItemAdapter() - - val coordinator: CoordinatorLayout by bindView(R.id.kau_coordinator) - val toolbar: Toolbar by bindView(R.id.kau_toolbar) - val selectionCount: TextView by bindView(R.id.kau_selection_count) - val recycler: RecyclerView by bindView(R.id.kau_recyclerview) - val fab: FloatingActionButton by bindView(R.id.kau_fab) - - companion object { - /** - * Given the dimensions of our device and a minimum image size, - * Computer the optimal column count for our grid layout - * - * @return column count - */ - fun computeColumnCount(context: Context): Int { - val minImageSizePx = context.dimenPixelSize(R.dimen.kau_image_minimum_size) - val screenWidthPx = context.resources.displayMetrics.widthPixels - return screenWidthPx / minImageSizePx - } - - var accentColor: Int = 0xff666666.toInt() - - fun onImagePickerResult(resultCode: Int, data: Intent?): List { - if (resultCode != Activity.RESULT_OK || data == null || !data.hasExtra(IMAGE_PICKER_RESULT)) - return emptyList() - return data.getParcelableArrayListExtra(IMAGE_PICKER_RESULT) - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - setContentView(R.layout.kau_activity_image_picker) - - selectionCount.setCompoundDrawables(null, null, GoogleMaterial.Icon.gmd_image.toDrawable(this, 18), null) - - setSupportActionBar(toolbar) - supportActionBar?.apply { - setDisplayHomeAsUpEnabled(true) - setDisplayShowHomeEnabled(true) - setHomeAsUpIndicator(GoogleMaterial.Icon.gmd_close.toDrawable(this@ImagePickerActivity, 18)) - } - toolbar.setNavigationOnClickListener { onBackPressed() } - - recycler.apply { - layoutManager = GridLayoutManager(context, computeColumnCount(context)) - adapter = imageAdapter - setHasFixedSize(true) - itemAnimator = KauAnimator(FadeScaleAnimatorAdd(0.8f)) - } - - ImageItem.bindEvents(imageAdapter) - imageAdapter.withSelectionListener({ _, _ -> selectionCount.text = imageAdapter.selections.size.toString() }) - - fab.apply { - show() - setIcon(GoogleMaterial.Icon.gmd_send) - setOnClickListener { - val selection = imageAdapter.selectedItems - if (selection.isEmpty()) { - toast(R.string.kau_no_images_selected) - } else { - val intent = Intent() - val data = ArrayList(selection.map { it.data }) - intent.putParcelableArrayListExtra(IMAGE_PICKER_RESULT, data) - setResult(RESULT_OK, intent) - finish() - } - } - hideOnDownwardsScroll(recycler) - } - - loadImages() - } - - /** - * Request read permissions and load all external images - * The result will be filtered through {@link #onLoadFinished(Loader, Cursor)} - * Call this to make sure that we request permissions each time - * The adapter will be cleared on each successful call - */ - private fun loadImages() { - kauRequestPermissions(Manifest.permission.READ_EXTERNAL_STORAGE) { - granted, _ -> - if (granted) { - supportLoaderManager.initLoader(LOADER_ID, null, this) - setToolbarScrollable(true) - } else { - toast(R.string.kau_permission_denied) - setToolbarScrollable(false) - } - } - } - - /** - * Decide whether the toolbar can hide itself - * We typically want this behaviour unless we don't have enough images - * to fill the entire screen. In that case we don't want the recyclerview to be scrollable - * which means the toolbar shouldn't scroll either - - * @param scrollable true if scroll flags are enabled, false otherwise - */ - private fun setToolbarScrollable(scrollable: Boolean) { - val params = toolbar.layoutParams as AppBarLayout.LayoutParams - if (scrollable) - params.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS or AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL - else - params.scrollFlags = 0 - } - - override fun onCreateLoader(id: Int, args: Bundle?): Loader { - val columns = arrayOf( - MediaStore.Images.Media._ID, - MediaStore.Images.Media.TITLE, - MediaStore.Images.Media.DATA, - MediaStore.Images.Media.SIZE, - MediaStore.Images.Media.DISPLAY_NAME, - MediaStore.Images.Media.DATE_MODIFIED - ) - return CursorLoader(this, - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - columns, - null, - null, - //Sort by descending date - MediaStore.Images.Media.DATE_MODIFIED + " DESC") - } - - override fun onLoadFinished(loader: Loader, data: Cursor?) { - reset() - if (data == null || !data.moveToFirst()) { - toast(R.string.kau_no_images_found) - setToolbarScrollable(false) - return - } - do { - val model = ImageModel(data) - if (!shouldLoad(model)) continue - imageAdapter.add(ImageItem(model)) - } while (data.moveToNext()) - setToolbarScrollable((recycler.layoutManager as LinearLayoutManager).findLastCompletelyVisibleItemPosition() < imageAdapter.getItemCount() - 1) - } - - /** - * Optional filter to decide which images get displayed - * Defaults to checking their sizes to filter out - * very small images such as lurking drawables/icons - * - * Returns true if model should be displayed, false otherwise - */ - open fun shouldLoad(model: ImageModel): Boolean = model.size > 10000L - - private fun reset() { - imageAdapter.clear(); - } - - override fun onLoaderReset(loader: Loader) = reset() - - override fun onBackPressed() { - setResult(RESULT_CANCELED) - super.onBackPressed() - } -} \ No newline at end of file diff --git a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivityBase.kt b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivityBase.kt new file mode 100644 index 0000000..9d988d1 --- /dev/null +++ b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivityBase.kt @@ -0,0 +1,200 @@ +package ca.allanwang.kau.imagepicker + +import android.Manifest +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.database.Cursor +import android.os.Bundle +import android.provider.MediaStore +import android.support.design.widget.AppBarLayout +import android.support.design.widget.CoordinatorLayout +import android.support.design.widget.FloatingActionButton +import android.support.v4.app.LoaderManager +import android.support.v4.content.CursorLoader +import android.support.v4.content.Loader +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.GridLayoutManager +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.support.v7.widget.Toolbar +import android.widget.TextView +import ca.allanwang.kau.animators.FadeScaleAnimatorAdd +import ca.allanwang.kau.animators.KauAnimator +import ca.allanwang.kau.permissions.kauRequestPermissions +import ca.allanwang.kau.utils.* +import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter +import com.mikepenz.google_material_typeface_library.GoogleMaterial + + +/** + * Created by Allan Wang on 2017-07-04. + * + * Base activity for selecting images from storage + */ +abstract class ImagePickerActivityBase : AppCompatActivity(), LoaderManager.LoaderCallbacks { + + val imageAdapter = FastItemAdapter() + + val coordinator: CoordinatorLayout by bindView(R.id.kau_coordinator) + val toolbar: Toolbar by bindView(R.id.kau_toolbar) + val selectionCount: TextView by bindView(R.id.kau_selection_count) + val recycler: RecyclerView by bindView(R.id.kau_recyclerview) + val fab: FloatingActionButton by bindView(R.id.kau_fab) + + companion object { + /** + * Given the dimensions of our device and a minimum image size, + * Computer the optimal column count for our grid layout + * + * @return column count + */ + fun computeColumnCount(context: Context): Int { + val minImageSizePx = context.dimenPixelSize(R.dimen.kau_image_minimum_size) + val screenWidthPx = context.resources.displayMetrics.widthPixels + return screenWidthPx / minImageSizePx + } + + var accentColor: Int = 0xff666666.toInt() + + fun onImagePickerResult(resultCode: Int, data: Intent?): List { + if (resultCode != Activity.RESULT_OK || data == null || !data.hasExtra(IMAGE_PICKER_RESULT)) + return emptyList() + return data.getParcelableArrayListExtra(IMAGE_PICKER_RESULT) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.kau_activity_image_picker) + + selectionCount.setCompoundDrawables(null, null, GoogleMaterial.Icon.gmd_image.toDrawable(this, 18), null) + + setSupportActionBar(toolbar) + supportActionBar?.apply { + setDisplayHomeAsUpEnabled(true) + setDisplayShowHomeEnabled(true) + setHomeAsUpIndicator(GoogleMaterial.Icon.gmd_close.toDrawable(this@ImagePickerActivityBase, 18)) + } + toolbar.setNavigationOnClickListener { onBackPressed() } + + recycler.apply { + layoutManager = GridLayoutManager(context, computeColumnCount(context)) + adapter = imageAdapter + setHasFixedSize(true) + itemAnimator = KauAnimator(FadeScaleAnimatorAdd(0.8f)) + } + + ImageItem.bindEvents(imageAdapter) + imageAdapter.withSelectionListener({ _, _ -> selectionCount.text = imageAdapter.selections.size.toString() }) + + fab.apply { + show() + setIcon(GoogleMaterial.Icon.gmd_send) + setOnClickListener { + val selection = imageAdapter.selectedItems + if (selection.isEmpty()) { + toast(R.string.kau_no_images_selected) + } else { + val intent = Intent() + val data = ArrayList(selection.map { it.data }) + intent.putParcelableArrayListExtra(IMAGE_PICKER_RESULT, data) + setResult(RESULT_OK, intent) + finish() + } + } + hideOnDownwardsScroll(recycler) + } + + loadImages() + } + + /** + * Request read permissions and load all external images + * The result will be filtered through {@link #onLoadFinished(Loader, Cursor)} + * Call this to make sure that we request permissions each time + * The adapter will be cleared on each successful call + */ + private fun loadImages() { + kauRequestPermissions(Manifest.permission.READ_EXTERNAL_STORAGE) { + granted, _ -> + if (granted) { + supportLoaderManager.initLoader(LOADER_ID, null, this) + setToolbarScrollable(true) + } else { + toast(R.string.kau_permission_denied) + setToolbarScrollable(false) + } + } + } + + /** + * Decide whether the toolbar can hide itself + * We typically want this behaviour unless we don't have enough images + * to fill the entire screen. In that case we don't want the recyclerview to be scrollable + * which means the toolbar shouldn't scroll either + + * @param scrollable true if scroll flags are enabled, false otherwise + */ + private fun setToolbarScrollable(scrollable: Boolean) { + val params = toolbar.layoutParams as AppBarLayout.LayoutParams + if (scrollable) + params.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS or AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL + else + params.scrollFlags = 0 + } + + override fun onCreateLoader(id: Int, args: Bundle?): Loader { + val columns = arrayOf( + MediaStore.Images.Media._ID, + MediaStore.Images.Media.TITLE, + MediaStore.Images.Media.DATA, + MediaStore.Images.Media.SIZE, + MediaStore.Images.Media.DISPLAY_NAME, + MediaStore.Images.Media.DATE_MODIFIED + ) + return CursorLoader(this, + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + columns, + null, + null, + //Sort by descending date + MediaStore.Images.Media.DATE_MODIFIED + " DESC") + } + + override fun onLoadFinished(loader: Loader, data: Cursor?) { + reset() + if (data == null || !data.moveToFirst()) { + toast(R.string.kau_no_images_found) + setToolbarScrollable(false) + return + } + do { + val model = ImageModel(data) + if (!shouldLoad(model)) continue + imageAdapter.add(ImageItem(model)) + } while (data.moveToNext()) + setToolbarScrollable((recycler.layoutManager as LinearLayoutManager).findLastCompletelyVisibleItemPosition() < imageAdapter.getItemCount() - 1) + } + + /** + * Optional filter to decide which images get displayed + * Defaults to checking their sizes to filter out + * very small images such as lurking drawables/icons + * + * Returns true if model should be displayed, false otherwise + */ + open fun shouldLoad(model: ImageModel): Boolean = model.size > 10000L + + private fun reset() { + imageAdapter.clear(); + } + + override fun onLoaderReset(loader: Loader) = reset() + + override fun onBackPressed() { + setResult(RESULT_CANCELED) + super.onBackPressed() + } +} \ No newline at end of file diff --git a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerBinder.kt b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerBinder.kt index 9e63464..8e8a69c 100644 --- a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerBinder.kt +++ b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerBinder.kt @@ -13,18 +13,16 @@ import android.content.Intent /** * Image picker launcher */ -fun Activity.kauLaunchImagePicker(clazz: Class, requestCode: Int) { +fun Activity.kauLaunchImagePicker(clazz: Class, requestCode: Int) { startActivityForResult(Intent(this, clazz), requestCode) } -fun Activity.kauLaunchImagePicker(requestCode: Int) = kauLaunchImagePicker(ImagePickerActivity::class.java, requestCode) - /** * Image picker result * call under [Activity.onActivityResult] * and make sure that the requestCode matches first */ -fun Activity.kauOnImagePickerResult(resultCode: Int, data: Intent?) = ImagePickerActivity.onImagePickerResult(resultCode, data) +fun Activity.kauOnImagePickerResult(resultCode: Int, data: Intent?) = ImagePickerActivityBase.onImagePickerResult(resultCode, data) internal const val LOADER_ID = 42 internal const val IMAGE_PICKER_RESULT = "image_picker_result" diff --git a/imagepicker/src/main/res-public/values/colors.xml b/imagepicker/src/main/res-public/values/colors.xml new file mode 100644 index 0000000..ebaa3f7 --- /dev/null +++ b/imagepicker/src/main/res-public/values/colors.xml @@ -0,0 +1,6 @@ + + + + #30000000 + + \ No newline at end of file diff --git a/imagepicker/src/main/res-public/values/dimens.xml b/imagepicker/src/main/res-public/values/dimens.xml new file mode 100644 index 0000000..3ff2dd7 --- /dev/null +++ b/imagepicker/src/main/res-public/values/dimens.xml @@ -0,0 +1,6 @@ + + + + 120dp + + \ No newline at end of file diff --git a/imagepicker/src/main/res-public/values/public.xml b/imagepicker/src/main/res-public/values/public.xml index cf14680..3a1d9c5 100644 --- a/imagepicker/src/main/res-public/values/public.xml +++ b/imagepicker/src/main/res-public/values/public.xml @@ -1,4 +1,6 @@ - + + + \ No newline at end of file diff --git a/imagepicker/src/main/res-public/values/styles.xml b/imagepicker/src/main/res-public/values/styles.xml new file mode 100644 index 0000000..4d4a135 --- /dev/null +++ b/imagepicker/src/main/res-public/values/styles.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/imagepicker/src/main/res/values/colors.xml b/imagepicker/src/main/res/values/colors.xml deleted file mode 100644 index ebaa3f7..0000000 --- a/imagepicker/src/main/res/values/colors.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - #30000000 - - \ No newline at end of file diff --git a/imagepicker/src/main/res/values/dimens.xml b/imagepicker/src/main/res/values/dimens.xml deleted file mode 100644 index 3ff2dd7..0000000 --- a/imagepicker/src/main/res/values/dimens.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - 120dp - - \ No newline at end of file diff --git a/imagepicker/src/main/res/values/styles.xml b/imagepicker/src/main/res/values/styles.xml deleted file mode 100644 index 0d9ce64..0000000 --- a/imagepicker/src/main/res/values/styles.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - -- cgit v1.2.3