Utils.kt 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package im.angry.openeuicc.util
  2. import android.content.Context
  3. import android.content.pm.PackageManager
  4. import android.graphics.Bitmap
  5. import android.se.omapi.SEService
  6. import android.telephony.TelephonyManager
  7. import androidx.fragment.app.Fragment
  8. import com.google.zxing.BinaryBitmap
  9. import com.google.zxing.RGBLuminanceSource
  10. import com.google.zxing.common.HybridBinarizer
  11. import com.google.zxing.qrcode.QRCodeReader
  12. import im.angry.openeuicc.OpenEuiccApplication
  13. import im.angry.openeuicc.di.AppContainer
  14. import kotlinx.coroutines.Dispatchers
  15. import kotlinx.coroutines.runBlocking
  16. import kotlinx.coroutines.sync.Mutex
  17. import kotlinx.coroutines.sync.withLock
  18. import kotlinx.coroutines.withContext
  19. import kotlin.RuntimeException
  20. import kotlin.coroutines.resume
  21. import kotlin.coroutines.resumeWithException
  22. import kotlin.coroutines.suspendCoroutine
  23. val Context.selfAppVersion: String
  24. get() =
  25. try {
  26. val pInfo = packageManager.getPackageInfo(packageName, 0)
  27. pInfo.versionName!!
  28. } catch (e: PackageManager.NameNotFoundException) {
  29. throw RuntimeException(e)
  30. }
  31. suspend fun readSelfLog(lines: Int = 2048): String = withContext(Dispatchers.IO) {
  32. try {
  33. Runtime.getRuntime().exec("logcat -t $lines").inputStream.readBytes()
  34. .decodeToString()
  35. } catch (_: Exception) {
  36. ""
  37. }
  38. }
  39. interface OpenEuiccContextMarker {
  40. val openEuiccMarkerContext: Context
  41. get() = when (this) {
  42. is Context -> this
  43. is Fragment -> requireContext()
  44. else -> throw RuntimeException("OpenEuiccUIContextMarker shall only be used on Fragments or UI types that derive from Context")
  45. }
  46. val openEuiccApplication: OpenEuiccApplication
  47. get() = openEuiccMarkerContext.applicationContext as OpenEuiccApplication
  48. val appContainer: AppContainer
  49. get() = openEuiccApplication.appContainer
  50. val telephonyManager: TelephonyManager
  51. get() = appContainer.telephonyManager
  52. }
  53. // Create an instance of OMAPI SEService in a manner that "makes sense" without unpredictable callbacks
  54. suspend fun connectSEService(context: Context): SEService = suspendCoroutine { cont ->
  55. // Use a Mutex to make sure the continuation is run *after* the "service" variable is assigned
  56. val lock = Mutex()
  57. var service: SEService? = null
  58. val callback = {
  59. runBlocking {
  60. lock.withLock {
  61. cont.resume(service!!)
  62. }
  63. }
  64. }
  65. runBlocking {
  66. // If this were not protected by a Mutex, callback might be run before service is even assigned
  67. // Yes, we are on Android, we could have used something like a Handler, but we cannot really
  68. // assume the coroutine is run on a thread that has a Handler. We either use our own HandlerThread
  69. // (and then cleanup becomes an issue), or we use a lock
  70. lock.withLock {
  71. try {
  72. service = SEService(context, { it.run() }, callback)
  73. } catch (e: Exception) {
  74. cont.resumeWithException(e)
  75. }
  76. }
  77. }
  78. }
  79. inline fun <T> Bitmap.use(f: (Bitmap) -> T): T =
  80. try {
  81. f(this)
  82. } finally {
  83. recycle()
  84. }
  85. fun decodeQrFromBitmap(bmp: Bitmap): String? =
  86. runCatching {
  87. val pixels = IntArray(bmp.width * bmp.height)
  88. bmp.getPixels(pixels, 0, bmp.width, 0, 0, bmp.width, bmp.height)
  89. val luminanceSource = RGBLuminanceSource(bmp.width, bmp.height, pixels)
  90. val binaryBmp = BinaryBitmap(HybridBinarizer(luminanceSource))
  91. QRCodeReader().decode(binaryBmp).text
  92. }.getOrNull()