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