diff options
author | Allan Wang <me@allanwang.ca> | 2017-07-23 23:26:34 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-23 23:26:34 -0700 |
commit | 50ad7f0ae89fc52ce57fe03328f4221fb57f2eac (patch) | |
tree | 69ead8807bb7371428953a0363519343f03f9b5b | |
parent | 4706b8f6a8d08a6961da6ab34d15881b63356d79 (diff) | |
download | kau-50ad7f0ae89fc52ce57fe03328f4221fb57f2eac.tar.gz kau-50ad7f0ae89fc52ce57fe03328f4221fb57f2eac.tar.bz2 kau-50ad7f0ae89fc52ce57fe03328f4221fb57f2eac.zip |
Fully implement imagepicker and create play store showcase (#12)3.1.1
* Update changelog
* Add uri to imagemodel
* Revamp image pickers
* Prepare play store showcase
* Add encrypted files
* Test showcase
* Clean elastic recycler activity
60 files changed, 742 insertions, 243 deletions
diff --git a/.travis.yml b/.travis.yml index fb97b18..1830df2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,10 @@ android: - extra-android-m2repository - extra-google-m2repository licenses: - - '.+' -script: ./gradlew lintRelease test + - ".+" +script: +- ./gradlew --quiet androidGitVersion +- if [[ "$TRAVIS_BRANCH" == "master" ]]; then ./gradlew lintRelease publishRelease; else ./gradlew lintRelease test; fi branches: except: - gh-pages @@ -26,6 +28,9 @@ notifications: sudo: false cache: directories: - - $HOME/.m2 + - "$HOME/.m2" before_script: -- chmod +x gradlew
\ No newline at end of file +- chmod +x gradlew +before_install: +- openssl aes-256-cbc -K $encrypted_12e8842891a3_key -iv $encrypted_12e8842891a3_iv + -in files/kau.tar.enc -out kau.tar -d @@ -90,7 +90,8 @@ dependencies { ## [Image Picker](imagepicker#readme) * WIP - Overlaying media chooser * Includes `:core-ui`, -[`Glide`](https://github.com/bumptech/glide) +[`Glide`](https://github.com/bumptech/glide), +[`Blurry`](https://github.com/wasabeef/Blurry) ## [Kpref Activity](kpref-activity#readme) * Fully programmatic implementation of a Preference Activity, backed by RecyclerViews diff --git a/adapter/src/main/kotlin/ca/allanwang/kau/animators/KauAnimator.kt b/adapter/src/main/kotlin/ca/allanwang/kau/animators/KauAnimator.kt index 5b36551..9e52aac 100644 --- a/adapter/src/main/kotlin/ca/allanwang/kau/animators/KauAnimator.kt +++ b/adapter/src/main/kotlin/ca/allanwang/kau/animators/KauAnimator.kt @@ -8,13 +8,13 @@ import ca.allanwang.kau.utils.KAU_RIGHT /** * Created by Allan Wang on 2017-06-27. */ -class KauAnimator( +open class KauAnimator( val addAnimator: KauAnimatorAdd = SlideAnimatorAdd(KAU_BOTTOM), val removeAnimator: KauAnimatorRemove = SlideAnimatorRemove(KAU_RIGHT), val changeAnimator: KauAnimatorChange = FadeAnimatorChange() ) : BaseItemAnimator() { - fun startDelay(holder: RecyclerView.ViewHolder, duration: Long, factor: Float) + open fun startDelay(holder: RecyclerView.ViewHolder, duration: Long, factor: Float) = Math.max(0L, (holder.adapterPosition * duration * factor).toLong()) override fun removeAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator { diff --git a/build.gradle b/build.gradle index bd440a8..72d35aa 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,4 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. -import groovy.xml.MarkupBuilder - buildscript { repositories { jcenter() @@ -10,6 +8,7 @@ buildscript { classpath 'com.android.tools.build:gradle:2.3.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${KOTLIN}" classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + classpath 'com.github.triplet.gradle:play-publisher:1.2.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/core-ui/src/main/kotlin/ca/allanwang/kau/ui/views/MeasuredImageView.kt b/core-ui/src/main/kotlin/ca/allanwang/kau/ui/views/MeasuredImageView.kt index ebb6397..5db5eaa 100644 --- a/core-ui/src/main/kotlin/ca/allanwang/kau/ui/views/MeasuredImageView.kt +++ b/core-ui/src/main/kotlin/ca/allanwang/kau/ui/views/MeasuredImageView.kt @@ -3,7 +3,6 @@ package ca.allanwang.kau.ui.views import android.content.Context import android.support.v7.widget.AppCompatImageView import android.util.AttributeSet -import android.widget.ImageView /** * Created by Allan Wang on 2017-07-14. diff --git a/core-ui/src/main/kotlin/ca/allanwang/kau/ui/widgets/ElasticDragDismissFrameLayout.kt b/core-ui/src/main/kotlin/ca/allanwang/kau/ui/widgets/ElasticDragDismissFrameLayout.kt index 995ccab..5cdfc92 100644 --- a/core-ui/src/main/kotlin/ca/allanwang/kau/ui/widgets/ElasticDragDismissFrameLayout.kt +++ b/core-ui/src/main/kotlin/ca/allanwang/kau/ui/widgets/ElasticDragDismissFrameLayout.kt @@ -21,9 +21,12 @@ import android.content.Context import android.graphics.Color import android.os.Build import android.support.annotation.RequiresApi +import android.support.v7.widget.RecyclerView +import android.transition.TransitionInflater import android.util.AttributeSet import android.view.View import android.widget.FrameLayout +import ca.allanwang.kau.logging.KL import ca.allanwang.kau.ui.R import ca.allanwang.kau.utils.* @@ -237,4 +240,15 @@ class ElasticDragDismissFrameLayout @JvmOverloads constructor( } } + fun addExitListener(activity: Activity, transitionBottom: Int = R.transition.kau_exit_slide_bottom, transitionTop: Int = R.transition.kau_exit_slide_top) { + addListener(object : ElasticDragDismissFrameLayout.SystemChromeFader(activity) { + override fun onDragDismissed() { + KL.d("New transition") + activity.window.returnTransition = TransitionInflater.from(activity) + .inflateTransition(if (translationY > 0) transitionBottom else transitionTop) + activity.finishAfterTransition() + } + }) + } + } diff --git a/core-ui/src/main/res-public/values/styles.xml b/core-ui/src/main/res-public/values/styles.xml index 583ede7..31c5b74 100644 --- a/core-ui/src/main/res-public/values/styles.xml +++ b/core-ui/src/main/res-public/values/styles.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="no"?> <resources> + <style name="Kau.Translucent"> <item name="android:windowBackground">@color/kau_shadow_overlay</item> <item name="android:colorBackgroundCacheHint">@null</item> @@ -13,6 +14,8 @@ <item name="android:windowAnimationStyle">@null</item> </style> + <!--Transitions are only v21+--> + <style name="Kau.Translucent.SlideBottom" /> <style name="Kau.Translucent.SlideTop" /> diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt index 54aeaff..5631e70 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt @@ -2,11 +2,11 @@ package ca.allanwang.kau.utils import android.annotation.SuppressLint import android.app.Activity +import android.app.ActivityOptions import android.content.Intent import android.graphics.Color -import android.os.Build +import android.os.Bundle import android.support.annotation.ColorInt -import android.support.annotation.RequiresApi import android.support.annotation.StringRes import android.support.design.widget.Snackbar import android.view.Menu @@ -20,6 +20,27 @@ import org.jetbrains.anko.contentView */ /** + * Helper class to launch an activity for result + * Counterpart of [Activity.startActivityForResult] + * For starting activities without result, see [startActivity] + */ +@SuppressLint("NewApi") +fun Activity.startActivityForResult( + clazz: Class<out Activity>, + requestCode: Int, + transition: Boolean = false, + bundle: Bundle? = null, + intentBuilder: Intent.() -> Unit = {}) { + val intent = Intent(this, clazz) + val fullBundle = if (transition && buildIsLollipopAndUp) + ActivityOptions.makeSceneTransitionAnimation(this).toBundle() + else Bundle() + if (bundle != null) fullBundle.putAll(bundle) + intent.intentBuilder() + startActivityForResult(intent, requestCode, if (fullBundle.isEmpty) null else fullBundle) +} + +/** * Restarts an activity from itself with a fade animation * Keeps its existing extra bundles and has a builder to accept other parameters */ diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt index 7a92665..2219b5d 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt @@ -26,6 +26,12 @@ import com.afollestad.materialdialogs.MaterialDialog /** * Created by Allan Wang on 2017-06-03. */ + +/** + * Helper class to launch an activity from a context + * Counterpart of [ContextCompat.startActivity] + * For starting activities for results, see [startActivityForResult] + */ @SuppressLint("NewApi") fun Context.startActivity( clazz: Class<out Activity>, @@ -33,14 +39,14 @@ fun Context.startActivity( transition: Boolean = false, bundle: Bundle? = null, intentBuilder: Intent.() -> Unit = {}) { - val intent = (Intent(this, clazz)) + val intent = Intent(this, clazz) if (clearStack) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) - intent.intentBuilder() val fullBundle = if (transition && this is Activity && buildIsLollipopAndUp) ActivityOptions.makeSceneTransitionAnimation(this).toBundle() else Bundle() if (transition && this !is Activity) KL.d("Cannot make scene transition when context is not an instance of an Activity") if (bundle != null) fullBundle.putAll(bundle) + intent.intentBuilder() ContextCompat.startActivity(this, intent, if (fullBundle.isEmpty) null else fullBundle) if (this is Activity && clearStack) finish() } diff --git a/docs/Changelog.md b/docs/Changelog.md index 4a913b5..99bfb50 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,7 +1,14 @@ # Changelog -## v3.1 +## v3.1.1 +* :imagepicker: Add uri val to ImageModel + +## v3.1.0 * :core: Allow for nullable throwables when logging +* :core: Remove some extra DSL annotations +* :kpref-activity: Bring down to minSdk 19 and fix compatibility +* :adapter: Update readme for iitems and animators +* :about: Move strings to private ## v3.0 * :core: Add setPadding[x] diff --git a/files/.gitignore b/files/.gitignore new file mode 100644 index 0000000..e9bf9f6 --- /dev/null +++ b/files/.gitignore @@ -0,0 +1,4 @@ +gplay-keys.json +kau.keystore +kau.properties +update-dev.sh diff --git a/files/images/kau_banner.png b/files/images/kau_banner.png Binary files differnew file mode 100644 index 0000000..d59b043 --- /dev/null +++ b/files/images/kau_banner.png diff --git a/files/images/kau_round.svg b/files/images/kau_round.svg new file mode 100644 index 0000000..e8892e6 --- /dev/null +++ b/files/images/kau_round.svg @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="160mm" + height="160mm" + viewBox="0 0 566.92911 566.92912" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="kau_round.svg" + inkscape:export-filename="C:\Users\User7681\Downloads\kau_round_72.png" + inkscape:export-xdpi="11.43" + inkscape:export-ydpi="11.43"> + <defs + id="defs4"> + <filter + style="color-interpolation-filters:sRGB" + inkscape:label="Drop Shadow" + id="filter4312"> + <feFlood + flood-opacity="0.498039" + flood-color="rgb(0,0,0)" + result="flood" + id="feFlood4314" /> + <feComposite + in="flood" + in2="SourceGraphic" + operator="in" + result="composite1" + id="feComposite4316" /> + <feGaussianBlur + in="composite1" + stdDeviation="3.3" + result="blur" + id="feGaussianBlur4318" /> + <feOffset + dx="5" + dy="5" + result="offset" + id="feOffset4320" /> + <feComposite + in="SourceGraphic" + in2="offset" + operator="over" + result="composite2" + id="feComposite4322" /> + </filter> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.49497475" + inkscape:cx="335.78333" + inkscape:cy="368.84238" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + showguides="false" + inkscape:window-width="1536" + inkscape:window-height="841" + inkscape:window-x="-8" + inkscape:window-y="-8" + inkscape:window-maximized="1" + fit-margin-top="9" + fit-margin-left="20" + fit-margin-right="20" + fit-margin-bottom="20" + borderlayer="false" + inkscape:showpageshadow="true" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-103.41957,-202.20087)"> + <g + id="g4324" + transform="translate(-4.5444214,0)"> + <circle + r="217.14285" + cy="481.12094" + cx="391.42856" + id="path4136" + style="opacity:1;fill:#ff8900;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="circle4211" + d="m 274.76172,306.12695 0,357.85157 a 217.14285,217.14285 0 0 0 116.66601,34.28515 217.14285,217.14285 0 0 0 137.13672,-48.94531 L 274.76172,306.12695 Z" + style="opacity:1;fill:#0086dc;fill-opacity:0.8627451;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="circle4206" + d="m 391.42773,263.97852 a 217.14285,217.14285 0 0 0 -116.66601,34.04687 l 0,358.08984 253.79101,-343.17578 a 217.14285,217.14285 0 0 0 -137.125,-48.96093 z" + style="opacity:1;fill:#00a9ff;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter4312)" /> + </g> + </g> +</svg> diff --git a/files/images/kau_round_512.png b/files/images/kau_round_512.png Binary files differnew file mode 100644 index 0000000..0fa7c94 --- /dev/null +++ b/files/images/kau_round_512.png diff --git a/files/logo.svg b/files/images/logo.svg index 4f5a27b..4f5a27b 100644 --- a/files/logo.svg +++ b/files/images/logo.svg diff --git a/files/kau.tar.enc b/files/kau.tar.enc Binary files differnew file mode 100644 index 0000000..4d39f9f --- /dev/null +++ b/files/kau.tar.enc diff --git a/files/travis.sh b/files/travis.sh new file mode 100644 index 0000000..9df9ad9 --- /dev/null +++ b/files/travis.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# Add appropriate files for encryption + +rm kau.tar.enc +cd .. +tar cvf kau.tar files/gplay-keys.json files/kau.keystore files/kau.properties +travis encrypt-file kau.tar --add +rm kau.tar +mv kau.tar.enc files/
\ No newline at end of file diff --git a/imagepicker/README.md b/imagepicker/README.md index e37e417..f70de2b 100644 --- a/imagepicker/README.md +++ b/imagepicker/README.md @@ -1,12 +1,35 @@ # KAU :imagepicker ImagePicker is a beautiful gallery activity that allows you to pick images -from your storage. It is backed by FastAdapter and Glide, and offers blur and fade transitions. +from your storage. It is backed by FastAdapter and Glide, and stems from the ImagePickerCore model -`ImagePickerActivityBase` is already fully functional, so you may directly extend it with no further changes -and add the activity to your manifest +Currently, there are two options: -You may also easily launch the activity through the simple binder: +-------------------------------- + +## ImagePickerActivityBase + +A full screen multi image picker with beautiful animations. +Images are blurred when selected, and there is a counter on the top right. +There is a FAB to send back the response. + +`R.style.Kau.ImagePicker` is added for your convenience. + +## ImagePickerActivityOverlayBase + +This overlaying activity makes use of transitions and nested scrolling, and is only for Lollipop and up. +Only one image can be selected, so the overlay exists immediately upon the first selection. +Having this model also means that each item is only one simple image, as opposed to the blurrable image view above. +As a result, this activity has faster loading on scrolling. + +`R.style.Kau.ImagePicker.Overlay` is added for your convenience. + +-------------------------------- + +Both activities work out of the box and can be extended without needing further modifications. +Their convenience styles default to a slide in slide out animation from the bottom edge. + +You may also easily launch either activity through the simple binder: ``` Activity.kauLaunchImagePicker(YourClass::class.java, yourRequestCode) ``` @@ -14,6 +37,5 @@ Activity.kauLaunchImagePicker(YourClass::class.java, yourRequestCode) Note that this launches the activity through a `startActivityForResult` call You may get the activity response by overriding your `onActivityResult` method -to first verify that the request code matches and then call `kauOnImagePickerResult` - -This module also has a template style `Kau.ImagePicker` that defaults to a slide up animation.
\ No newline at end of file +to first verify that the request code matches and then call `kauOnImagePickerResult`, +which will return the list of ImageModels.
\ No newline at end of file diff --git a/imagepicker/build.gradle b/imagepicker/build.gradle index a31fac0..93f52ac 100644 --- a/imagepicker/build.gradle +++ b/imagepicker/build.gradle @@ -4,7 +4,7 @@ apply from: '../android-lib.gradle' dependencies { - compile project(':adapter') + compile project(':core-ui') compile "com.github.bumptech.glide:glide:${GLIDE}" kapt "com.github.bumptech.glide:compiler:${GLIDE}" 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 2bfc57f..ffeb560 100644 --- a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageItem.kt +++ b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageItem.kt @@ -1,22 +1,16 @@ package ca.allanwang.kau.imagepicker -import android.content.Context -import android.graphics.Color import android.graphics.drawable.Drawable import android.support.v7.widget.RecyclerView import android.view.View import ca.allanwang.kau.iitems.KauIItem import ca.allanwang.kau.utils.bindView -import ca.allanwang.kau.utils.gone import com.bumptech.glide.Glide import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target import com.mikepenz.fastadapter.FastAdapter -import com.mikepenz.google_material_typeface_library.GoogleMaterial -import com.mikepenz.iconics.IconicsDrawable -import com.mikepenz.iconics.typeface.IIcon /** * Created by Allan Wang on 2017-07-04. @@ -25,7 +19,6 @@ class ImageItem(val data: ImageModel) : KauIItem<ImageItem, ImageItem.ViewHolder>(R.layout.kau_iitem_image, { ViewHolder(it) }) { private var failedToLoad = false - var withFade = true companion object { fun bindEvents(fastAdapter: FastAdapter<ImageItem>) { @@ -45,50 +38,28 @@ class ImageItem(val data: ImageModel) override fun bindView(holder: ViewHolder, payloads: List<Any>?) { super.bindView(holder, payloads) - if (withFade) holder.container.alpha = 0f Glide.with(holder.itemView) .load(data.data) .listener(object : RequestListener<Drawable> { override fun onLoadFailed(e: GlideException?, model: Any, target: Target<Drawable>, isFirstResource: Boolean): Boolean { - failedToLoad = true; - holder.container.setIcon(GoogleMaterial.Icon.gmd_error); - if (withFade) holder.container.animate().alpha(1f).start(); + failedToLoad = true + holder.container.imageBase.setImageDrawable(ImagePickerCore.getErrorDrawable(holder.itemView.context)) return true; } override fun onResourceReady(resource: Drawable, model: Any, target: Target<Drawable>, dataSource: DataSource, isFirstResource: Boolean): Boolean { holder.container.imageBase.setImageDrawable(resource) if (isSelected) holder.container.blurInstantly() - if (withFade) holder.container.animate().alpha(1f).start(); return true; } }) .into(holder.container.imageBase) } - private fun BlurredImageView.setIcon(icon: IIcon) { - val sizePx = computeViewSize(context) - imageBase.setImageDrawable(IconicsDrawable(context, icon) - .sizePx(sizePx) - .paddingPx(sizePx / 3) - .color(Color.WHITE)) - imageBase.setBackgroundColor(ImagePickerActivityBase.accentColor) - imageForeground.gone() - } - - private fun computeViewSize(context: Context): Int { - val screenWidthPx = context.resources.displayMetrics.widthPixels - return screenWidthPx / ImagePickerActivityBase.computeColumnCount(context) - } - override fun unbindView(holder: ViewHolder) { super.unbindView(holder) - if (!failedToLoad) { - Glide.with(holder.itemView).clear(holder.container.imageBase) - holder.container.removeBlurInstantly() - } else { - holder.container.fullReset() - } + Glide.with(holder.itemView).clear(holder.container.imageBase) + holder.container.removeBlurInstantly() failedToLoad = false } diff --git a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageItemBasic.kt b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageItemBasic.kt new file mode 100644 index 0000000..62a0b09 --- /dev/null +++ b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageItemBasic.kt @@ -0,0 +1,71 @@ +package ca.allanwang.kau.imagepicker + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Intent +import android.graphics.drawable.Drawable +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.RecyclerView +import android.view.View +import ca.allanwang.kau.iitems.KauIItem +import ca.allanwang.kau.ui.views.MeasuredImageView +import ca.allanwang.kau.utils.bindView +import ca.allanwang.kau.utils.buildIsLollipopAndUp +import com.bumptech.glide.Glide +import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.Target +import com.mikepenz.fastadapter.FastAdapter + +/** + * Created by Allan Wang on 2017-07-04. + */ +class ImageItemBasic(val data: ImageModel) + : KauIItem<ImageItem, ImageItemBasic.ViewHolder>(R.layout.kau_iitem_image_basic, { ViewHolder(it) }) { + + companion object { + @SuppressLint("NewApi") + fun bindEvents(activity: Activity, fastAdapter: FastAdapter<ImageItemBasic>) { + fastAdapter.withSelectable(false) + //add image data and return right away + .withOnClickListener { v, _, item, _ -> + val intent = Intent() + val data = arrayListOf(item.data) + intent.putParcelableArrayListExtra(IMAGE_PICKER_RESULT, data) + activity.setResult(AppCompatActivity.RESULT_OK, intent) + if (buildIsLollipopAndUp) activity.finishAfterTransition() + else activity.finish() + true + } + } + } + + override fun isSelectable(): Boolean = false + + override fun bindView(holder: ViewHolder, payloads: List<Any>?) { + super.bindView(holder, payloads) + Glide.with(holder.itemView) + .load(data.data) + .listener(object : RequestListener<Drawable> { + override fun onLoadFailed(e: GlideException?, model: Any, target: Target<Drawable>, isFirstResource: Boolean): Boolean { + holder.image.setImageDrawable(ImagePickerCore.getErrorDrawable(holder.itemView.context)) + return true; + } + + override fun onResourceReady(resource: Drawable, model: Any, target: Target<Drawable>, dataSource: DataSource, isFirstResource: Boolean): Boolean { + return false + } + }) + .into(holder.image) + } + + override fun unbindView(holder: ViewHolder) { + super.unbindView(holder) + Glide.with(holder.itemView).clear(holder.image) + } + + class ViewHolder(v: View) : RecyclerView.ViewHolder(v) { + val image: MeasuredImageView by bindView(R.id.kau_image) + } +}
\ No newline at end of file diff --git a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageModel.kt b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageModel.kt index d744650..202805c 100644 --- a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageModel.kt +++ b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImageModel.kt @@ -1,10 +1,12 @@ package ca.allanwang.kau.imagepicker import android.database.Cursor +import android.net.Uri import android.os.Parcel import android.os.Parcelable import android.provider.MediaStore import android.support.annotation.NonNull +import java.io.File /** @@ -34,6 +36,8 @@ data class ImageModel(val size: Long, val dateModified: Long, val data: String, parcel.writeString(this.displayName) } + val uri: Uri by lazy { Uri.fromFile(File(data)) } + override fun describeContents(): Int { return 0 } diff --git a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivityBase.kt b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivityBase.kt index 9d988d1..3a9809c 100644 --- a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivityBase.kt +++ b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivityBase.kt @@ -1,40 +1,27 @@ 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 + * Images are blurred when selected, and multiple images can be selected at a time. + * Having three layered images makes this slightly slower than [ImagePickerActivityOverlayBase] */ -abstract class ImagePickerActivityBase : AppCompatActivity(), LoaderManager.LoaderCallbacks<Cursor> { - - val imageAdapter = FastItemAdapter<ImageItem>() +abstract class ImagePickerActivityBase : ImagePickerCore<ImageItem>() { val coordinator: CoordinatorLayout by bindView(R.id.kau_coordinator) val toolbar: Toolbar by bindView(R.id.kau_toolbar) @@ -42,28 +29,6 @@ abstract class ImagePickerActivityBase : AppCompatActivity(), LoaderManager.Load 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<ImageModel> { - 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) @@ -79,12 +44,7 @@ abstract class ImagePickerActivityBase : AppCompatActivity(), LoaderManager.Load } toolbar.setNavigationOnClickListener { onBackPressed() } - recycler.apply { - layoutManager = GridLayoutManager(context, computeColumnCount(context)) - adapter = imageAdapter - setHasFixedSize(true) - itemAnimator = KauAnimator(FadeScaleAnimatorAdd(0.8f)) - } + initializeRecycler(recycler) ImageItem.bindEvents(imageAdapter) imageAdapter.withSelectionListener({ _, _ -> selectionCount.text = imageAdapter.selections.size.toString() }) @@ -110,24 +70,7 @@ abstract class ImagePickerActivityBase : AppCompatActivity(), LoaderManager.Load 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) - } - } - } + override fun converter(model: ImageModel): ImageItem = ImageItem(model) /** * Decide whether the toolbar can hide itself @@ -145,56 +88,13 @@ abstract class ImagePickerActivityBase : AppCompatActivity(), LoaderManager.Load params.scrollFlags = 0 } - override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { - 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<Cursor>, 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()) + override fun onLoadFinished(loader: Loader<Cursor>?, data: Cursor?) { + super.onLoadFinished(loader, data) 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 onStatusChange(loaded: Boolean) { + setToolbarScrollable(loaded) } - override fun onLoaderReset(loader: Loader<Cursor>) = 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/ImagePickerActivityOverlayBase.kt b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivityOverlayBase.kt new file mode 100644 index 0000000..ed8eee4 --- /dev/null +++ b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerActivityOverlayBase.kt @@ -0,0 +1,51 @@ +package ca.allanwang.kau.imagepicker + +import android.os.Build +import android.os.Bundle +import android.support.annotation.RequiresApi +import android.support.v7.widget.RecyclerView +import ca.allanwang.kau.ui.widgets.ElasticDragDismissFrameLayout +import ca.allanwang.kau.utils.bindView +import ca.allanwang.kau.utils.toast + +/** + * Created by Allan Wang on 2017-07-23. + * + * Base activity for selecting images from storage + * This variant is an overlay and selects one image only before returning directly + * It is more efficient than [ImagePickerActivityBase], as all images are one layer deep + * as opposed to three layers deep + */ +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +abstract class ImagePickerActivityOverlayBase : ImagePickerCore<ImageItemBasic>() { + + val draggable: ElasticDragDismissFrameLayout by bindView(R.id.kau_draggable) + val recycler: RecyclerView by bindView(R.id.kau_recyclerview) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.kau_activity_image_picker_overlay) + initializeRecycler(recycler) + ImageItemBasic.bindEvents(this, imageAdapter) + + draggable.addExitListener(this, R.transition.kau_image_exit_bottom, R.transition.kau_image_exit_top) + draggable.setOnClickListener { finishAfterTransition() } + + loadImages() + } + + override fun finishAfterTransition() { + recycler.stopScroll() + super.finishAfterTransition() + } + + override fun onStatusChange(loaded: Boolean) { + if (!loaded) toast(R.string.kau_no_images_loaded) + } + + override fun converter(model: ImageModel): ImageItemBasic = ImageItemBasic(model) + + override fun onBackPressed() { + finishAfterTransition() + } +}
\ 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 8e8a69c..db8d113 100644 --- a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerBinder.kt +++ b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerBinder.kt @@ -2,6 +2,7 @@ package ca.allanwang.kau.imagepicker import android.app.Activity import android.content.Intent +import ca.allanwang.kau.utils.startActivityForResult /** * Created by Allan Wang on 2017-07-21. @@ -11,10 +12,11 @@ import android.content.Intent */ /** - * Image picker launcher + * Image picker launchers */ -fun Activity.kauLaunchImagePicker(clazz: Class<out ImagePickerActivityBase>, requestCode: Int) { - startActivityForResult(Intent(this, clazz), requestCode) +fun Activity.kauLaunchImagePicker(clazz: Class<out ImagePickerCore<*>>, requestCode: Int) { +// startActivityForResult(clazz, requestCode, true) + startActivityForResult(clazz, requestCode, transition = ImagePickerActivityOverlayBase::class.java.isAssignableFrom(clazz)) } /** @@ -22,7 +24,7 @@ fun Activity.kauLaunchImagePicker(clazz: Class<out ImagePickerActivityBase>, req * call under [Activity.onActivityResult] * and make sure that the requestCode matches first */ -fun Activity.kauOnImagePickerResult(resultCode: Int, data: Intent?) = ImagePickerActivityBase.onImagePickerResult(resultCode, data) +fun Activity.kauOnImagePickerResult(resultCode: Int, data: Intent?) = ImagePickerCore.onImagePickerResult(resultCode, data) internal const val LOADER_ID = 42 internal const val IMAGE_PICKER_RESULT = "image_picker_result" diff --git a/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerCore.kt b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerCore.kt new file mode 100644 index 0000000..f197a5e --- /dev/null +++ b/imagepicker/src/main/kotlin/ca/allanwang/kau/imagepicker/ImagePickerCore.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.graphics.Color +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.provider.MediaStore +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.RecyclerView +import ca.allanwang.kau.animators.FadeScaleAnimatorAdd +import ca.allanwang.kau.animators.KauAnimator +import ca.allanwang.kau.permissions.kauRequestPermissions +import ca.allanwang.kau.utils.dimenPixelSize +import ca.allanwang.kau.utils.toast +import com.mikepenz.fastadapter.IItem +import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter +import com.mikepenz.google_material_typeface_library.GoogleMaterial +import com.mikepenz.iconics.IconicsDrawable + +/** + * Created by Allan Wang on 2017-07-23. + * + * Container for the main logic behind the image pickers + */ +abstract class ImagePickerCore<T : IItem<*, *>> : AppCompatActivity(), LoaderManager.LoaderCallbacks<Cursor> { + + 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 + } + + /** + * Compute our resulting image size + */ + fun computeViewSize(context: Context): Int { + val screenWidthPx = context.resources.displayMetrics.widthPixels + return screenWidthPx / computeColumnCount(context) + } + + /** + * Create error tile for a given item + */ + fun getErrorDrawable(context: Context): Drawable { + val sizePx = ImagePickerCore.computeViewSize(context) + return IconicsDrawable(context, GoogleMaterial.Icon.gmd_error) + .sizePx(sizePx) + .backgroundColor(accentColor) + .paddingPx(sizePx / 3) + .color(Color.WHITE) + } + + var accentColor: Int = 0xff666666.toInt() + + /** + * Helper method to retrieve the images from our iamge picker + * This is used for both single and multiple photo picks + */ + fun onImagePickerResult(resultCode: Int, data: Intent?): List<ImageModel> { + if (resultCode != Activity.RESULT_OK || data == null || !data.hasExtra(IMAGE_PICKER_RESULT)) + return emptyList() + return data.getParcelableArrayListExtra(IMAGE_PICKER_RESULT) + } + + /** + * Number of loaded images we should cache + * This is arbitrary + */ + const val CACHE_SIZE = 80 + + /** + * We know that Glide takes a while to initially fetch the images + * My as well make it look pretty + */ + const val INITIAL_LOAD_DELAY = 600L + } + + val imageAdapter: FastItemAdapter<T> = FastItemAdapter() + + /** + * Further improve preloading by extending the layout space + */ + val extraSpace: Int by lazy { resources.displayMetrics.heightPixels } + + fun initializeRecycler(recycler: RecyclerView) { + recycler.apply { + val manager = object : GridLayoutManager(context, computeColumnCount(context)) { + override fun getExtraLayoutSpace(state: RecyclerView.State?): Int { + return extraSpace + } + } + setItemViewCacheSize(CACHE_SIZE) + isDrawingCacheEnabled = true + layoutManager = manager + adapter = imageAdapter + setHasFixedSize(true) + itemAnimator = object : KauAnimator(FadeScaleAnimatorAdd(0.8f)) { + override fun startDelay(holder: RecyclerView.ViewHolder, duration: Long, factor: Float): Long { + return super.startDelay(holder, duration, factor) + INITIAL_LOAD_DELAY + } + } + } + } + + //Sort by descending date + var sortQuery = MediaStore.Images.Media.DATE_MODIFIED + " DESC" + + /** + * 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 + */ + open fun loadImages() { + kauRequestPermissions(Manifest.permission.READ_EXTERNAL_STORAGE) { + granted, _ -> + if (granted) { + supportLoaderManager.initLoader(LOADER_ID, null, this) + onStatusChange(true) + } else { + toast(R.string.kau_permission_denied) + onStatusChange(false) + } + } + } + + override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { + 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, sortQuery) + } + + override fun onLoadFinished(loader: Loader<Cursor>?, data: Cursor?) { + reset() + if (data == null || !data.moveToFirst()) { + toast(R.string.kau_no_images_found) + onStatusChange(false) + return + } + val items = mutableListOf<T>() + do { + val model = ImageModel(data) + if (!shouldLoad(model)) continue + items.add(converter(model)) + } while (data.moveToNext()) + addItems(items) + } + + abstract fun converter(model: ImageModel): T + + override fun onLoaderReset(loader: Loader<Cursor>?) = reset() + + /** + * Called at the end of [onLoadFinished] + * when the adapter should add the items + */ + open fun addItems(items: List<T>) { + imageAdapter.add(items) + } + + /** + * Clears the adapter to prepare for a new load + */ + open fun reset() { + imageAdapter.clear() + } + + /** + * 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 + + open fun onStatusChange(loaded: Boolean) {} + +}
\ No newline at end of file diff --git a/imagepicker/src/main/res-public/values-v21/styles.xml b/imagepicker/src/main/res-public/values-v21/styles.xml new file mode 100644 index 0000000..bae68da --- /dev/null +++ b/imagepicker/src/main/res-public/values-v21/styles.xml @@ -0,0 +1,9 @@ +<resources> + + <style name="Kau.ImagePicker.Overlay" parent="Kau.Translucent"> + <item name="android:statusBarColor">@android:color/transparent</item> + <item name="android:windowEnterTransition">@transition/kau_image_enter</item> + <item name="android:windowReturnTransition">@transition/kau_image_exit_bottom</item> + </style> + +</resources> diff --git a/imagepicker/src/main/res-public/values/public.xml b/imagepicker/src/main/res-public/values/public.xml index 3a1d9c5..a892651 100644 --- a/imagepicker/src/main/res-public/values/public.xml +++ b/imagepicker/src/main/res-public/values/public.xml @@ -3,4 +3,5 @@ <public name='kau_blurred_image_selection_overlay' type='color' /> <public name='kau_image_minimum_size' type='dimen' /> <public name='Kau.ImagePicker' type='style' /> + <public name='Kau.ImagePicker.Overlay' type='style' /> </resources>
\ 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 index 4d4a135..99f294a 100644 --- a/imagepicker/src/main/res-public/values/styles.xml +++ b/imagepicker/src/main/res-public/values/styles.xml @@ -1,7 +1,12 @@ - <resources> +<resources> <style name="Kau.ImagePicker"> <item name="android:windowAnimationStyle">@style/KauSlideInSlideOutBottom</item> </style> + <!--Just as a placeholder for public.xml--> + <style name="Kau.ImagePicker.Overlay" parent="Kau.Translucent"> + <item name="android:windowAnimationStyle">@null</item> + </style> + </resources> diff --git a/imagepicker/src/main/res/layout-v21/kau_activity_image_picker_overlay.xml b/imagepicker/src/main/res/layout-v21/kau_activity_image_picker_overlay.xml new file mode 100644 index 0000000..a0ce301 --- /dev/null +++ b/imagepicker/src/main/res/layout-v21/kau_activity_image_picker_overlay.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<ca.allanwang.kau.ui.widgets.ElasticDragDismissFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/kau_draggable" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:dragDismissDistance="@dimen/kau_drag_dismiss_distance_large" + app:dragDismissScale="0.95"> + + <android.support.v7.widget.RecyclerView + android:id="@+id/kau_recyclerview" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="@dimen/kau_drag_dismiss_distance" + android:background="?android:colorBackground" /> + +</ca.allanwang.kau.ui.widgets.ElasticDragDismissFrameLayout>
\ No newline at end of file diff --git a/imagepicker/src/main/res/layout/kau_activity_image_picker.xml b/imagepicker/src/main/res/layout/kau_activity_image_picker.xml index 5b0300d..6d991b0 100644 --- a/imagepicker/src/main/res/layout/kau_activity_image_picker.xml +++ b/imagepicker/src/main/res/layout/kau_activity_image_picker.xml @@ -4,6 +4,7 @@ android:id="@+id/kau_coordinator" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="?android:colorBackground" android:fitsSystemWindows="true"> <android.support.design.widget.AppBarLayout diff --git a/imagepicker/src/main/res/layout/kau_iitem_image_basic.xml b/imagepicker/src/main/res/layout/kau_iitem_image_basic.xml new file mode 100644 index 0000000..b89e41d --- /dev/null +++ b/imagepicker/src/main/res/layout/kau_iitem_image_basic.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<ca.allanwang.kau.ui.views.MeasuredImageView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/kau_image" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="2dp" + android:foreground="@drawable/kau_selectable_white" + android:scaleType="centerCrop" + app:relativeHeight="1" />
\ No newline at end of file diff --git a/imagepicker/src/main/res/transition-v21/kau_image_enter.xml b/imagepicker/src/main/res/transition-v21/kau_image_enter.xml new file mode 100644 index 0000000..447c0c9 --- /dev/null +++ b/imagepicker/src/main/res/transition-v21/kau_image_enter.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@android:interpolator/fast_out_linear_in" + android:transitionOrdering="together"> + + <slide + android:duration="400" + android:slideEdge="bottom"> + <targets android:targetId="@id/kau_draggable" /> + </slide> + + <fade + android:duration="200" + android:startDelay="200" /> + +</transitionSet> diff --git a/imagepicker/src/main/res/transition-v21/kau_image_exit_bottom.xml b/imagepicker/src/main/res/transition-v21/kau_image_exit_bottom.xml new file mode 100644 index 0000000..447c0c9 --- /dev/null +++ b/imagepicker/src/main/res/transition-v21/kau_image_exit_bottom.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@android:interpolator/fast_out_linear_in" + android:transitionOrdering="together"> + + <slide + android:duration="400" + android:slideEdge="bottom"> + <targets android:targetId="@id/kau_draggable" /> + </slide> + + <fade + android:duration="200" + android:startDelay="200" /> + +</transitionSet> diff --git a/imagepicker/src/main/res/transition-v21/kau_image_exit_top.xml b/imagepicker/src/main/res/transition-v21/kau_image_exit_top.xml new file mode 100644 index 0000000..8d64f48 --- /dev/null +++ b/imagepicker/src/main/res/transition-v21/kau_image_exit_top.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@android:interpolator/fast_out_linear_in" + android:transitionOrdering="together"> + + <slide + android:duration="400" + android:slideEdge="top"> + <targets android:targetId="@id/kau_draggable" /> + </slide> + + <fade + android:duration="200" + android:startDelay="200" /> + +</transitionSet> diff --git a/imagepicker/src/main/res/values/strings.xml b/imagepicker/src/main/res/values/strings.xml index 7aa7f3e..17af1d8 100644 --- a/imagepicker/src/main/res/values/strings.xml +++ b/imagepicker/src/main/res/values/strings.xml @@ -3,4 +3,5 @@ <string name="kau_no_images_found">No images found</string> <string name="kau_no_images_selected">No images have been selected</string> <string name="kau_blurrable_imageview">Blurrable ImageView</string> + <string name="kau_no_images_loaded">No images loaded</string> </resources>
\ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index b318174..f1a0f10 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -3,6 +3,7 @@ plugins { } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'com.github.triplet.play' repositories { jcenter() @@ -11,6 +12,14 @@ repositories { maven { url "https://maven.google.com" } } +play { + jsonFile = file('../files/gplay-keys.json') + track = 'beta' + errorOnSizeLimit = true + uploadImages = false + untrackOld = true +} + android { compileSdkVersion Integer.parseInt(project.TARGET_SDK) buildToolsVersion project.BUILD_TOOLS @@ -28,9 +37,24 @@ android { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } + signingConfigs { + + def releaseProps = new Properties() + file("../files/kau.properties").withInputStream { releaseProps.load(it) } + + release { + storeFile file("../files/kau.keystore") + storePassword releaseProps.getProperty('storePassword') + keyAlias releaseProps.getProperty('keyAlias') + keyPassword releaseProps.getProperty('keyPassword') + } + + } + buildTypes { release { minifyEnabled true + signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index da26bdf..85fb199 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -10,12 +10,12 @@ android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" + android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" - android:label="@string/title_activity_main" + android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar.Transparent"> <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -32,6 +32,9 @@ android:name=".ImagePickerActivity" android:theme="@style/Kau.ImagePicker" /> <activity + android:name=".ImagePickerActivityOverlay" + android:theme="@style/Kau.ImagePicker.Overlay" /> + <activity android:name=".AdapterActivity" android:theme="@style/Kau.Translucent.SlideBottom" /> </application> diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/AnimActivity.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/AnimActivity.kt index abc65b9..abf44d8 100644 --- a/sample/src/main/kotlin/ca/allanwang/kau/sample/AnimActivity.kt +++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/AnimActivity.kt @@ -7,13 +7,14 @@ import ca.allanwang.kau.permissions.PERMISSION_ACCESS_COARSE_LOCATION import ca.allanwang.kau.permissions.PERMISSION_ACCESS_FINE_LOCATION import ca.allanwang.kau.permissions.kauOnRequestPermissionsResult import ca.allanwang.kau.permissions.kauRequestPermissions -import ca.allanwang.kau.swipe.SWIPE_EDGE_BOTTOM +import ca.allanwang.kau.swipe.SWIPE_EDGE_LEFT import ca.allanwang.kau.swipe.kauSwipeOnCreate import ca.allanwang.kau.swipe.kauSwipeOnDestroy import ca.allanwang.kau.swipe.kauSwipeOnPostCreate import ca.allanwang.kau.utils.fullLinearRecycler import ca.allanwang.kau.utils.startActivitySlideOut import ca.allanwang.kau.utils.toast +import ca.allanwang.kau.utils.withAlpha import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter /** @@ -27,63 +28,10 @@ class AnimActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val adapter = FastItemAdapter<PermissionCheckbox>() - setContentView(fullLinearRecycler(adapter)) + setContentView(fullLinearRecycler(adapter).apply { setBackgroundColor(KPrefSample.bgColor.withAlpha(255)) }) + adapter.add(listOf( PERMISSION_ACCESS_COARSE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, - PERMISSION_ACCESS_FINE_LOCATION, PERMISSION_ACCESS_FINE_LOCATION ).map { PermissionCheckbox(it) }) adapter.withOnClickListener { _, _, item, _ -> @@ -96,7 +44,7 @@ class AnimActivity : AppCompatActivity() { true } kauSwipeOnCreate { - edgeFlag = SWIPE_EDGE_BOTTOM + edgeFlag = SWIPE_EDGE_LEFT } } diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/ImagePicker.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/ImagePicker.kt new file mode 100644 index 0000000..c7f28bc --- /dev/null +++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/ImagePicker.kt @@ -0,0 +1,11 @@ +package ca.allanwang.kau.sample + +import ca.allanwang.kau.imagepicker.ImagePickerActivityBase +import ca.allanwang.kau.imagepicker.ImagePickerActivityOverlayBase + +/** + * Created by Allan Wang on 2017-07-23. + */ +class ImagePickerActivity : ImagePickerActivityBase() + +class ImagePickerActivityOverlay : ImagePickerActivityOverlayBase()
\ No newline at end of file diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/ImagePickerActivity.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/ImagePickerActivity.kt deleted file mode 100644 index 8a23192..0000000 --- a/sample/src/main/kotlin/ca/allanwang/kau/sample/ImagePickerActivity.kt +++ /dev/null @@ -1,8 +0,0 @@ -package ca.allanwang.kau.sample - -import ca.allanwang.kau.imagepicker.ImagePickerActivityBase - -/** - * Created by Allan Wang on 2017-07-23. - */ -class ImagePickerActivity : ImagePickerActivityBase()
\ No newline at end of file diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt index ecfef04..7ce10af 100644 --- a/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt +++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt @@ -168,6 +168,10 @@ class MainActivity : KPrefActivity() { onClick = { _, _, _ -> kauLaunchImagePicker(ImagePickerActivity::class.java, REQUEST_IMAGE); false } } + plainText(R.string.gallery_overlay_showcase) { + onClick = { _, _, _ -> kauLaunchImagePicker(ImagePickerActivityOverlay::class.java, REQUEST_IMAGE); false } + } + plainText(R.string.adapter_showcase) { onClick = { _, _, _ -> startActivity(AdapterActivity::class.java, transition = true); false } } diff --git a/sample/src/main/play/contactEmail b/sample/src/main/play/contactEmail new file mode 100644 index 0000000..3e03392 --- /dev/null +++ b/sample/src/main/play/contactEmail @@ -0,0 +1 @@ +pitchedapps@gmail.com
\ No newline at end of file diff --git a/sample/src/main/play/defaultLanguage b/sample/src/main/play/defaultLanguage new file mode 100644 index 0000000..ffdd217 --- /dev/null +++ b/sample/src/main/play/defaultLanguage @@ -0,0 +1 @@ +en-CA
\ No newline at end of file diff --git a/sample/src/main/play/en-CA/listing/fulldescription b/sample/src/main/play/en-CA/listing/fulldescription new file mode 100644 index 0000000..2d9fe89 --- /dev/null +++ b/sample/src/main/play/en-CA/listing/fulldescription @@ -0,0 +1,3 @@ +This app aims to demonstrate the features available in <a href="https://allanwang.github.io/KAU/">KAU</a>, or Kotlin Android Utils. + +The project is open sourced on <a href="https://github.com/AllanWang/KAU">github</a>
\ No newline at end of file diff --git a/sample/src/main/play/en-CA/listing/shortdescription b/sample/src/main/play/en-CA/listing/shortdescription new file mode 100644 index 0000000..9424ee8 --- /dev/null +++ b/sample/src/main/play/en-CA/listing/shortdescription @@ -0,0 +1 @@ +A simple showcase for Kotlin Android Utils
\ No newline at end of file diff --git a/sample/src/main/play/en-CA/listing/title b/sample/src/main/play/en-CA/listing/title new file mode 100644 index 0000000..c3c5422 --- /dev/null +++ b/sample/src/main/play/en-CA/listing/title @@ -0,0 +1 @@ +KAU - Library Showcase
\ No newline at end of file diff --git a/sample/src/main/play/en-CA/whatsnew b/sample/src/main/play/en-CA/whatsnew new file mode 100644 index 0000000..1d15ce2 --- /dev/null +++ b/sample/src/main/play/en-CA/whatsnew @@ -0,0 +1 @@ +A full changelog is available in the app
\ No newline at end of file diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher.png b/sample/src/main/res/mipmap-hdpi/ic_launcher.png Binary files differindex cde69bc..9b9a9ef 100644 --- a/sample/src/main/res/mipmap-hdpi/ic_launcher.png +++ b/sample/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png Binary files differdeleted file mode 100644 index 9a078e3..0000000 --- a/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png +++ /dev/null diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher.png b/sample/src/main/res/mipmap-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index c133a0c..0000000 --- a/sample/src/main/res/mipmap-mdpi/ic_launcher.png +++ /dev/null diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png Binary files differdeleted file mode 100644 index efc028a..0000000 --- a/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png +++ /dev/null diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png Binary files differindex bfa42f0..18b65ef 100644 --- a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png +++ b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png Binary files differdeleted file mode 100644 index 3af2608..0000000 --- a/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png +++ /dev/null diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary files differindex 324e72c..14f2082 100644 --- a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png +++ b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png Binary files differdeleted file mode 100644 index 9bec2e6..0000000 --- a/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +++ /dev/null diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png Binary files differindex aee44e1..fe0aa98 100644 --- a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png +++ b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png Binary files differdeleted file mode 100644 index 34947cd..0000000 --- a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +++ /dev/null diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml index f3880b2..c64b623 100644 --- a/sample/src/main/res/values/strings.xml +++ b/sample/src/main/res/values/strings.xml @@ -1,6 +1,5 @@ <resources> - <string name="app_name">KPrefs</string> - <string name="title_activity_main">MainActivity</string> + <string name="app_name">KAU</string> <string name="header">This is a header</string> <string name="desc">This is a description</string> <string name="checkbox_1">Checkbox 1</string> @@ -21,6 +20,7 @@ <string name="your_email">your.email@here.com</string> <string name="your_subject">Your subject</string> <string name="gallery_showcase">Gallery Showcase</string> + <string name="gallery_overlay_showcase">Gallery Overlay Showcase</string> <string name="adapter_showcase">Adapter Showcase</string> <string name="about_kau">KAU (Kotlin Android Utils) is a collection of common extension functions and complex UIs that can be used in almost all apps. It is meant to implement the shared components, so you can focus on what makes your app unique.</string> </resources> diff --git a/sample/src/main/res/xml/kau_changelog.xml b/sample/src/main/res/xml/kau_changelog.xml index 76aee36..7ed2073 100644 --- a/sample/src/main/res/xml/kau_changelog.xml +++ b/sample/src/main/res/xml/kau_changelog.xml @@ -6,15 +6,22 @@ <item text="" /> --> - <version title="v3.1"/> - <item text=":core: Allow for nullable throwables when logging" /> - <item text="" /> - <item text="" /> + <version title="v3.1.1"/> + <item text=":imagepicker: Add uri val to ImageModel" /> + <item text=":imagepicker: Create bindings and overlay activity" /> + <item text="Add showcase app to play store" /> <item text="" /> <item text="" /> <item text="" /> <item text="" /> + <version title="v3.1.0"/> + <item text=":core: Allow for nullable throwables when logging" /> + <item text=":core: Remove some extra DSL annotations" /> + <item text=":kpref-activity: Bring down to minSdk 19 and fix compatibility" /> + <item text=":adapter: Update readme for iitems and animators" /> + <item text=":about: Move strings to private" /> + <version title="v3.0"/> <item text=":core: Add setPadding[x]" /> <item text=":core: [breaking] Replace update[x]Margin to setMargin[x]" /> |