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