diff options
| author | 2023-08-30 14:59:06 -0400 | |
|---|---|---|
| committer | 2023-09-11 12:57:40 -0400 | |
| commit | 5591d163172fac7e7f5786ea28adde437a841e26 (patch) | |
| tree | e7aed5cf7699ae8e7675082a6f06e989530c9e88 /java/src | |
| parent | cfc377b37c1e0164f830bbbe8933731a591e7ed7 (diff) | |
Enable dependency injection using Hilt
This includes modifications to initialization of ChooserActivity
to avoid violating scoping and order of initialization.
Bug: 299610743
Test: atest IntentResolverUnitTests
Test: atest CtsSharesheetDeviceTest
Change-Id: I6570bda272eff44b3a64eab9df38049beb9c9fcc
Diffstat (limited to 'java/src')
8 files changed, 270 insertions, 43 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index d244fa2d..1e670a21 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -25,6 +25,7 @@ import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_S import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK; import static androidx.lifecycle.LifecycleKt.getCoroutineScope; + import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET; import android.annotation.IntDef; @@ -101,6 +102,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import dagger.hilt.android.AndroidEntryPoint; + import java.io.File; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -121,7 +124,8 @@ import java.util.function.Consumer; * for example, as generated by {@see android.content.Intent#createChooser(Intent, CharSequence)}. * */ -public class ChooserActivity extends ResolverActivity implements +@AndroidEntryPoint(ResolverActivity.class) +public class ChooserActivity extends Hilt_ChooserActivity implements ResolverListAdapter.ResolverListCommunicator { private static final String TAG = "ChooserActivity"; @@ -230,8 +234,6 @@ public class ChooserActivity extends ResolverActivity implements */ private boolean mFinishWhenStopped = false; - public ChooserActivity() {} - @Override protected void onCreate(Bundle savedInstanceState) { Tracer.INSTANCE.markLaunched(); @@ -255,6 +257,34 @@ public class ChooserActivity extends ResolverActivity implements super_onCreate(null); return; } + mPinnedSharedPrefs = getPinnedSharedPrefs(this); + mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row); + mShouldDisplayLandscape = + shouldDisplayLandscape(getResources().getConfiguration().orientation); + setRetainInOnStop(mChooserRequest.shouldRetainInOnStop()); + + createProfileRecords( + new AppPredictorFactory( + getApplicationContext(), + mChooserRequest.getSharedText(), + mChooserRequest.getTargetIntentFilter()), + mChooserRequest.getTargetIntentFilter()); + + + super.onCreate( + savedInstanceState, + mChooserRequest.getTargetIntent(), + mChooserRequest.getAdditionalTargets(), + mChooserRequest.getTitle(), + mChooserRequest.getDefaultTitleResource(), + mChooserRequest.getInitialIntents(), + /* resolutionList= */ null, + /* supportsAlwaysUseOption= */ false, + new DefaultTargetDataLoader(this, getLifecycle(), false), + /* safeForwardingMode= */ true); + + mFeatureFlagRepository = createFeatureFlagRepository(); + mIntegratedDeviceComponents = getIntegratedDeviceComponents(); mRefinementManager = new ViewModelProvider(this).get(ChooserRefinementManager.class); @@ -288,31 +318,13 @@ public class ChooserActivity extends ResolverActivity implements mEnterTransitionAnimationDelegate, new HeadlineGeneratorImpl(this)); - mPinnedSharedPrefs = getPinnedSharedPrefs(this); - - mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row); - mShouldDisplayLandscape = - shouldDisplayLandscape(getResources().getConfiguration().orientation); - setRetainInOnStop(mChooserRequest.shouldRetainInOnStop()); - - createProfileRecords( - new AppPredictorFactory( - getApplicationContext(), - mChooserRequest.getSharedText(), - mChooserRequest.getTargetIntentFilter()), - mChooserRequest.getTargetIntentFilter()); - - super.onCreate( - savedInstanceState, - mChooserRequest.getTargetIntent(), - mChooserRequest.getAdditionalTargets(), - mChooserRequest.getTitle(), - mChooserRequest.getDefaultTitleResource(), - mChooserRequest.getInitialIntents(), - /* resolutionList= */ null, - /* supportsAlwaysUseOption= */ false, - new DefaultTargetDataLoader(this, getLifecycle(), false), - /* safeForwardingMode= */ true); + updateStickyContentPreview(); + if (shouldShowStickyContentPreview() + || mChooserMultiProfilePagerAdapter + .getCurrentRootAdapter().getSystemRowCount() != 0) { + getEventLog().logActionShareWithPreview( + mChooserContentPreviewUi.getPreferredContentPreview()); + } mChooserShownTime = System.currentTimeMillis(); final long systemCost = mChooserShownTime - intentReceivedTime; @@ -546,18 +558,6 @@ public class ChooserActivity extends ResolverActivity implements return selectedProfile; } - @Override - protected boolean postRebuildList(boolean rebuildCompleted) { - updateStickyContentPreview(); - if (shouldShowStickyContentPreview() - || mChooserMultiProfilePagerAdapter - .getCurrentRootAdapter().getSystemRowCount() != 0) { - getEventLog().logActionShareWithPreview( - mChooserContentPreviewUi.getPreferredContentPreview()); - } - return postRebuildListInternal(rebuildCompleted); - } - /** * Check if the profile currently used is a work profile. * @return true if it is work profile, false if it is parent profile (or no work profile is diff --git a/java/src/com/android/intentresolver/ChooserRefinementManager.java b/java/src/com/android/intentresolver/ChooserRefinementManager.java index 2ebe48a6..b3b08de7 100644 --- a/java/src/com/android/intentresolver/ChooserRefinementManager.java +++ b/java/src/com/android/intentresolver/ChooserRefinementManager.java @@ -34,16 +34,21 @@ import androidx.lifecycle.ViewModel; import com.android.intentresolver.chooser.TargetInfo; +import dagger.hilt.android.lifecycle.HiltViewModel; + import java.util.List; import java.util.function.Consumer; +import javax.inject.Inject; + /** * Helper class to manage Sharesheet's "refinement" flow, where callers supply a "refinement * activity" that will be invoked when a target is selected, allowing the calling app to add - * additional extras and other refinements (subject to {@link Intent#filterEquals()}), e.g., to + * additional extras and other refinements (subject to {@link Intent#filterEquals}), e.g., to * convert the format of the payload, or lazy-download some data that was deferred in the original * call). */ +@HiltViewModel @UiThread public final class ChooserRefinementManager extends ViewModel { private static final String TAG = "ChooserRefinement"; @@ -88,6 +93,9 @@ public final class ChooserRefinementManager extends ViewModel { private MutableLiveData<RefinementCompletion> mRefinementCompletion = new MutableLiveData<>(); + @Inject + public ChooserRefinementManager() {} + public LiveData<RefinementCompletion> getRefinementCompletion() { return mRefinementCompletion; } diff --git a/java/src/com/android/intentresolver/MainApplication.kt b/java/src/com/android/intentresolver/MainApplication.kt new file mode 100644 index 00000000..0a826629 --- /dev/null +++ b/java/src/com/android/intentresolver/MainApplication.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 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.intentresolver + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp(Application::class) open class MainApplication : Hilt_MainApplication() diff --git a/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt b/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt index 6013f5a0..b55b8b38 100644 --- a/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt +++ b/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt @@ -25,14 +25,20 @@ import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.CreationExtras import com.android.intentresolver.ChooserRequestParameters import com.android.intentresolver.R +import com.android.intentresolver.inject.Background +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.plus /** A trivial view model to keep a [PreviewDataProvider] instance over a configuration change */ -class PreviewViewModel( +@HiltViewModel +class PreviewViewModel +@Inject +constructor( private val application: Application, - private val dispatcher: CoroutineDispatcher = Dispatchers.IO, + @Background private val dispatcher: CoroutineDispatcher = Dispatchers.IO, ) : BasePreviewViewModel() { private var previewDataProvider: PreviewDataProvider? = null private var imageLoader: ImagePreviewImageLoader? = null diff --git a/java/src/com/android/intentresolver/inject/ActivityModule.kt b/java/src/com/android/intentresolver/inject/ActivityModule.kt new file mode 100644 index 00000000..21bfe4c6 --- /dev/null +++ b/java/src/com/android/intentresolver/inject/ActivityModule.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 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.intentresolver.inject + +import android.app.Activity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import kotlinx.coroutines.CoroutineScope + +@Module +@InstallIn(ActivityComponent::class) +object ActivityModule { + + @Provides + @ActivityOwned + fun lifecycle(activity: Activity): Lifecycle { + check(activity is LifecycleOwner) { "activity must implement LifecycleOwner" } + return activity.lifecycle + } + + @Provides + @ActivityOwned + fun activityScope(activity: Activity): CoroutineScope { + check(activity is LifecycleOwner) { "activity must implement LifecycleOwner" } + return activity.lifecycleScope + } +} diff --git a/java/src/com/android/intentresolver/inject/ConcurrencyModule.kt b/java/src/com/android/intentresolver/inject/ConcurrencyModule.kt new file mode 100644 index 00000000..e0f8e88b --- /dev/null +++ b/java/src/com/android/intentresolver/inject/ConcurrencyModule.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 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.intentresolver.inject + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob + +@Module +@InstallIn(SingletonComponent::class) +object ConcurrencyModule { + + @Provides @Main fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate + + /** Injectable alternative to [MainScope()][kotlinx.coroutines.MainScope] */ + @Provides + @Singleton + @Main + fun mainCoroutineScope(@Main mainDispatcher: CoroutineDispatcher) = + CoroutineScope(SupervisorJob() + mainDispatcher) + + @Provides @Background fun backgroundDispatcher(): CoroutineDispatcher = Dispatchers.IO +} diff --git a/java/src/com/android/intentresolver/inject/FrameworkModule.kt b/java/src/com/android/intentresolver/inject/FrameworkModule.kt new file mode 100644 index 00000000..39a2faf9 --- /dev/null +++ b/java/src/com/android/intentresolver/inject/FrameworkModule.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 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.intentresolver.inject + +import android.app.ActivityManager +import android.app.admin.DevicePolicyManager +import android.content.ClipboardManager +import android.content.Context +import android.content.pm.LauncherApps +import android.content.pm.PackageManager +import android.content.pm.ShortcutManager +import android.os.UserManager +import android.view.WindowManager +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent + +private fun <T> Context.requireSystemService(serviceClass: Class<T>): T { + return checkNotNull(getSystemService(serviceClass)) +} + +@Module +@InstallIn(SingletonComponent::class) +object FrameworkModule { + + @Provides fun contentResolver(@ApplicationContext ctx: Context) = ctx.contentResolver!! + + @Provides + fun activityManager(@ApplicationContext ctx: Context) = + ctx.requireSystemService(ActivityManager::class.java) + + @Provides + fun clipboardManager(@ApplicationContext ctx: Context) = + ctx.requireSystemService(ClipboardManager::class.java) + + @Provides + fun devicePolicyManager(@ApplicationContext ctx: Context) = + ctx.requireSystemService(DevicePolicyManager::class.java) + + @Provides + fun launcherApps(@ApplicationContext ctx: Context) = + ctx.requireSystemService(LauncherApps::class.java) + + @Provides + fun packageManager(@ApplicationContext ctx: Context) = + ctx.requireSystemService(PackageManager::class.java) + + @Provides + fun shortcutManager(@ApplicationContext ctx: Context) = + ctx.requireSystemService(ShortcutManager::class.java) + + @Provides + fun userManager(@ApplicationContext ctx: Context) = + ctx.requireSystemService(UserManager::class.java) + + @Provides + fun windowManager(@ApplicationContext ctx: Context) = + ctx.requireSystemService(WindowManager::class.java) +} diff --git a/java/src/com/android/intentresolver/inject/Qualifiers.kt b/java/src/com/android/intentresolver/inject/Qualifiers.kt new file mode 100644 index 00000000..2bfb1ff9 --- /dev/null +++ b/java/src/com/android/intentresolver/inject/Qualifiers.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 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.intentresolver.inject + +import javax.inject.Qualifier + +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class ActivityOwned + +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Background + +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Default + +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Main |