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

Commit ebd18697 authored by Anton Potapov's avatar Anton Potapov
Browse files

Add IconViewModel load icons on the uiBackground thread

Flag: EXEMPT UNUSED
Bug: 28686600
Test: atest DrawablesViewModelTest
Change-Id: I7d54c2067b3bfb29440699f090193be3d6e83b9a
parent f81b1561
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.common.ui.compose

import android.graphics.drawable.Drawable
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.ui.platform.LocalContext
import com.android.systemui.screencapture.common.ui.viewmodel.DrawableLoaderViewModel

/** @see DrawableLoaderViewModel */
@Composable
fun loadDrawable(viewModel: DrawableLoaderViewModel, @DrawableRes resId: Int): Drawable? {
    val context = LocalContext.current
    val drawable by
        produceState<Drawable?>(initialValue = null, keys = arrayOf(viewModel, context, resId)) {
            value = viewModel.loadDrawable(context = context, resId = resId)
        }
    return drawable
}
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.common.ui.compose

import androidx.annotation.DrawableRes
import androidx.compose.runtime.Composable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.screencapture.common.ui.viewmodel.DrawableLoaderViewModel

/** @see DrawableLoaderViewModel */
@Composable
fun loadIcon(
    viewModel: DrawableLoaderViewModel,
    @DrawableRes resId: Int,
    contentDescription: ContentDescription?,
): Icon.Loaded? {
    return loadDrawable(viewModel, resId)?.let { drawable ->
        Icon.Loaded(drawable = drawable, res = resId, contentDescription = contentDescription)
    }
}
+58 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.common.ui.viewmodel

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.Drawable
import androidx.annotation.DrawableRes
import com.android.systemui.dagger.qualifiers.UiBackground
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.withContext

/**
 * Interface to mark your ViewModels as ones capable of loading drawables. This enables it to load
 * them on [UiBackground] executor by using [DrawableLoaderViewModelImpl].
 *
 * Example:
 * ```
 * class FooViewModel(vm: DrawableLoaderViewModelImpl): DrawableLoaderViewModel by vm
 * ```
 *
 * And then in compose:
 * ```
 * @Composable
 * fun Foo(viewModel: FooViewModel) {
 *   loadIcon(viewModel, R.drawable.icon)?.let { Icon(it) }
 * }
 * ```
 */
interface DrawableLoaderViewModel {

    suspend fun loadDrawable(context: Context, @DrawableRes resId: Int): Drawable
}

class DrawableLoaderViewModelImpl
@Inject
constructor(@UiBackground private val uiBackgroundCoroutineContext: CoroutineContext) :
    DrawableLoaderViewModel {

    @SuppressLint("UseCompatLoadingForDrawables")
    override suspend fun loadDrawable(context: Context, @DrawableRes resId: Int): Drawable =
        withContext(uiBackgroundCoroutineContext) { context.getDrawable(resId)!! }
}
+28 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.common.ui.viewmodel

import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.backgroundCoroutineContext

val Kosmos.drawableLoaderViewModelImpl: DrawableLoaderViewModelImpl by
    Kosmos.Fixture {
        DrawableLoaderViewModelImpl(uiBackgroundCoroutineContext = backgroundCoroutineContext)
    }

var Kosmos.drawableLoaderViewModel: DrawableLoaderViewModel by
    Kosmos.Fixture { drawableLoaderViewModelImpl }