Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1383ff3a authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Catch IllegalArgumentException caused by recycled bitmap" into qt-r1-dev

parents f803a67b c56a2669
Loading
Loading
Loading
Loading
+43 −32
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.renderscript.Allocation
import android.renderscript.Element
import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicBlur
import android.util.Log
import android.util.MathUtils
import com.android.internal.graphics.ColorUtils
import com.android.systemui.statusbar.notification.MediaNotificationProcessor
@@ -32,6 +33,7 @@ import com.android.systemui.statusbar.notification.MediaNotificationProcessor
import javax.inject.Inject
import javax.inject.Singleton

private const val TAG = "MediaArtworkProcessor"
private const val COLOR_ALPHA = (255 * 0.7f).toInt()
private const val BLUR_RADIUS = 25f
private const val DOWNSAMPLE = 6
@@ -42,16 +44,20 @@ class MediaArtworkProcessor @Inject constructor() {
    private val mTmpSize = Point()
    private var mArtworkCache: Bitmap? = null

    fun processArtwork(context: Context, artwork: Bitmap): Bitmap {
    fun processArtwork(context: Context, artwork: Bitmap): Bitmap? {
        if (mArtworkCache != null) {
            return mArtworkCache!!
            return mArtworkCache
        }

        context.display.getSize(mTmpSize)
        val renderScript = RenderScript.create(context)
        val blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript))
        var input: Allocation? = null
        var output: Allocation? = null
        var inBitmap: Bitmap? = null
        try {
            context.display.getSize(mTmpSize)
            val rect = Rect(0, 0, artwork.width, artwork.height)
            MathUtils.fitRect(rect, Math.max(mTmpSize.x / DOWNSAMPLE, mTmpSize.y / DOWNSAMPLE))
        var inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(),
            inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(),
                    true /* filter */)
            // Render script blurs only support ARGB_8888, we need a conversion if we got a
            // different bitmap config.
@@ -60,12 +66,13 @@ class MediaArtworkProcessor @Inject constructor() {
                inBitmap = oldIn.copy(Bitmap.Config.ARGB_8888, false /* isMutable */)
                oldIn.recycle()
            }
        val input = Allocation.createFromBitmap(renderScript, inBitmap,
                Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE)
            val outBitmap = Bitmap.createBitmap(inBitmap.width, inBitmap.height,
                    Bitmap.Config.ARGB_8888)
        val output = Allocation.createFromBitmap(renderScript, outBitmap)
        val blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript))

            input = Allocation.createFromBitmap(renderScript, inBitmap,
                    Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE)
            output = Allocation.createFromBitmap(renderScript, outBitmap)

            blur.setRadius(BLUR_RADIUS)
            blur.setInput(input)
            blur.forEach(output)
@@ -73,14 +80,18 @@ class MediaArtworkProcessor @Inject constructor() {

            val swatch = MediaNotificationProcessor.findBackgroundSwatch(artwork)

        input.destroy()
        output.destroy()
        inBitmap.recycle()
        blur.destroy()

            val canvas = Canvas(outBitmap)
            canvas.drawColor(ColorUtils.setAlphaComponent(swatch.rgb, COLOR_ALPHA))
            return outBitmap
        } catch (ex: IllegalArgumentException) {
            Log.e(TAG, "error while processing artwork", ex)
            return null
        } finally {
            input?.destroy()
            output?.destroy()
            blur.destroy()
            inBitmap?.recycle()
        }
    }

    fun clearCache() {
+109 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * 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 com.android.systemui.statusbar

import com.google.common.truth.Truth.assertThat

import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Point
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

private const val WIDTH = 200
private const val HEIGHT = 200

@RunWith(AndroidTestingRunner::class)
@SmallTest
class MediaArtworkProcessorTest : SysuiTestCase() {

    private var screenWidth = 0
    private var screenHeight = 0

    private lateinit var processor: MediaArtworkProcessor

    @Before
    fun setUp() {
        processor = MediaArtworkProcessor()

        val point = Point()
        context.display.getSize(point)
        screenWidth = point.x
        screenHeight = point.y
    }

    @After
    fun tearDown() {
        processor.clearCache()
    }

    @Test
    fun testProcessArtwork() {
        // GIVEN some "artwork", which is just a solid blue image
        val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
        Canvas(artwork).drawColor(Color.BLUE)
        // WHEN the background is created from the artwork
        val background = processor.processArtwork(context, artwork)!!
        // THEN the background has the size of the screen that has been downsamples
        assertThat(background.height).isLessThan(screenHeight)
        assertThat(background.width).isLessThan(screenWidth)
        assertThat(background.config).isEqualTo(Bitmap.Config.ARGB_8888)
    }

    @Test
    fun testCache() {
        // GIVEN a solid blue image
        val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
        Canvas(artwork).drawColor(Color.BLUE)
        // WHEN the background is processed twice
        val background1 = processor.processArtwork(context, artwork)!!
        val background2 = processor.processArtwork(context, artwork)!!
        // THEN the two bitmaps are the same
        // Note: This is currently broken and trying to use caching causes issues
        assertThat(background1).isNotSameAs(background2)
    }

    @Test
    fun testConfig() {
        // GIVEN some which is not ARGB_8888
        val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ALPHA_8)
        Canvas(artwork).drawColor(Color.BLUE)
        // WHEN the background is created from the artwork
        val background = processor.processArtwork(context, artwork)!!
        // THEN the background has Config ARGB_8888
        assertThat(background.config).isEqualTo(Bitmap.Config.ARGB_8888)
    }

    @Test
    fun testRecycledArtwork() {
        // GIVEN some "artwork", which is just a solid blue image
        val artwork = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888)
        Canvas(artwork).drawColor(Color.BLUE)
        // AND the artwork is recycled
        artwork.recycle()
        // WHEN the background is created from the artwork
        val background = processor.processArtwork(context, artwork)
        // THEN the processed bitmap is null
        assertThat(background).isNull()
    }
}
 No newline at end of file