Creates ChooserHelper, a peer class for code cleanup

This is a peer class to ChooserActivity to incrementally assume the
tasks of initialization. Code moved through this class will have
carefully controlled rules (documented in the class) for control and
data flow to prevent additional tangles.

This commit makes only a single control flow change, forwarding the
call to ChooserActivity#init through this class. This makes no change
to functionality yet, but provides a hook for following CLs.

Bug: 309960444
Test: atest com.android.intentresolver
Change-Id: I4d895adb00a09a9d18117639b2d85e3fe880e067
diff --git a/java/src/com/android/intentresolver/v2/ChooserActivity.java b/java/src/com/android/intentresolver/v2/ChooserActivity.java
index 3d8bfac..3c9a424 100644
--- a/java/src/com/android/intentresolver/v2/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/v2/ChooserActivity.java
@@ -273,6 +273,7 @@
     private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1;
     private static final int SCROLL_STATUS_SCROLLING_HORIZONTAL = 2;
 
+    @Inject public ChooserHelper mChooserHelper;
     @Inject public ActivityModel mActivityModel;
     @Inject public FeatureFlags mFeatureFlags;
     @Inject public android.service.chooser.FeatureFlags mChooserServiceFeatureFlags;
@@ -354,7 +355,11 @@
     protected final void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         Log.i(TAG, "onCreate");
-        Log.i(TAG, "activityLaunch=" + mActivityModel.toString());
+        Log.i(TAG, "mActivityModel=" + mActivityModel.toString());
+
+        // The postInit hook is invoked when this function returns, via Lifecycle.
+        mChooserHelper.setPostCreateCallback(this::init);
+
         int callerUid = mActivityModel.getLaunchedFromUid();
         if (callerUid < 0 || UserHandle.isIsolated(callerUid)) {
             Log.e(TAG, "Can't start a resolver from uid " + callerUid);
@@ -374,7 +379,6 @@
                     .create(mActivityModel.getLaunchedFromUid(), chosenComponentSender);
         }
         mLogic = createActivityLogic();
-        init();
     }
 
     private void init() {
diff --git a/java/src/com/android/intentresolver/v2/ChooserHelper.kt b/java/src/com/android/intentresolver/v2/ChooserHelper.kt
new file mode 100644
index 0000000..17bc273
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ChooserHelper.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.intentresolver.v2
+
+import android.app.Activity
+import androidx.activity.ComponentActivity
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import dagger.hilt.android.scopes.ActivityScoped
+import javax.inject.Inject
+
+/**
+ * __Purpose__
+ *
+ * Cleanup aid. Provides a pathway to cleaner code.
+ *
+ * __Incoming References__
+ *
+ * For use by ChooserActivity only; must not be accessed by any code outside of ChooserActivity.
+ * This prevents circular dependencies and coupling, and maintains unidirectional flow. This is
+ * important for maintaining a migration path towards healthier architecture.
+ *
+ * __Outgoing References__
+ *
+ * _ChooserActivity_
+ *
+ * This class must only reference it's host as Activity/ComponentActivity; no down-cast to
+ * [ChooserActivity]. Other components should be passed in and not pulled from other places. This
+ * prevents circular dependencies from forming.
+ *
+ * _Elsewhere_
+ *
+ * Where possible, Singleton and ActivityScoped dependencies should be injected here instead of
+ * referenced from an existing location. If not available for injection, the value should be
+ * constructed here, then provided to where it is needed. If existing objects from ChooserActivity
+ * are required, supply a factory interface which satisfies the necessary dependencies and use it
+ * during construction.
+ */
+
+@ActivityScoped
+class ChooserHelper @Inject constructor(
+    hostActivity: Activity,
+) : DefaultLifecycleObserver {
+    // This is guaranteed by Hilt, since only a ComponentActivity is injectable.
+    private val activity: ComponentActivity = hostActivity as ComponentActivity
+
+    private var activityPostCreate: Runnable? = null
+
+    init {
+        activity.lifecycle.addObserver(this)
+    }
+
+    /**
+     * Provides a optional callback to setup state which is not yet possible to do without circular
+     * dependencies or by moving more code.
+     */
+    fun setPostCreateCallback(onPostCreate: Runnable) {
+        activityPostCreate = onPostCreate
+    }
+
+    /**
+     * Invoked by Lifecycle, after Activity.onCreate() _returns_.
+     */
+    override fun onCreate(owner: LifecycleOwner) {
+        activityPostCreate?.run()
+    }
+}
\ No newline at end of file