1 /*
2  * Copyright (C) 2019 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 package com.android.launcher3.util;
17 
18 import android.util.Log;
19 
20 import androidx.annotation.Nullable;
21 
22 import com.android.launcher3.BaseActivity;
23 
24 import java.io.PrintWriter;
25 import java.lang.ref.WeakReference;
26 import java.util.concurrent.CopyOnWriteArrayList;
27 
28 /**
29  * Helper class to statically track activity creation
30  * @param <T> The activity type to track
31  */
32 public final class ActivityTracker<T extends BaseActivity> {
33 
34     private static final String TAG = "ActivityTracker";
35 
36     private WeakReference<T> mCurrentActivity = new WeakReference<>(null);
37     private CopyOnWriteArrayList<SchedulerCallback<T>> mCallbacks = new CopyOnWriteArrayList<>();
38 
39     @Nullable
getCreatedActivity()40     public <R extends T> R getCreatedActivity() {
41         return (R) mCurrentActivity.get();
42     }
43 
onActivityDestroyed(T activity)44     public void onActivityDestroyed(T activity) {
45         if (mCurrentActivity.get() == activity) {
46             mCurrentActivity.clear();
47         }
48     }
49 
50     /**
51      * Call {@link SchedulerCallback#init(BaseActivity, boolean)} when the
52      * activity is ready. If the activity is already created, this is called immediately.
53      *
54      * The tracker maintains a strong ref to the callback, so it is up to the caller to return
55      * {@code false} in the callback OR to unregister the callback explicitly.
56      *
57      * @param callback The callback to call init() on when the activity is ready.
58      */
registerCallback(SchedulerCallback<T> callback, String reasonString)59     public void registerCallback(SchedulerCallback<T> callback, String reasonString) {
60         Log.d(TAG, "Registering callback: " + callback + ", reason=" + reasonString);
61         T activity = mCurrentActivity.get();
62         mCallbacks.add(callback);
63         if (activity != null) {
64             if (!callback.init(activity, activity.isStarted())) {
65                 unregisterCallback(callback, "ActivityTracker.registerCallback: Intent handled");
66             }
67         }
68     }
69 
70     /**
71      * Unregisters a registered callback.
72      */
unregisterCallback(SchedulerCallback<T> callback, String reasonString)73     public void unregisterCallback(SchedulerCallback<T> callback, String reasonString) {
74         Log.d(TAG, "Unregistering callback: " + callback + ", reason=" + reasonString);
75         mCallbacks.remove(callback);
76     }
77 
handleCreate(T activity)78     public boolean handleCreate(T activity) {
79         mCurrentActivity = new WeakReference<>(activity);
80         return handleIntent(activity, false /* alreadyOnHome */);
81     }
82 
handleNewIntent(T activity)83     public boolean handleNewIntent(T activity) {
84         return handleIntent(activity, activity.isStarted());
85     }
86 
handleIntent(T activity, boolean alreadyOnHome)87     private boolean handleIntent(T activity, boolean alreadyOnHome) {
88         boolean handled = false;
89         if (!mCallbacks.isEmpty()) {
90             Log.d(TAG, "handleIntent: mCallbacks=" + mCallbacks);
91         }
92         for (SchedulerCallback<T> cb : mCallbacks) {
93             if (!cb.init(activity, alreadyOnHome)) {
94                 // Callback doesn't want any more updates
95                 unregisterCallback(cb, "ActivityTracker.handleIntent: Intent handled");
96             }
97             handled = true;
98         }
99         return handled;
100     }
101 
dump(String prefix, PrintWriter writer)102     public void dump(String prefix, PrintWriter writer) {
103         writer.println(prefix + "ActivityTracker:");
104         writer.println(prefix + "\tmCurrentActivity=" + mCurrentActivity.get());
105         writer.println(prefix + "\tmCallbacks=" + mCallbacks);
106     }
107 
108     public interface SchedulerCallback<T extends BaseActivity> {
109 
110         /**
111          * Called when the activity is ready.
112          * @param alreadyOnHome Whether the activity is already started.
113          * @return Whether to continue receiving callbacks (i.e. if the activity is recreated).
114          */
init(T activity, boolean alreadyOnHome)115         boolean init(T activity, boolean alreadyOnHome);
116     }
117 }
118