diff options
author | Allan Wang <me@allanwang.ca> | 2017-07-03 20:09:35 -0700 |
---|---|---|
committer | Allan Wang <me@allanwang.ca> | 2017-07-03 20:09:35 -0700 |
commit | 139f2dd8207d3a9cd67157a3e3754a9982c7f69d (patch) | |
tree | 548f23ff4f6ddce0a24e740fc550c75ad997fc29 /library/src/main/kotlin | |
parent | b88a8834dc3be12a37856e9e2584eee7ef52c22e (diff) | |
download | kau-139f2dd8207d3a9cd67157a3e3754a9982c7f69d.tar.gz kau-139f2dd8207d3a9cd67157a3e3754a9982c7f69d.tar.bz2 kau-139f2dd8207d3a9cd67157a3e3754a9982c7f69d.zip |
Initial creation of the Permission Manager
Diffstat (limited to 'library/src/main/kotlin')
6 files changed, 143 insertions, 6 deletions
diff --git a/library/src/main/kotlin/ca/allanwang/kau/iitems/KotlinIItem.kt b/library/src/main/kotlin/ca/allanwang/kau/iitems/KotlinIItem.kt new file mode 100644 index 0000000..7e4bda6 --- /dev/null +++ b/library/src/main/kotlin/ca/allanwang/kau/iitems/KotlinIItem.kt @@ -0,0 +1,23 @@ +package ca.allanwang.kau.iitems + +import android.support.annotation.LayoutRes +import android.support.v7.widget.RecyclerView +import android.view.View +import com.mikepenz.fastadapter.IClickable +import com.mikepenz.fastadapter.IItem +import com.mikepenz.fastadapter.items.AbstractItem + +/** + * Created by Allan Wang on 2017-07-03. + * + * Kotlin implementation of the [AbstractItem] to make things shorter + */ +open class KotlinIItem<Item, VH : RecyclerView.ViewHolder>( + private val type: Int, + @param:LayoutRes private val layoutRes: Int, + private val viewHolder: (v: View) -> VH +) : AbstractItem<Item, VH>() where Item : IItem<*, *>, Item : IClickable<*> { + override final fun getType(): Int = type + override final fun getViewHolder(v: View): VH = viewHolder(v) + override final fun getLayoutRes(): Int = layoutRes +}
\ No newline at end of file diff --git a/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt b/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt new file mode 100644 index 0000000..7181e2c --- /dev/null +++ b/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt @@ -0,0 +1,57 @@ +package ca.allanwang.kau.permissions + +import android.app.Activity +import android.content.Context +import android.support.v4.app.ActivityCompat +import ca.allanwang.kau.logging.KL +import ca.allanwang.kau.utils.KauException +import ca.allanwang.kau.utils.buildIsMarshmallowAndUp +import ca.allanwang.kau.utils.hasPermission +import java.lang.ref.WeakReference + +/** + * Created by Allan Wang on 2017-07-03. + */ +internal object PermissionManager { + + var requestInProgress = false + val pendingResults: MutableList<WeakReference<PermissionResult>> by lazy { mutableListOf<WeakReference<PermissionResult>>() } + + operator fun invoke(context: Context, permissions: Array<out String>, callback: (granted: Boolean, deniedPerm: String?) -> Unit) { + if (!buildIsMarshmallowAndUp) return callback(true, null) + val missingPermissions = permissions.filter { !context.hasPermission(it) } + if (missingPermissions.isEmpty()) return callback(true, null) + pendingResults.add(WeakReference(PermissionResult(permissions, callback = callback))) + if (!requestInProgress) { + requestInProgress = true + requestPermissions(context, missingPermissions.toTypedArray()) + } else KL.d("Request is postponed since another one is still in progress") + } + + @Synchronized internal fun requestPermissions(context: Context, permissions: Array<String>) { + val activity = (context as? Activity) ?: throw KauException("Context is not an instance of an activity; cannot request permissions") + ActivityCompat.requestPermissions(activity, permissions, 1) + } + + fun onRequestPermissionsResult(context: Context, permissions: Array<String>, grantResults: IntArray) { + val count = Math.min(permissions.size, grantResults.size) + val iter = pendingResults.iterator() + while (iter.hasNext()) { + val action = iter.next().get() + if ((0 until count).any { action?.onResult(permissions[it], grantResults[it]) ?: true }) + iter.remove() + } + if (pendingResults.isEmpty()) + requestInProgress = false + else { + val action = pendingResults.map { it.get() }.firstOrNull { it != null } + if (action == null) { //actions have been unlinked from their weak references + pendingResults.clear() + requestInProgress = false + return + } + requestPermissions(context, action.permissions.toTypedArray()) + } + } + +}
\ No newline at end of file diff --git a/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionResult.kt b/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionResult.kt new file mode 100644 index 0000000..14bfdff --- /dev/null +++ b/library/src/main/kotlin/ca/allanwang/kau/permissions/PermissionResult.kt @@ -0,0 +1,26 @@ +package ca.allanwang.kau.permissions + +import android.content.pm.PackageManager + +/** + * Created by Allan Wang on 2017-07-03. + */ +class PermissionResult(permissions: Array<out String>, val callback: (granted: Boolean, deniedPerm: String?) -> Unit) { + val permissions = mutableSetOf(*permissions) + + /** + * Called from the manager whenever a permission has changed + * Returns true if result is completed, false otherwise + */ + fun onResult(permission: String, result: Int): Boolean { + if (result != PackageManager.PERMISSION_GRANTED) { + callback(false, permission) + permissions.clear() + return true + } + permissions.remove(permission) + if (permissions.isNotEmpty()) return false + callback(true, null) + return true + } +}
\ No newline at end of file diff --git a/library/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt b/library/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt new file mode 100644 index 0000000..d466cb3 --- /dev/null +++ b/library/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt @@ -0,0 +1,26 @@ +package ca.allanwang.kau.permissions + +import android.app.Activity +import android.content.Context + +/** + * Created by Allan Wang on 2017-07-02. + * + * Bindings for the permission manager + */ + +/** + * Hook that should be added inside all [Activity.onRequestPermissionsResult] so that the Permission manager can handle the responses + */ +fun Activity.kauOnRequestPermissionsResult(permissions: Array<String>, grantResults: IntArray) + = PermissionManager.onRequestPermissionsResult(this, permissions, grantResults) + +/** + * Request a permission with a callback + * In reality, an activity is needed to fulfill the request, but a context is enough if those permissions are already granted + * To be safe, you may want to check that the context can be casted successfully first + * The [callback] returns [granted], which is true if all permissions are granted + * [deniedPerm] is the first denied permission, if granted is false + */ +fun Context.requestPermissions(vararg permissions: String, callback: (granted: Boolean, deniedPerm: String?) -> Unit) + = PermissionManager(this, permissions, callback)
\ No newline at end of file diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt index c56a09d..21021e2 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt @@ -4,6 +4,7 @@ import android.app.Activity import android.app.ActivityOptions import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.graphics.drawable.Drawable import android.net.ConnectivityManager import android.net.Uri @@ -131,13 +132,13 @@ fun Context.resolveString(@AttrRes attr: Int, fallback: String = ""): String { * Wrapper function for the MaterialDialog adapterBuilder * There is no need to call build() or show() as those are done by default */ -fun Context.materialDialog(action: MaterialDialog.Builder.() -> Unit): MaterialDialog { +inline fun Context.materialDialog(action: MaterialDialog.Builder.() -> Unit): MaterialDialog { val builder = MaterialDialog.Builder(this) builder.action() return builder.show() } -val Context.isNetworkAvailable: Boolean +inline val Context.isNetworkAvailable: Boolean get() { val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val activeNetworkInfo = connectivityManager.activeNetworkInfo @@ -146,17 +147,19 @@ val Context.isNetworkAvailable: Boolean fun Context.getDip(value: Float): Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, resources.displayMetrics) -val Context.isRtl: Boolean +inline val Context.isRtl: Boolean get() = resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL /** * Determine if the navigation bar will be on the bottom of the screen, based on logic in * PhoneWindowManager. */ -val Context.isNavBarOnBottom: Boolean +inline val Context.isNavBarOnBottom: Boolean get() { val cfg = resources.configuration val dm = resources.displayMetrics val canMove = dm.widthPixels != dm.heightPixels && cfg.smallestScreenWidthDp < 600 return !canMove || dm.widthPixels < dm.heightPixels - }
\ No newline at end of file + } + +fun Context.hasPermission(permissions: String) = !buildIsMarshmallowAndUp || ContextCompat.checkSelfPermission(this, permissions) == PackageManager.PERMISSION_GRANTED
\ No newline at end of file diff --git a/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt b/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt index aa0736e..84794f9 100644 --- a/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt +++ b/library/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt @@ -106,4 +106,6 @@ inline fun <T : AutoCloseable, R> T.use(block: (T) -> R): R { fun postDelayed(delay: Long, action: () -> Unit) { Handler().postDelayed(action, delay) -}
\ No newline at end of file +} + +class KauException(message: String) : RuntimeException(message)
\ No newline at end of file |