Kotlin creating Bitmap must not be null - android

lateinit var bitmap: Bitmap
onCreate{
bitmap = BitmapFactory.decodeResource(applicationContext.resources, Color.TRANSPARENT)
}
I am trying to create a transparent bitmap like above. However, it gives me this error.
Caused by: java.lang.IllegalStateException: BitmapFactory.decodeReso…urces, Color.TRANSPARENT) must not be null
What did I wrong?

As mentioned by #commonWare, either declare bitmap nullable:
var bitmap: Bitmap?
or handle null when you call decodeResource:
bitmap = BitmapFactory.decodeResource(applicationContext.resources, Color.TRANSPARENT) ?: DEFAULT_BITMAP
where DEFAULT_BITMAP is a default Bitmap you have created earlier.
And better if you have control over decodeResource method, make the return type non nullable and return some default value instead of null :
fun decodeResource(...): Bitmap {
.....
return bitmap ?: DEFAULT_BITMAP // return a default value instead of returning null
}

Related

How to force recomposition of an Image composable without recreating a different bitmap each time?

It is clear that in Android Compose if the state of variable/input of a composable changes, the composable gets recomposed.
Using:
val object = remember{ mutableStateOf(Object) }
is possible to preserve(remember) the instance of the object on each recomposition unless the mutable state gets reassigned to a different object which will actually trigger the recomposition of the composables that use that object as argument.
Now let's assume:
val bitmap = remember { ImageBitmap() }
val canvas = remember { Canvas(bitmap) }
---
//composable
Image(bitmap, contentDescription = "..")
in this case the image gets loaded only once, even after the bitmap gets edited the Image composable will never recompose and the result never displayed on the screen.
Somebody would say to use a MutableState variable:
val bitmap = remember { mutableStateOf(ImageBitmap()) }
but the mutable state doesn't emit if the reference of the object bitmap is the same.
What would be the best way to control the frame rate at which the bitmap is displayed in the composable without recreating a different bitmap each time?
So, I've found a temporary solution, even though I feel is not really efficient.
Sample code:
val bitmap = remember { ImageBitmap(500, 500, hasAlpha = true, config =
ImageBitmapConfig.Argb8888) }
val canvas = remember { Canvas(bitmap)}
var frames by remember { mutableStateOf(0) }
var isDrawing by remember { mutableStateOf(false) }
LaunchedEffect(isDrawing){
while (isDrawing){
frames++
delay(16)
}
}
Box(modifier = Modifier.pointerInteropFilter() { e ->
if (e.actionMasked == MotionEvent.ACTION_DOWN) {
isDrawing = true
}
if (e.actionMasked == MotionEvent.ACTION_MOVE) {
path = logic to get path
canvas.drawPath(path, paint)
}
if (e.actionMasked == MotionEvent.ACTION_UP) {
isDrawing = false
}
}){
Canvas() { frames.let { drawImage(bitTmp) } }
}

CameraX PreviewView to Bitmap instead of a File

