SubwayTooter-Android-App/anko/src/main/java/org/jetbrains/anko/internals/Internals.kt

317 lines
12 KiB
Kotlin

/*
* Copyright 2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("MatchingDeclarationName")
package org.jetbrains.anko.internals
import android.app.Activity
import android.app.Service
import android.app.UiModeManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.database.Cursor
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.util.AttributeSet
import android.view.ContextThemeWrapper
import android.view.View
import android.view.ViewGroup
import android.view.ViewManager
import org.jetbrains.anko.*
import java.io.Serializable
import java.util.*
object AnkoInternals {
const val NO_GETTER: String = "Property does not have a getter"
fun noGetter(): Nothing = throw AnkoException("Property does not have a getter")
private class AnkoContextThemeWrapper(base: Context?, val theme: Int) :
ContextThemeWrapper(base, theme)
fun <T : View> addView(manager: ViewManager, view: T) = when (manager) {
is ViewGroup -> manager.addView(view)
is AnkoContext<*> -> manager.addView(view, null)
else -> throw AnkoException("$manager is the wrong parent")
}
fun <T : View> addView(ctx: Context, view: T) {
ctx.UI { addView(this, view) }
}
fun <T : View> addView(activity: Activity, view: T) {
createAnkoContext(activity, { addView(this, view) }, true)
}
fun wrapContextIfNeeded(ctx: Context, theme: Int): Context {
return if (theme != 0 && (ctx !is AnkoContextThemeWrapper || ctx.theme != theme)) {
// If the context isn't a ContextThemeWrapper, or it is but does not have
// the same theme as we need, wrap it in a new wrapper
AnkoContextThemeWrapper(ctx, theme)
} else {
ctx
}
}
fun applyRecursively(v: View, style: (View) -> Unit) {
style(v)
if (v is ViewGroup) {
val maxIndex = v.childCount - 1
for (i in 0..maxIndex) {
v.getChildAt(i)?.let { applyRecursively(it, style) }
}
}
}
fun getContext(manager: ViewManager): Context = when (manager) {
is ViewGroup -> manager.context
is AnkoContext<*> -> manager.ctx
else -> throw AnkoException("$manager is the wrong parent")
}
inline fun <T> T.createAnkoContext(
ctx: Context,
init: AnkoContext<T>.() -> Unit,
setContentView: Boolean = false,
): AnkoContext<T> {
val dsl = AnkoContextImpl(ctx, this, setContentView)
dsl.init()
return dsl
}
// Some constants not present in Android SDK v.15
private object InternalConfiguration {
const val SCREENLAYOUT_LAYOUTDIR_MASK = 0xC0
const val SCREENLAYOUT_LAYOUTDIR_SHIFT = 6
const val SCREENLAYOUT_LAYOUTDIR_RTL = 0x02 shl SCREENLAYOUT_LAYOUTDIR_SHIFT
const val UI_MODE_TYPE_APPLIANCE = 0x05
const val UI_MODE_TYPE_WATCH = 0x06
}
@JvmStatic
fun <T> createIntent(
ctx: Context,
clazz: Class<out T>,
params: Array<out Pair<String, Any?>>,
): Intent {
val intent = Intent(ctx, clazz)
if (params.isNotEmpty()) fillIntentArguments(intent, params)
return intent
}
@JvmStatic
fun internalStartActivity(
ctx: Context,
activity: Class<out Activity>,
params: Array<out Pair<String, Any?>>,
) {
ctx.startActivity(createIntent(ctx, activity, params))
}
@JvmStatic
fun internalStartActivityForResult(
act: Activity,
activity: Class<out Activity>,
requestCode: Int,
params: Array<out Pair<String, Any?>>,
) {
act.startActivityForResult(createIntent(act, activity, params), requestCode)
}
@JvmStatic
fun internalStartService(
ctx: Context,
service: Class<out Service>,
params: Array<out Pair<String, Any?>>,
): ComponentName? = ctx.startService(createIntent(ctx, service, params))
@JvmStatic
fun internalStopService(
ctx: Context,
service: Class<out Service>,
params: Array<out Pair<String, Any?>>,
): Boolean = ctx.stopService(createIntent(ctx, service, params))
@JvmStatic
private fun fillIntentArguments(intent: Intent, params: Array<out Pair<String, Any?>>) {
params.forEach {
when (val value = it.second) {
null -> intent.putExtra(it.first, null as Serializable?)
is Int -> intent.putExtra(it.first, value)
is Long -> intent.putExtra(it.first, value)
is String -> intent.putExtra(it.first, value)
is Float -> intent.putExtra(it.first, value)
is Double -> intent.putExtra(it.first, value)
is Char -> intent.putExtra(it.first, value)
is Short -> intent.putExtra(it.first, value)
is Boolean -> intent.putExtra(it.first, value)
is Bundle -> intent.putExtra(it.first, value)
is Array<*> -> when {
value.isArrayOf<CharSequence>() -> intent.putExtra(it.first, value)
value.isArrayOf<String>() -> intent.putExtra(it.first, value)
value.isArrayOf<Parcelable>() -> intent.putExtra(it.first, value)
else -> throw AnkoException("Intent extra ${it.first} has wrong type ${value.javaClass.name}")
}
is IntArray -> intent.putExtra(it.first, value)
is LongArray -> intent.putExtra(it.first, value)
is FloatArray -> intent.putExtra(it.first, value)
is DoubleArray -> intent.putExtra(it.first, value)
is CharArray -> intent.putExtra(it.first, value)
is ShortArray -> intent.putExtra(it.first, value)
is BooleanArray -> intent.putExtra(it.first, value)
is CharSequence -> intent.putExtra(it.first, value)
is Serializable -> intent.putExtra(it.first, value)
is Parcelable -> intent.putExtra(it.first, value)
else -> throw AnkoException("Intent extra ${it.first} has wrong type ${value.javaClass.name}")
}
return@forEach
}
}
@JvmStatic
inline fun <T> useCursor(cursor: Cursor, f: (Cursor) -> T): T {
return cursor.use(f)
// Closeable only added in API 16
}
@JvmStatic
fun <T : View> initiateView(ctx: Context, viewClass: Class<T>): T {
fun getConstructor1() = viewClass.getConstructor(Context::class.java)
fun getConstructor2() =
viewClass.getConstructor(Context::class.java, AttributeSet::class.java)
return try {
getConstructor1().newInstance(ctx)
} catch (_: NoSuchMethodException) {
try {
getConstructor2().newInstance(ctx, null)
} catch (_: NoSuchMethodException) {
throw AnkoException("Can't initiate View of class ${viewClass.name}: can't find proper constructor")
}
}
}
@Suppress("LongParameterList")
@JvmStatic
fun testConfiguration(
ctx: Context,
screenSize: ScreenSize?,
density: ClosedRange<Int>?,
language: String?,
orientation: Orientation?,
long: Boolean?,
fromSdk: Int?,
sdk: Int?,
uiMode: UiMode?,
nightMode: Boolean?,
rightToLeft: Boolean?,
smallestWidth: Int?,
): Boolean {
val config = ctx.resources?.configuration
if (screenSize != null) {
if (config == null) return false
when (config.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK) {
Configuration.SCREENLAYOUT_SIZE_UNDEFINED -> {}
Configuration.SCREENLAYOUT_SIZE_SMALL -> if (screenSize != ScreenSize.SMALL) return false
Configuration.SCREENLAYOUT_SIZE_NORMAL -> if (screenSize != ScreenSize.NORMAL) return false
Configuration.SCREENLAYOUT_SIZE_LARGE -> if (screenSize != ScreenSize.LARGE) return false
Configuration.SCREENLAYOUT_SIZE_XLARGE -> if (screenSize != ScreenSize.XLARGE) return false
}
}
if (density != null) {
val currentDensityDpi = ctx.resources?.displayMetrics?.densityDpi ?: return false
if (currentDensityDpi !in density || currentDensityDpi == density.endInclusive) return false
}
if (language != null) {
val locale = Locale.getDefault()
val currentLanguage =
if (language.indexOf('_') >= 0) locale.toString() else locale.language
if (currentLanguage != language) return false
}
if (orientation != null) {
if (config == null) return false
@Suppress("DEPRECATION")
when (config.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> if (orientation != Orientation.LANDSCAPE) return false
Configuration.ORIENTATION_PORTRAIT -> if (orientation != Orientation.PORTRAIT) return false
Configuration.ORIENTATION_SQUARE -> if (orientation != Orientation.SQUARE) return false
Configuration.ORIENTATION_UNDEFINED -> Unit
}
}
if (long != null) {
if (config == null) return false
val currentLong = config.screenLayout and Configuration.SCREENLAYOUT_LONG_MASK
if (currentLong == Configuration.SCREENLAYOUT_LONG_YES && !long) return false
if (currentLong == Configuration.SCREENLAYOUT_LONG_NO && long) return false
}
if (fromSdk != null) {
if (Build.VERSION.SDK_INT < fromSdk) return false
}
if (sdk != null) {
if (Build.VERSION.SDK_INT != sdk) return false
}
if (uiMode != null) {
if (config == null) return false
when (config.uiMode and Configuration.UI_MODE_TYPE_MASK) {
Configuration.UI_MODE_TYPE_NORMAL -> if (uiMode != UiMode.NORMAL) return false
Configuration.UI_MODE_TYPE_DESK -> if (uiMode != UiMode.DESK) return false
Configuration.UI_MODE_TYPE_CAR -> if (uiMode != UiMode.CAR) return false
Configuration.UI_MODE_TYPE_TELEVISION -> if (uiMode != UiMode.TELEVISION) return false
InternalConfiguration.UI_MODE_TYPE_APPLIANCE -> if (uiMode != UiMode.APPLIANCE) return false
InternalConfiguration.UI_MODE_TYPE_WATCH -> if (uiMode != UiMode.WATCH) return false
}
}
if (nightMode != null) {
val uiModeManager =
ctx.getSystemService(Context.UI_MODE_SERVICE) as? UiModeManager ?: return false
val currentMode = uiModeManager.nightMode
if (currentMode == UiModeManager.MODE_NIGHT_YES && !nightMode) return false
if (currentMode == UiModeManager.MODE_NIGHT_NO && nightMode) return false
}
if (rightToLeft != null) {
if (config == null) return false
val rtlMode = (config.screenLayout and
InternalConfiguration.SCREENLAYOUT_LAYOUTDIR_MASK) == InternalConfiguration.SCREENLAYOUT_LAYOUTDIR_RTL
if (rtlMode != rightToLeft) return false
}
if (smallestWidth != null) {
if (config == null) return false
if (config.smallestScreenWidthDp == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
if (smallestWidth != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) return false
} else if (config.smallestScreenWidthDp < smallestWidth) return false
}
return true
}
}