Add rageskahe module

This commit is contained in:
Benoit Marty 2022-12-21 14:36:57 +01:00
parent 0644a5822f
commit 3f7a83c519
64 changed files with 3191 additions and 35 deletions

View file

@ -1,2 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.VIBRATE" />
</manifest>

View file

@ -0,0 +1,11 @@
package io.element.android.x.core.bitmap
import android.graphics.Bitmap
import java.io.File
fun File.writeBitmap(bitmap: Bitmap, format: Bitmap.CompressFormat, quality: Int) {
outputStream().use { out ->
bitmap.compress(format, quality, out)
out.flush()
}
}

View file

@ -0,0 +1,5 @@
package io.element.android.x.core.bool
fun Boolean?.orTrue() = this ?: true
fun Boolean?.orFalse() = this ?: false

View file

@ -0,0 +1,55 @@
package io.element.android.x.core.extensions
import android.util.Patterns
fun Boolean.toOnOff() = if (this) "ON" else "OFF"
inline fun <T> T.ooi(block: (T) -> Unit): T = also(block)
/**
* Check if a CharSequence is an email.
*/
fun CharSequence.isEmail() = Patterns.EMAIL_ADDRESS.matcher(this).matches()
// fun CharSequence.isMatrixId() = MatrixPatterns.isUserId(this.toString())
/**
* Return empty CharSequence if the CharSequence is null.
*/
fun CharSequence?.orEmpty() = this ?: ""
/**
* Check if a CharSequence is a phone number.
*/
/*
fun CharSequence.isMsisdn(): Boolean {
return try {
PhoneNumberUtil.getInstance().parse(ensurePrefix("+"), null)
true
} catch (e: NumberParseException) {
false
}
}
*/
/**
* Useful to append a String at the end of a filename but before the extension if any
* Ex:
* - "file.txt".insertBeforeLast("_foo") will return "file_foo.txt"
* - "file".insertBeforeLast("_foo") will return "file_foo"
* - "fi.le.txt".insertBeforeLast("_foo") will return "fi.le_foo.txt"
* - null.insertBeforeLast("_foo") will return "_foo".
*/
fun String?.insertBeforeLast(insert: String, delimiter: String = "."): String {
if (this == null) return insert
val idx = lastIndexOf(delimiter)
return if (idx == -1) {
this + insert
} else {
replaceRange(idx, idx, insert)
}
}
inline fun <reified R> Any?.takeAs(): R? {
return takeIf { it is R } as R?
}

View file

@ -0,0 +1,38 @@
package io.element.android.x.core.file
import timber.log.Timber
import java.io.File
import java.util.zip.GZIPOutputStream
/**
* GZip a file.
*
* @param file the input file
* @return the gzipped file
*/
fun compressFile(file: File): File? {
Timber.v("## compressFile() : compress ${file.name}")
val dstFile = file.resolveSibling(file.name + ".gz")
if (dstFile.exists()) {
dstFile.delete()
}
return try {
GZIPOutputStream(dstFile.outputStream()).use { gos ->
file.inputStream().use {
it.copyTo(gos, 2048)
}
}
Timber.v("## compressFile() : ${file.length()} compressed to ${dstFile.length()} bytes")
dstFile
} catch (e: Exception) {
Timber.e(e, "## compressFile() failed")
null
} catch (oom: OutOfMemoryError) {
Timber.e(oom, "## compressFile() failed")
null
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright 2020 New Vector Ltd
*
* 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.
*/
package io.element.android.x.core.hardware
import android.content.Context
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import androidx.core.content.getSystemService
fun Context.vibrate(durationMillis: Long = 100) {
val vibrator = getSystemService<Vibrator>() ?: return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(durationMillis, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
@Suppress("DEPRECATION")
vibrator.vibrate(durationMillis)
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* 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.
*/
package io.element.android.x.core.mimetype
import io.element.android.x.core.bool.orFalse
// The Android SDK does not provide constant for mime type, add some of them here
object MimeTypes {
const val Any: String = "*/*"
const val OctetStream = "application/octet-stream"
const val Apk = "application/vnd.android.package-archive"
const val Images = "image/*"
const val Png = "image/png"
const val BadJpg = "image/jpg"
const val Jpeg = "image/jpeg"
const val Gif = "image/gif"
const val Ogg = "audio/ogg"
const val PlainText = "text/plain"
fun String?.normalizeMimeType() = if (this == BadJpg) Jpeg else this
fun String?.isMimeTypeImage() = this?.startsWith("image/").orFalse()
fun String?.isMimeTypeVideo() = this?.startsWith("video/").orFalse()
fun String?.isMimeTypeAudio() = this?.startsWith("audio/").orFalse()
fun String?.isMimeTypeApplication() = this?.startsWith("application/").orFalse()
fun String?.isMimeTypeFile() = this?.startsWith("file/").orFalse()
fun String?.isMimeTypeText() = this?.startsWith("text/").orFalse()
fun String?.isMimeTypeAny() = this?.startsWith("*/").orFalse()
}

View file

@ -0,0 +1,57 @@
package io.element.android.x.core.screenshot
import android.app.Activity
import android.graphics.Bitmap
import android.graphics.Canvas
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.view.PixelCopy
import android.view.View
fun View.screenshot(bitmapCallback: (ImageResult) -> Unit) {
try {
val handler = Handler(Looper.getMainLooper())
val bitmap = Bitmap.createBitmap(
width,
height,
Bitmap.Config.ARGB_8888,
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
PixelCopy.request(
(this.context as Activity).window,
clipBounds,
bitmap,
{
when (it) {
PixelCopy.SUCCESS -> {
bitmapCallback.invoke(ImageResult.Success(bitmap))
}
else -> {
bitmapCallback.invoke(ImageResult.Error(Exception(it.toString())))
}
}
},
handler
)
} else {
handler.post {
val canvas = Canvas(bitmap)
.apply {
translate(-clipBounds.left.toFloat(), -clipBounds.top.toFloat())
}
this.draw(canvas)
canvas.setBitmap(null)
bitmapCallback.invoke(ImageResult.Success(bitmap))
}
}
} catch (e: Exception) {
bitmapCallback.invoke(ImageResult.Error(e))
}
}
sealed interface ImageResult {
data class Error(val exception: Exception) : ImageResult
data class Success(val data: Bitmap) : ImageResult
}