package net.osmand.telegram.utils import android.content.Context import android.graphics.* import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.hardware.Sensor import android.hardware.SensorManager import android.support.annotation.ColorInt import android.support.annotation.ColorRes import android.support.annotation.DrawableRes import android.support.v4.content.ContextCompat import android.support.v4.graphics.drawable.DrawableCompat import android.view.Surface import android.view.WindowManager import android.widget.ImageView import android.widget.TextView import net.osmand.Location import net.osmand.data.LatLon import net.osmand.telegram.R import net.osmand.telegram.TelegramApplication import net.osmand.telegram.ui.views.DirectionDrawable import java.util.* class UiUtils(private val app: TelegramApplication) { private val drawableCache = LinkedHashMap() private val circleBitmapCache = LinkedHashMap() private val isLightContent: Boolean get() = true fun getCircleBitmap(path: String): Bitmap? { var bmp: Bitmap? = circleBitmapCache[path] if (bmp == null) { bmp = BitmapFactory.decodeFile(path) if (bmp != null) { bmp = app.uiUtils.createCircleBitmap(bmp, true) circleBitmapCache[path] = bmp } } return bmp } private fun getDrawable(@DrawableRes resId: Int, @ColorRes clrId: Int): Drawable? { val hash = (resId.toLong() shl 31) + clrId var d: Drawable? = drawableCache[hash] if (d == null) { d = ContextCompat.getDrawable(app, resId) if (d != null) { d = DrawableCompat.wrap(d) d!!.mutate() if (clrId != 0) { DrawableCompat.setTint(d, ContextCompat.getColor(app, clrId)) } drawableCache[hash] = d } } return d } private fun getPaintedDrawable(@DrawableRes resId: Int, @ColorInt color: Int): Drawable? { val hash = (resId.toLong() shl 31) + color var d: Drawable? = drawableCache[hash] if (d == null) { d = ContextCompat.getDrawable(app, resId) if (d != null) { d = DrawableCompat.wrap(d) d!!.mutate() DrawableCompat.setTint(d, color) drawableCache[hash] = d } } return d } fun getPaintedIcon(@DrawableRes id: Int, @ColorInt color: Int): Drawable? { return getPaintedDrawable(id, color) } fun getIcon(@DrawableRes id: Int, @ColorRes colorId: Int): Drawable? { return getDrawable(id, colorId) } fun getIcon(@DrawableRes backgroundId: Int, @DrawableRes id: Int, @ColorRes colorId: Int): Drawable { val b = getDrawable(backgroundId, 0) val f = getDrawable(id, colorId) val layers = arrayOfNulls(2) layers[0] = b layers[1] = f return LayerDrawable(layers) } fun getThemedIcon(@DrawableRes id: Int): Drawable? { return getDrawable(id, if (isLightContent) R.color.icon_light else 0) } fun getIcon(@DrawableRes id: Int): Drawable? { return getDrawable(id, 0) } fun getIcon(@DrawableRes id: Int, light: Boolean): Drawable? { return getDrawable(id, if (light) R.color.icon_light else 0) } private fun createCircleBitmap(source: Bitmap, recycleSource: Boolean = false): Bitmap { val size = Math.min(source.width, source.height) val width = (source.width - size) / 2 val height = (source.height - size) / 2 val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) val canvas = Canvas(bitmap) val paint = Paint() val shader = BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) if (width != 0 || height != 0) { // source isn't square, move viewport to center val matrix = Matrix() matrix.setTranslate((-width).toFloat(), (-height).toFloat()) shader.setLocalMatrix(matrix) } paint.shader = shader paint.isAntiAlias = true val r = size / 2f canvas.drawCircle(r, r, r, paint) if (recycleSource) { source.recycle() } return bitmap } fun updateLocationView( arrow: ImageView?, text: TextView?, toLoc: LatLon?, cache: UpdateLocationViewCache ) { val fromLoc = app.locationProvider.lastKnownLocationLatLon val heading = app.locationProvider.heading val mes = FloatArray(2) val locPassive = fromLoc == null || toLoc == null || cache.outdatedLocation val colorId = if (locPassive) R.color.icon_light else R.color.ctrl_active_light if (fromLoc != null && toLoc != null) { Location.distanceBetween( fromLoc.latitude, fromLoc.longitude, toLoc.latitude, toLoc.longitude, mes ) } if (arrow != null) { var newImage = false val drawable = arrow.drawable val dd = if (drawable is DirectionDrawable) { drawable } else { newImage = true DirectionDrawable(app) } dd.setImage(R.drawable.ic_direction_arrow, colorId) if (fromLoc == null || toLoc == null || heading == null) { dd.setAngle(0f) } else { dd.setAngle(mes[1] - heading + 180 + cache.screenOrientation) } if (newImage) { arrow.setImageDrawable(dd) } arrow.invalidate() } if (text != null) { text.setTextColor(ContextCompat.getColor(app, colorId)) val meters = if (fromLoc == null || toLoc == null) 0f else mes[1] text.text = OsmandFormatter.getFormattedDistance(meters, app) } } fun getUpdateLocationViewCache() = UpdateLocationViewCache().apply { screenOrientation = getScreenOrientation() } private fun getScreenOrientation(): Int { // screenOrientation correction must not be applied for devices without compass val sensorManager = app.getSystemService(Context.SENSOR_SERVICE) as SensorManager? if (sensorManager?.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) == null) { return 0 } val windowManager = app.getSystemService(Context.WINDOW_SERVICE) as WindowManager? val rotation = windowManager?.defaultDisplay?.rotation ?: return 0 return when (rotation) { Surface.ROTATION_90 -> 90 Surface.ROTATION_180 -> 180 Surface.ROTATION_270 -> 270 else -> 0 } } class UpdateLocationViewCache { var screenOrientation: Int = 0 var outdatedLocation: Boolean = false } }