Skip to content

WSC 48th MAD 2nd

THE PREPARE NOTE

Text can be drag and drop to external app like messenger

AndroidView(modifier = modifier, factory = { context ->
TextView(context).apply {
this.text = "Hello World!!!"
textSize = 30f
setTypeface(typeface, android.graphics.Typeface.BOLD)
setTextColor(android.graphics.Color.BLACK)
setOnLongClickListener { v: View ->
val item = ClipData.Item(this.text)
val mimeTypes = arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN)
val dragData = ClipData("text", mimeTypes, item)
val shadow = View.DragShadowBuilder(v)
v.startDragAndDrop(dragData, shadow, null, View.DRAG_FLAG_GLOBAL)
true
}
}
})

change pivot in graphicsLayer

transformOrigin = TransformOrigin(0.5f, 1f)
DisposableEffect(Unit) {
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
val listener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
val x = event.values[0]
tiledLeft = x in 3f..15f
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> {
sensorManager.registerListener(
listener,
accelerometer,
SensorManager.SENSOR_DELAY_GAME
)
}
Lifecycle.Event.ON_PAUSE -> {
sensorManager.unregisterListener(listener)
}
else -> {}
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
sensorManager.unregisterListener(listener)
}
}

use onGloballyPositioned to get LayoutCoordinates of the target composable

val view = LocalView.current
val window = (view.context as Activity).window
var captureArea: LayoutCoordinates? = null
suspend fun captureMap() {
delay(100L)
val areaRect = captureArea?.boundsInWindow()?.toAndroidRect()
if (areaRect != null) {
val bitmap = Bitmap.createBitmap(areaRect.width(), areaRect.height(), Bitmap.Config.ARGB_8888)
PixelCopy.request(window, areaRect, bitmap, { result ->
if (result == PixelCopy.SUCCESS) {
val contentValue = ContentValues().apply {
put(
MediaStore.MediaColumns.DISPLAY_NAME,
"${System.currentTimeMillis()}_map.png"
)
put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
}
val resolver = context.contentResolver
val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val uri = resolver.insert(contentUri, contentValue)!!
resolver.openOutputStream(uri)?.let {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
it.flush()
it.close()
}
}
}, Handler(Looper.getMainLooper()))
}
}
Modifier.pointerInput(Unit) {
awaitPointerEventScope {
while (true) {
val event = awaitPointerEvent()
val position = event.changes.first().position
val liftUp = event.changes.all { !it.pressed }
when {
liftUp -> {
draw.add(Path().apply {
currentDraw
.toList()
.forEachIndexed { index, offset ->
if (index == 0) moveTo(offset.x, offset.y)
lineTo(offset.x, offset.y)
}
})
currentDraw.clear()
}
event.changes.first().pressed -> {
currentDraw.add(position)
}
}
}
}
}
@Composable
fun TravelScreen() {
val context = LocalContext.current
val data = remember { mutableStateListOf<Location>() }
var mapScaleFactor by remember { mutableFloatStateOf(0f) }
val mapBitmap =
remember { BitmapFactory.decodeResource(context.resources, R.drawable.map_paris_city) }
var scale by remember { mutableFloatStateOf(1.2f) }
var pan by remember { mutableStateOf(Offset.Zero) }
Box(
modifier = Modifier
.clipToBounds()
.fillMaxSize()
.graphicsLayer {
translationX = pan.x * scale
translationY = pan.y * scale
scaleX = scale
scaleY = scale
}
.pointerInput(Unit) {
detectTransformGestures { _, panLevel, zoom, _ ->
scale *= zoom
pan += panLevel
}
}
) {
Box(modifier = Modifier.align(Alignment.Center)) {
Image(
bitmap = mapBitmap.asImageBitmap(),
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.onGloballyPositioned {
mapScaleFactor = it.size.width.toFloat() / mapBitmap.width
}
.align(Alignment.Center), contentScale = ContentScale.Fit
)
data.forEach { location ->
Box(modifier = Modifier
.offset(
((location.markOffset().first * mapScaleFactor) - 20).dp,
((location.markOffset().second * mapScaleFactor) - 40).dp
)
.graphicsLayer {
scaleX /= scale
scaleY /= scale
transformOrigin = TransformOrigin(0.5f, 1f)
}
.size(40.dp)
.clip(CircleShape)
.clickable {
}) {
Image(
painter = painterResource(R.drawable.icon_map_marker),
contentDescription = null
)
}
}
}
}
}
val uri = Uri.parse("geo:0,0?q=${detail.locationName}")
val mapIntent = Intent(
Intent.ACTION_VIEW, uri
) .setPackage("com.google.android.apps.maps")
context.startActivity(mapIntent)
  • Map parsing
