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 
17 package com.android.systemui.mediaprojection.appselector
18 
19 import android.app.Activity
20 import android.content.ComponentName
21 import android.content.Context
22 import android.os.UserHandle
23 import androidx.lifecycle.DefaultLifecycleObserver
24 import com.android.launcher3.icons.IconFactory
25 import com.android.systemui.dagger.qualifiers.Application
26 import com.android.systemui.media.MediaProjectionAppSelectorActivity
27 import com.android.systemui.media.MediaProjectionAppSelectorActivity.Companion.EXTRA_HOST_APP_USER_HANDLE
28 import com.android.systemui.media.MediaProjectionPermissionActivity
29 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
30 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
31 import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
32 import com.android.systemui.mediaprojection.appselector.data.IconLoaderLibAppIconLoader
33 import com.android.systemui.mediaprojection.appselector.data.RecentTaskLabelLoader
34 import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
35 import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
36 import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider
37 import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController
38 import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider
39 import com.android.systemui.mediaprojection.devicepolicy.MediaProjectionDevicePolicyModule
40 import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile
41 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
42 import com.android.systemui.statusbar.policy.ConfigurationController
43 import dagger.Binds
44 import dagger.BindsInstance
45 import dagger.Module
46 import dagger.Provides
47 import dagger.Subcomponent
48 import dagger.multibindings.ClassKey
49 import dagger.multibindings.IntoMap
50 import dagger.multibindings.IntoSet
51 import javax.inject.Qualifier
52 import javax.inject.Scope
53 import kotlinx.coroutines.CoroutineScope
54 import kotlinx.coroutines.SupervisorJob
55 
56 @Qualifier @Retention(AnnotationRetention.BINARY) annotation class MediaProjectionAppSelector
57 
58 @Qualifier @Retention(AnnotationRetention.BINARY) annotation class HostUserHandle
59 
60 @Retention(AnnotationRetention.RUNTIME) @Scope annotation class MediaProjectionAppSelectorScope
61 
62 @Module(
63     subcomponents = [MediaProjectionAppSelectorComponent::class],
64     includes = [MediaProjectionDevicePolicyModule::class]
65 )
66 interface MediaProjectionModule {
67     @Binds
68     @IntoMap
69     @ClassKey(MediaProjectionAppSelectorActivity::class)
70     fun provideMediaProjectionAppSelectorActivity(
71         activity: MediaProjectionAppSelectorActivity
72     ): Activity
73 
74     @Binds
75     @IntoMap
76     @ClassKey(MediaProjectionPermissionActivity::class)
77     fun bindsMediaProjectionPermissionActivity(impl: MediaProjectionPermissionActivity): Activity
78 }
79 
80 /**
81  * Scoped values for [MediaProjectionAppSelectorComponent]. We create a scope for the activity so
82  * certain dependencies like [TaskPreviewSizeProvider] could be reused.
83  */
84 @Module
85 interface MediaProjectionAppSelectorModule {
86 
87     @Binds
88     @MediaProjectionAppSelectorScope
89     fun bindRecentTaskThumbnailLoader(
90         impl: ActivityTaskManagerThumbnailLoader
91     ): RecentTaskThumbnailLoader
92 
93     @Binds
94     @MediaProjectionAppSelectorScope
95     fun bindRecentTaskLabelLoader(impl: ActivityTaskManagerLabelLoader): RecentTaskLabelLoader
96 
97     @Binds
98     @MediaProjectionAppSelectorScope
99     fun bindRecentTaskListProvider(impl: ShellRecentTaskListProvider): RecentTaskListProvider
100 
101     @Binds
102     @MediaProjectionAppSelectorScope
103     fun bindAppIconLoader(impl: IconLoaderLibAppIconLoader): AppIconLoader
104 
105     @Binds
106     @IntoSet
107     fun taskPreviewSizeProviderAsLifecycleObserver(
108         impl: TaskPreviewSizeProvider
109     ): DefaultLifecycleObserver
110 
111     companion object {
112         @Provides
113         @MediaProjectionAppSelector
114         @MediaProjectionAppSelectorScope
115         fun provideAppSelectorComponentName(context: Context): ComponentName =
116             ComponentName(context, MediaProjectionAppSelectorActivity::class.java)
117 
118         @Provides
119         @MediaProjectionAppSelector
120         @MediaProjectionAppSelectorScope
121         fun provideCallerPackageName(activity: MediaProjectionAppSelectorActivity): String? =
122             activity.callingPackage
123 
124         @Provides
125         @MediaProjectionAppSelector
126         @MediaProjectionAppSelectorScope
127         fun bindConfigurationController(
128             activity: MediaProjectionAppSelectorActivity
129         ): ConfigurationController = ConfigurationControllerImpl(activity)
130 
131         @Provides
132         @HostUserHandle
133         @MediaProjectionAppSelectorScope
134         fun hostUserHandle(activity: MediaProjectionAppSelectorActivity): UserHandle {
135             val extras =
136                 activity.intent.extras
137                     ?: error("MediaProjectionAppSelectorActivity should be launched with extras")
138             return extras.getParcelable(EXTRA_HOST_APP_USER_HANDLE)
139                 ?: error(
140                     "MediaProjectionAppSelectorActivity should be provided with " +
141                         "$EXTRA_HOST_APP_USER_HANDLE extra"
142                 )
143         }
144 
145         @Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context)
146 
147         @Provides
148         @MediaProjectionAppSelector
149         @MediaProjectionAppSelectorScope
150         fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope =
151             CoroutineScope(applicationScope.coroutineContext + SupervisorJob())
152     }
153 }
154 
155 @Subcomponent(modules = [MediaProjectionAppSelectorModule::class])
156 @MediaProjectionAppSelectorScope
157 interface MediaProjectionAppSelectorComponent {
158 
159     /** Generates [MediaProjectionAppSelectorComponent]. */
160     @Subcomponent.Factory
161     interface Factory {
162         /** Create a factory to inject the activity into the graph */
163         fun create(
164             @BindsInstance activity: MediaProjectionAppSelectorActivity,
165             @BindsInstance view: MediaProjectionAppSelectorView,
166             @BindsInstance resultHandler: MediaProjectionAppSelectorResultHandler,
167         ): MediaProjectionAppSelectorComponent
168     }
169 
170     val controller: MediaProjectionAppSelectorController
171     val recentsViewController: MediaProjectionRecentsViewController
172     val emptyStateProvider: MediaProjectionBlockerEmptyStateProvider
173     @get:HostUserHandle val hostUserHandle: UserHandle
174     @get:PersonalProfile val personalProfileUserHandle: UserHandle
175 
176     @MediaProjectionAppSelector val configurationController: ConfigurationController
177     val lifecycleObservers: Set<DefaultLifecycleObserver>
178 }
179