I know there are other questions that was answered to many times and I tried to find the answer, but I couldn't understand of it most. I want to make the camera to take a photo, but instead of making a physical image file, I want it directly to make it into a bitmap and display it in an ImageView from a different activity, however I constantly getting the Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference, that's because of the viewFinder.bitmap is a null and decided to make a seperate function getBitmap(): Bitmap?. I don't know what it wants me to do honestly or any lead from this, all I want to do is to make the photo straight to ImageView as a bitmap. Thank you in advance.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.camerax5, PID: 5472
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.camerax5/com.example.camerax5.VirtualPreview}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3141)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3284)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1972)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7179)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:975)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:164)
at android.view.ContextThemeWrapper.getTheme(ContextThemeWrapper.java:157)
at android.content.Context.obtainStyledAttributes(Context.java:677)
at androidx.appcompat.app.AppCompatDelegateImpl.createSubDecor(AppCompatDelegateImpl.java:842)
at androidx.appcompat.app.AppCompatDelegateImpl.ensureSubDecor(AppCompatDelegateImpl.java:809)
at androidx.appcompat.app.AppCompatDelegateImpl.findViewById(AppCompatDelegateImpl.java:633)
at androidx.appcompat.app.AppCompatActivity.findViewById(AppCompatActivity.java:259)
at com.example.camerax5.MainActivity.getBitmap(MainActivity.kt:118)
at com.example.camerax5.VirtualPreview.onCreate(VirtualPreview.kt:19)
at android.app.Activity.performCreate(Activity.java:7335)
at android.app.Activity.performCreate(Activity.java:7326)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1275)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3121)
So the problem was occured on this line:
viewFinder = findViewById(R.id.previewView)
Here is the full code of what I was trying to do:
MainActivity.kt and underneath the MainActivity code is the other class with another activity to show the bitmap in the ImageView, I added this way is because I don't want to separate them and making them confusing:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewFinder = findViewById(R.id.previewView)
//Permissions
if (allPermissions()) {
startCamera()
} else {
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
//Capture button
val captureBtn: Button = findViewById(R.id.captureButton)
captureBtn.setOnClickListener {
takePhoto()
}
outputDirectory = outputDirectoryFolder()
cameraExecutor = Executors.newSingleThreadExecutor()
}
private fun takePhoto() {
Toast.makeText(baseContext, "Processing ..", Toast.LENGTH_SHORT).show()
//Directly to open the new Activity
val intent = Intent(this, VirtualPreview::class.java)
startActivity(intent)
}
fun getBitmap(): Bitmap? {
//Preview to Bitmap
viewFinder = findViewById(R.id.previewView)
val source = viewFinder.bitmap
val newWidth = 200
val newHeight = 200
val bitmapWidth = source?.width
val bitmapHeight = source?.height
//Matrix
val matrix = Matrix()
val scaleWidth = newWidth.toFloat() / bitmapWidth!!
val scaleHeight = newHeight.toFloat() / bitmapHeight!!
matrix.postScale(scaleWidth, scaleHeight)
return Bitmap.createBitmap(source, 0, 0, newWidth, newHeight, matrix, true)
}
}
class VirtualPreview : AppCompatActivity() {
private lateinit var acceptButton: Button
private lateinit var denyButton: Button
private lateinit var imageView: ImageView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_virtual_preview)
val bitmapPreview = MainActivity().getBitmap()
val drawableBitmap = BitmapDrawable(resources, bitmapPreview)
imageView = findViewById(R.id.imagePreview)
imageView.setImageDrawable(drawableBitmap)
acceptButton = findViewById(R.id.acceptButton)
acceptButton.setOnClickListener {
}
denyButton = findViewById(R.id.denyButton)
denyButton.setOnClickListener {
//delete the matrix and returns to the main activity
finish()
}
}
}
val bitmapPreview = MainActivity().getBitmap() - This way you are creating a new instance of MainActivity and the new instance do not have any thing in preview view, also this method is not recommended. Better store bitmap inside MainActivity and then pass that bitmap to VirtualPreview activity in constructor (in bundle it won't work). It won't be a problem for your case, as you're scaling the bitmap to 200x200. and for large bitmap, store in file, and use that file. And for your case, you can create another imageview in mainactivity and show the bitmap in that imageview instead of creating another activity.

Android: Blur background of LinearLayout using Kotlin

I have seen a lot of posts on this topic with all solutions being based on Java and not Kotlin. Is there a solution similiar to the one using Java but in Kotlin?
first get a screenshot of the layout:
getViewScreenshot(view: View): Bitmap {
view.setDrawingCacheEnabled(true)
val bitmap = Bitmap.createBitmap(view.getDrawingCache())
view.setDrawingCacheEnabled(false)
return bitmap
}
then blur it with this function:
fun blurBitmap(bitmap: Bitmap, applicationContext: Context): Bitmap {
lateinit var rsContext: RenderScript
try {
// Create the output bitmap
val output = Bitmap.createBitmap(
bitmap.width, bitmap.height, bitmap.config)
// Blur the image
rsContext = RenderScript.create(applicationContext, RenderScript.ContextType.DEBUG)
val inAlloc = Allocation.createFromBitmap(rsContext, bitmap)
val outAlloc = Allocation.createTyped(rsContext, inAlloc.type)
val theIntrinsic = ScriptIntrinsicBlur.create(rsContext, Element.U8_4(rsContext))
theIntrinsic.apply {
setRadius(10f)
theIntrinsic.setInput(inAlloc)
theIntrinsic.forEach(outAlloc)
}
outAlloc.copyTo(output)
return output
} finally {
rsContext.finish()
}
}

PackageManager not returing application's icon

I am trying to get the icon of an application's notification. The listener works perfectly, but for most of the apps I tried (telegram, signal, android messages...) I cannot seem to get the app's icon.
Here is the code where I try to get the icon:
private fun getIcon(notification: StatusBarNotification, context: Context): Bitmap {
val packageManager = context.packageManager
return try {
Bitmap.createBitmap(drawableToBitmap(packageManager.getApplicationIcon(notification.packageName)))
} catch (exception: PackageManager.NameNotFoundException) {
return Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8)
}
}
private fun drawableToBitmap(drawable: Drawable): Bitmap {
if (drawable is BitmapDrawable) {
if (drawable.bitmap != null) {
return drawable.bitmap
}
}
val bitmap: Bitmap = if (drawable.intrinsicWidth <= 0 || drawable.intrinsicHeight <= 0) {
Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
} else {
Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
}
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
To get the icons you should use both class & package names instead of only using the package name. If the app supports changing icons or has more than one launcher activity this works much better.
fun getIcon(context: Context, packageName: String, className: String): Drawable? {
var drawable: Drawable? = null
try {
val intent = Intent(Intent.ACTION_MAIN)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.setClassName(packageName, className)
drawable = context.packageManager.getActivityIcon(intent)
} catch (e: Exception) {
try {
drawable = context.packageManager.getApplicationIcon(packageName)
} catch (e: Exception) {
e.printStackTrace()
}
}
return drawable
}
This function returns the icon drawable or returns null if it fails. It tries to get the icon using both package and class names at first but if it fails it uses only package name. You can just use the package name one if you don't want to use the class name as well.
If it's still just catching the exception every time and you know the app is installed, you probably lack some permissions. You can also try again with targetSdkVersion 29 instead of targetSdkVersion 30 since 30 adds some limitations to this kind of functions but I am not sure if those affect getting the icons as well.

Native Crash in getting bitmap from resource

I have a native crash:
A/libc: invalid address or address of corrupt block 0x55766f1b00 passed to try_realloc_chunk
A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0xdeadbaad in tid 32219 (onPool-worker-1)
when executing the drawable.draw(canvas) line in the following method:
fun getBitmapFromResource(context: Context, imageRes: Int, iconSize: Float = CATEGORY_ICON_SIZE): Bitmap? {
val drawable = ContextCompat.getDrawable(context, imageRes)
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val size = GraphicsUtils.toPx(context, iconSize)
val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable!!.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas) // crash!!
return bitmap
}
The drawable is VectorDrawable implementation. I am executing this code on a background thread in a coroutine.
I added vectorDrawables.useSupportLibrary = true to build.gradle file, but it did not help.
I need bitmap object because from its width and height I draw a custom chart and I need to perform size calculations there.
I had the suspicion that multi-threading might break the process, so I added this code in the runBlocking section (still on a background thread) - no effect.
Any ideas how to fix this?
After several hours of investigation, I fixed the issue.
The problem seems to be that more than one coroutine was entering the method at the same time. I used Mutex to make sure only one coroutine can be inside the method.
object UIUtilsSingleton {
private val mutex = Mutex()
suspend fun getBitmapFromResource(context: Context, imageRes: Int): Bitmap? {
var bitmap: Bitmap? = null
mutex.withLock {
val iconSize = 42f
val drawable = ContextCompat.getDrawable(context, imageRes)
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val size = GraphicsUtils.toPx(context, iconSize)
bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable!!.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
}
return bitmap
}
}

Categories

Resources