"ratings_categories" : [
{"Staff":8.7},{"Facilities":8.4},{"Cleanliness":8.6}
]
parentJsonObj.getJSONArray("ratings_categories")
.let { catArray ->
val map = mutableMapOf<String, Float>()
repeat(catArray.length()) { index ->
val catObj = catArray.getJSONObject(index)
val key = catObj.keys().next()
val value = catObj.getDouble(key).toFloat()
map[key] = value
}
map
}

Convert formated date text

  • with SimpleDateFormat
SimpleDateFormat(
"MMM dd, yyyy, hh:mm a",
Locale.US
).format(diary.uploadDatetime.let { formatedDate ->
SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.US).parse(formatedDate)?.time
})
  • with DateTimeFormatter
fun convertDate(string: String): Long {
val formats = listOf(
DateTimeFormatter.ofPattern("MM/dd/yyyy"),
DateTimeFormatter.ofPattern("MM-dd-yyyy"),
DateTimeFormatter.ofPattern("MMM dd yyyy")
)
formats.forEach {
try {
val date = LocalDate.parse(string.trim(), it)
return date.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli()
} catch (_: Exception) {
}
}
throw Exception("Not the correct format for check-in or check-out date")
}
CodeMeaning
yyyyyear(2025)
MMMMmonth(August)
MMMmonth(Aug)
MMmonth(08)
EEEEday of week(Wednesday)
EEEday of week(Wed)
ddday of month
HH24hr clock(18)
hh12hr clock(08)
mmminute
sssecond
aAM/PM marker

Use espresso to close on screen keyboard

Espresso.closeSoftKeyboard()
val coinSound = remember { MediaPlayer.create(context, R.raw.coin) }
val playerBitmapOriginal = remember {
BitmapFactory.decodeResource(context.resources, R.drawable.skiing_person)
}
Bitmap.createScaledBitmap(
playerBitmapOriginal,
(width).toInt(),
(height).toInt(),
true
)
fun adjustPlayerColor(originalBitmap: Bitmap, hue: Float, black: Boolean = false): Bitmap {
val bitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true)
val targetColor = Color.hsv(hue, 0.54f, 0.97f)
for (width in 0 until bitmap.width) {
for (height in 0 until bitmap.height) {
val pixelColor = bitmap.getPixel(width, height)
if (pixelColor == android.graphics.Color.parseColor("#f87373")) {
bitmap.setPixel(
width,
height,
if (black) android.graphics.Color.BLACK else targetColor.toArgb()
)
}
}
}
return bitmap
}

add in <activity

android:screenOrientation="portrait"
DisposableEffect(Unit) {
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
val listener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
val x = event.values[0]
tiledLeft = x in 3f..15f
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> {
sensorManager.registerListener(
listener,
accelerometer,
SensorManager.SENSOR_DELAY_GAME
)
}
Lifecycle.Event.ON_PAUSE -> {
sensorManager.unregisterListener(listener)
}
else -> {}
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
sensorManager.unregisterListener(listener)
}
}
fun getJson(path: String): String {
return URL(path).openConnection().let {
BufferedReader(InputStreamReader(it.getInputStream())).readText()
}
}
suspend fun auth(email: String, password: String): Pair<Boolean, String> {
return withContext(Dispatchers.IO) {
try {
val endpoint =
(URL("$host/api/users/signin").openConnection() as HttpURLConnection).apply {
requestMethod = "POST"
setRequestProperty("Content-Type", "application/json")
doOutput = true
}
val body = JSONObject().apply {
put("userEmailAddress", email)
put("userPassword", password)
}.toString()
endpoint.outputStream.use { req ->
req.write(body.toByteArray())
req.flush()
}
val ok = endpoint.responseCode == HttpURLConnection.HTTP_OK
val msg = (if (ok) endpoint.inputStream else endpoint.errorStream).bufferedReader()
.readText().let {
authToken = JSONObject(it).getJSONObject("data").getString("auth_token")
JSONObject(it).getString("msg")
}
Pair(ok, msg)
} catch (e: Exception) {
Log.e("Auth Api", "Error: $e")
Pair(false, "internal error")
}
}
}
val bgBitmap = remember { BitmapFactory.decodeResource(context.resources, R.drawable.background) }
val bgShader = ImageShader(bgBitmap.asImageBitmap(), TileMode.Repeated, TileMode.Repeated)
val bgBrush = ShaderBrush(bgShader)

.ttf may required manually adding config to font

Remember to Rebuild the project after adding new font to the font resource

Create fontFamily

val playFair = FontFamily(
Font(R.font.playfair_display_regular, FontWeight.Normal)
)
val weekDays = DateFormatSymbols(Locale.US).shortWeekdays.drop(1)

Better petal shaped pager rotation calculation

Section titled “Better petal shaped pager rotation calculation”
val rotation =
-(pagerState.currentPage - index + pagerState.currentPageOffsetFraction) * 10f
// currentPage - thisPage == relative page (ex: current -> 0 next -> -1 last -> 1