1 /* 2 * Copyright (C) 2024 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.intentresolver 18 19 import android.app.Activity 20 import android.os.UserHandle 21 import android.util.Log 22 import androidx.activity.ComponentActivity 23 import androidx.activity.viewModels 24 import androidx.lifecycle.DefaultLifecycleObserver 25 import androidx.lifecycle.Lifecycle 26 import androidx.lifecycle.LifecycleOwner 27 import com.android.intentresolver.annotation.JavaInterop 28 import com.android.intentresolver.domain.interactor.UserInteractor 29 import com.android.intentresolver.inject.Background 30 import com.android.intentresolver.ui.model.ResolverRequest 31 import com.android.intentresolver.ui.viewmodel.ResolverViewModel 32 import com.android.intentresolver.validation.Invalid 33 import com.android.intentresolver.validation.Valid 34 import com.android.intentresolver.validation.log 35 import dagger.hilt.android.scopes.ActivityScoped 36 import javax.inject.Inject 37 import kotlinx.coroutines.CoroutineDispatcher 38 39 private const val TAG: String = "ResolverHelper" 40 41 /** 42 * __Purpose__ 43 * 44 * Cleanup aid. Provides a pathway to cleaner code. 45 * 46 * __Incoming References__ 47 * 48 * ResolverHelper must not expose any properties or functions directly back to ResolverActivity. If 49 * a value or operation is required by ResolverActivity, then it must be added to 50 * ResolverInitializer (or a new interface as appropriate) with ResolverActivity supplying a 51 * callback to receive it at the appropriate point. This enforces unidirectional control flow. 52 * 53 * __Outgoing References__ 54 * 55 * _ResolverActivity_ 56 * 57 * This class must only reference it's host as Activity/ComponentActivity; no down-cast to 58 * [ResolverActivity]. Other components should be created here or supplied via Injection, and not 59 * referenced directly from the activity. This prevents circular dependencies from forming. If 60 * necessary, during cleanup the dependency can be supplied back to ChooserActivity as described 61 * above in 'Incoming References', see [ResolverInitializer]. 62 * 63 * _Elsewhere_ 64 * 65 * Where possible, Singleton and ActivityScoped dependencies should be injected here instead of 66 * referenced from an existing location. If not available for injection, the value should be 67 * constructed here, then provided to where it is needed. 68 */ 69 @ActivityScoped 70 @JavaInterop 71 class ResolverHelper 72 @Inject 73 constructor( 74 hostActivity: Activity, 75 private val userInteractor: UserInteractor, 76 @Background private val background: CoroutineDispatcher, 77 ) : DefaultLifecycleObserver { 78 // This is guaranteed by Hilt, since only a ComponentActivity is injectable. 79 private val activity: ComponentActivity = hostActivity as ComponentActivity 80 private val viewModel by activity.viewModels<ResolverViewModel>() 81 82 private lateinit var activityInitializer: Runnable 83 84 init { 85 activity.lifecycle.addObserver(this) 86 } 87 88 /** 89 * Set the initialization hook for the host activity. 90 * 91 * This _must_ be called from [ResolverActivity.onCreate]. 92 */ setInitializernull93 fun setInitializer(initializer: Runnable) { 94 if (activity.lifecycle.currentState != Lifecycle.State.INITIALIZED) { 95 error("setInitializer must be called before onCreate returns") 96 } 97 activityInitializer = initializer 98 } 99 100 /** Invoked by Lifecycle, after Activity.onCreate() _returns_. */ onCreatenull101 override fun onCreate(owner: LifecycleOwner) { 102 Log.i(TAG, "CREATE") 103 Log.i(TAG, "${viewModel.activityModel}") 104 105 val callerUid: Int = viewModel.activityModel.launchedFromUid 106 if (callerUid < 0 || UserHandle.isIsolated(callerUid)) { 107 Log.e(TAG, "Can't start a resolver from uid $callerUid") 108 activity.finish() 109 return 110 } 111 112 when (val request = viewModel.initialRequest) { 113 is Valid -> initializeActivity(request) 114 is Invalid -> reportErrorsAndFinish(request) 115 } 116 } 117 reportErrorsAndFinishnull118 private fun reportErrorsAndFinish(request: Invalid<ResolverRequest>) { 119 request.errors.forEach { it.log(TAG) } 120 activity.finish() 121 } 122 initializeActivitynull123 private fun initializeActivity(request: Valid<ResolverRequest>) { 124 Log.d(TAG, "initializeActivity") 125 request.warnings.forEach { it.log(TAG) } 126 127 activityInitializer.run() 128 } 129 } 130