1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.wallpaper.util
17 
18 import android.app.WallpaperColors
19 import android.graphics.Bitmap
20 import android.graphics.BitmapFactory
21 import android.graphics.ColorSpace
22 import android.os.Handler
23 import java.io.ByteArrayOutputStream
24 import java.util.concurrent.Executor
25 import java.util.concurrent.atomic.AtomicInteger
26 
27 /**
28  * Wallpaper color extractor. Instantiate it with a proper handler. We usually use the main thread
29  * handler, so we can change UI accordingly when colors are extracted.
30  */
31 class WallpaperColorsExtractor(
32     private val mExecutor: Executor,
33     private val mResultHandler: Handler
34 ) {
35     private val mCurrentTaskId = AtomicInteger(0)
36 
37     /**
38      * Extracts wallpaper colors. Noticed that when there are consecutive calls, only the results
39      * from the latest call will be posted. This is done by an incremental [mCurrentTaskId] to
40      * identify if a task id is still the latest, right before posting the results.
41      */
extractWallpaperColorsnull42     fun extractWallpaperColors(
43         wallpaperBitmap: Bitmap,
44         onColorsExtractedListener: OnColorsExtractedListener
45     ) {
46         mExecutor.execute {
47             val taskId = mCurrentTaskId.incrementAndGet()
48 
49             val tmpOut = ByteArrayOutputStream()
50             var shouldRecycle = false
51             var cropped = wallpaperBitmap
52             if (cropped.compress(Bitmap.CompressFormat.PNG, 100, tmpOut)) {
53                 val outByteArray = tmpOut.toByteArray()
54                 val options = BitmapFactory.Options()
55                 options.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.SRGB)
56                 cropped = BitmapFactory.decodeByteArray(outByteArray, 0, outByteArray.size)
57             }
58             if (cropped.config == Bitmap.Config.HARDWARE) {
59                 cropped = cropped.copy(Bitmap.Config.ARGB_8888, false)
60                 shouldRecycle = true
61             }
62             val colors = WallpaperColors.fromBitmap(cropped)
63             if (shouldRecycle) {
64                 cropped.recycle()
65             }
66             // This makes sure that the listener only listen to the latest results, when multiple
67             // extractWallpaperColors tasks are executed.
68             if (taskId == mCurrentTaskId.get()) {
69                 mResultHandler.post { onColorsExtractedListener.onColorsExtracted(colors) }
70             }
71         }
72     }
73 }
74