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

Commit 6cdbe0f2 authored by Chris Göllner's avatar Chris Göllner Committed by Android (Google) Code Review
Browse files

Merge "Partial Screen Sharing: use correct icon badges in recent apps selector" into main

parents 2f3debc0 318a65d6
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -25,8 +25,8 @@ import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
import com.android.systemui.mediaprojection.appselector.data.IconLoaderLibAppIconLoader
import com.android.systemui.mediaprojection.appselector.data.BasicAppIconLoader
import com.android.systemui.mediaprojection.appselector.data.BasicPackageManagerAppIconLoader
import com.android.systemui.mediaprojection.appselector.data.RecentTaskLabelLoader
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
@@ -102,7 +102,7 @@ interface MediaProjectionAppSelectorModule {

    @Binds
    @MediaProjectionAppSelectorScope
    fun bindAppIconLoader(impl: IconLoaderLibAppIconLoader): AppIconLoader
    fun bindAppIconLoader(impl: BasicPackageManagerAppIconLoader): BasicAppIconLoader

    @Binds
    @IntoSet
+68 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.mediaprojection.appselector.data

import android.content.ComponentName
import android.content.Context
import android.graphics.drawable.Drawable
import android.os.UserHandle
import com.android.launcher3.icons.BaseIconFactory
import com.android.launcher3.icons.IconFactory
import com.android.launcher3.util.UserIconInfo
import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext

class BadgedAppIconLoader
@Inject
constructor(
    private val basicAppIconLoader: BasicAppIconLoader,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
    private val context: Context,
    private val iconFactoryProvider: Provider<IconFactory>,
) {

    suspend fun loadIcon(
        userId: Int,
        userType: RecentTask.UserType,
        componentName: ComponentName
    ): Drawable? =
        withContext(backgroundDispatcher) {
            iconFactoryProvider.get().use<IconFactory, Drawable?> { iconFactory ->
                val icon =
                    basicAppIconLoader.loadIcon(userId, componentName) ?: return@withContext null
                val userHandler = UserHandle.of(userId)
                val iconType = getIconType(userType)
                val options =
                    BaseIconFactory.IconOptions().apply {
                        setUser(UserIconInfo(userHandler, iconType))
                    }
                val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options)
                badgedIcon.newIcon(context)
            }
        }

    private fun getIconType(userType: RecentTask.UserType): Int =
        when (userType) {
            RecentTask.UserType.CLONED -> UserIconInfo.TYPE_CLONED
            RecentTask.UserType.WORK -> UserIconInfo.TYPE_WORK
            RecentTask.UserType.PRIVATE -> UserIconInfo.TYPE_PRIVATE
            RecentTask.UserType.STANDARD -> UserIconInfo.TYPE_MAIN
        }
}
+4 −20
Original line number Diff line number Diff line
@@ -17,45 +17,29 @@
package com.android.systemui.mediaprojection.appselector.data

import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.os.UserHandle
import com.android.launcher3.icons.BaseIconFactory.IconOptions
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shared.system.PackageManagerWrapper
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext

interface AppIconLoader {
interface BasicAppIconLoader {
    suspend fun loadIcon(userId: Int, component: ComponentName): Drawable?
}

class IconLoaderLibAppIconLoader
class BasicPackageManagerAppIconLoader
@Inject
constructor(
    @Background private val backgroundDispatcher: CoroutineDispatcher,
    private val context: Context,
    // Use wrapper to access hidden API that allows to get ActivityInfo for any user id
    private val packageManagerWrapper: PackageManagerWrapper,
    private val packageManager: PackageManager,
    private val iconFactoryProvider: Provider<IconFactory>
) : AppIconLoader {
) : BasicAppIconLoader {

    override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? =
        withContext(backgroundDispatcher) {
            iconFactoryProvider.get().use<IconFactory, Drawable?> { iconFactory ->
                val activityInfo =
                    packageManagerWrapper.getActivityInfo(component, userId)
                        ?: return@withContext null
                val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null
                val userHandler = UserHandle.of(userId)
                val options = IconOptions().apply { setUser(userHandler) }
                val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options)
                badgedIcon.newIcon(context)
            }
            packageManagerWrapper.getActivityInfo(component, userId)?.loadIcon(packageManager)
        }
}
+9 −1
Original line number Diff line number Diff line
@@ -28,4 +28,12 @@ data class RecentTask(
    val baseIntentComponent: ComponentName?,
    @ColorInt val colorBackground: Int?,
    val isForegroundTask: Boolean,
)
    val userType: UserType,
) {
    enum class UserType {
        STANDARD,
        WORK,
        PRIVATE,
        CLONED
    }
}
+17 −2
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.systemui.mediaprojection.appselector.data

import android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE
import android.content.pm.UserInfo
import android.os.UserManager
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.kotlin.getOrNull
@@ -41,7 +43,8 @@ constructor(
    @Background private val coroutineDispatcher: CoroutineDispatcher,
    @Background private val backgroundExecutor: Executor,
    private val recentTasks: Optional<RecentTasks>,
    private val userTracker: UserTracker
    private val userTracker: UserTracker,
    private val userManager: UserManager,
) : RecentTaskListProvider {

    private val recents by lazy { recentTasks.getOrNull() }
@@ -65,7 +68,8 @@ constructor(
                        it.topActivity,
                        it.baseIntent?.component,
                        it.taskDescription?.backgroundColor,
                        isForegroundTask = it.taskId in foregroundTaskIds && it.isVisible
                        isForegroundTask = it.taskId in foregroundTaskIds && it.isVisible,
                        userType = userManager.getUserInfo(it.userId).toUserType(),
                    )
                }
        }
@@ -81,4 +85,15 @@ constructor(
                continuation.resume(tasks)
            }
        }

    private fun UserInfo.toUserType(): RecentTask.UserType =
        if (isCloneProfile) {
            RecentTask.UserType.CLONED
        } else if (isManagedProfile) {
            RecentTask.UserType.WORK
        } else if (isPrivateProfile) {
            RecentTask.UserType.PRIVATE
        } else {
            RecentTask.UserType.STANDARD
        }
}
Loading