summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
author Mark Renouf <mrenouf@google.com> 2023-08-30 14:59:06 -0400
committer Mark Renouf <mrenouf@google.com> 2023-09-11 12:57:40 -0400
commit5591d163172fac7e7f5786ea28adde437a841e26 (patch)
treee7aed5cf7699ae8e7675082a6f06e989530c9e88 /java/src
parentcfc377b37c1e0164f830bbbe8933731a591e7ed7 (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')
-rw-r--r--java/src/com/android/intentresolver/ChooserActivity.java80
-rw-r--r--java/src/com/android/intentresolver/ChooserRefinementManager.java10
-rw-r--r--java/src/com/android/intentresolver/MainApplication.kt22
-rw-r--r--java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt10
-rw-r--r--java/src/com/android/intentresolver/inject/ActivityModule.kt46
-rw-r--r--java/src/com/android/intentresolver/inject/ConcurrencyModule.kt43
-rw-r--r--java/src/com/android/intentresolver/inject/FrameworkModule.kt75
-rw-r--r--java/src/com/android/intentresolver/inject/Qualifiers.kt27
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