Merge "Fix an issue with concurrent modification on widget holders list" into tm-qpr-dev
This commit is contained in:
commit
f22345eace
|
@ -22,6 +22,7 @@ import android.appwidget.AppWidgetHost;
|
|||
import android.appwidget.AppWidgetHostView;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
|
@ -33,14 +34,14 @@ import androidx.annotation.WorkerThread;
|
|||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.model.WidgetsModel;
|
||||
import com.android.launcher3.util.IntSet;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHostView;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.widget.LauncherWidgetHolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
@ -50,6 +51,8 @@ import java.util.function.IntConsumer;
|
|||
*/
|
||||
public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
||||
|
||||
private static final String TAG = "QuickstepWidgetHolder";
|
||||
|
||||
private static final UpdateKey<AppWidgetProviderInfo> KEY_PROVIDER_UPDATE =
|
||||
AppWidgetHostView::onUpdateProviderInfo;
|
||||
private static final UpdateKey<RemoteViews> KEY_VIEWS_UPDATE =
|
||||
|
@ -63,6 +66,8 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
|||
|
||||
private static AppWidgetHost sWidgetHost = null;
|
||||
|
||||
private final SparseArray<AppWidgetHostView> mViews = new SparseArray<>();
|
||||
|
||||
private final @Nullable RemoteViews.InteractionHandler mInteractionHandler;
|
||||
|
||||
private final @NonNull IntConsumer mAppWidgetRemovedCallback;
|
||||
|
@ -71,15 +76,14 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
|||
// Map to all pending updated keyed with appWidgetId;
|
||||
private final SparseArray<PendingUpdate> mPendingUpdateMap = new SparseArray<>();
|
||||
|
||||
@Thunk
|
||||
QuickstepWidgetHolder(@NonNull Context context,
|
||||
private QuickstepWidgetHolder(@NonNull Context context,
|
||||
@Nullable IntConsumer appWidgetRemovedCallback,
|
||||
@Nullable RemoteViews.InteractionHandler interactionHandler) {
|
||||
super(context, appWidgetRemovedCallback);
|
||||
mAppWidgetRemovedCallback = appWidgetRemovedCallback != null ? appWidgetRemovedCallback
|
||||
: i -> {};
|
||||
mInteractionHandler = interactionHandler;
|
||||
sHolders.add(this);
|
||||
MAIN_EXECUTOR.execute(() -> sHolders.add(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,7 +96,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
|||
sHolders.forEach(h -> h.mAppWidgetRemovedCallback.accept(i))),
|
||||
() -> MAIN_EXECUTOR.execute(() ->
|
||||
sHolders.forEach(h -> h.mProviderChangedListeners.forEach(
|
||||
ProviderChangedListener::notifyWidgetProvidersChanged))),
|
||||
ProviderChangedListener::notifyWidgetProvidersChanged))),
|
||||
UI_HELPER_EXECUTOR.getLooper());
|
||||
if (!WidgetsModel.GO_DISABLE_WIDGETS) {
|
||||
sWidgetHost.startListening();
|
||||
|
@ -107,11 +111,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
|||
int count = mPendingUpdateMap.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
int widgetId = mPendingUpdateMap.keyAt(i);
|
||||
QuickstepWidgetHolderListener listener = sListeners.get(widgetId);
|
||||
if (listener == null) {
|
||||
continue;
|
||||
}
|
||||
AppWidgetHostView view = listener.mView.get(this);
|
||||
AppWidgetHostView view = mViews.get(widgetId);
|
||||
if (view == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -131,7 +131,16 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
|||
mPendingUpdateMap.clear();
|
||||
}
|
||||
|
||||
private <T> void addPendingAction(int widgetId, UpdateKey<T> key, T data) {
|
||||
private <T> void onWidgetUpdate(int widgetId, UpdateKey<T> key, T data) {
|
||||
if (isListening()) {
|
||||
AppWidgetHostView view = mViews.get(widgetId);
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
key.accept(view, data);
|
||||
return;
|
||||
}
|
||||
|
||||
PendingUpdate pendingUpdate = mPendingUpdateMap.get(widgetId);
|
||||
if (pendingUpdate == null) {
|
||||
pendingUpdate = new PendingUpdate();
|
||||
|
@ -167,7 +176,11 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
|||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
sHolders.remove(this);
|
||||
try {
|
||||
MAIN_EXECUTOR.submit(() -> sHolders.remove(this)).get();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to remove self from holder list", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -228,15 +241,16 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
|||
}
|
||||
widgetView.setInteractionHandler(mInteractionHandler);
|
||||
widgetView.setAppWidget(appWidgetId, appWidget);
|
||||
mViews.put(appWidgetId, widgetView);
|
||||
|
||||
QuickstepWidgetHolderListener listener = sListeners.get(appWidgetId);
|
||||
if (listener == null) {
|
||||
listener = new QuickstepWidgetHolderListener(appWidgetId, this, widgetView);
|
||||
listener = new QuickstepWidgetHolderListener(appWidgetId);
|
||||
sWidgetHost.setListener(appWidgetId, listener);
|
||||
sListeners.put(appWidgetId, listener);
|
||||
} else {
|
||||
listener.resetView(this, widgetView);
|
||||
}
|
||||
RemoteViews remoteViews = listener.addHolder(this);
|
||||
widgetView.updateAppWidget(remoteViews);
|
||||
|
||||
return widgetView;
|
||||
}
|
||||
|
@ -247,31 +261,30 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
|||
@Override
|
||||
public void clearViews() {
|
||||
for (int i = sListeners.size() - 1; i >= 0; i--) {
|
||||
sListeners.valueAt(i).mView.remove(this);
|
||||
sListeners.valueAt(i).mListeningHolders.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static class QuickstepWidgetHolderListener
|
||||
implements AppWidgetHost.AppWidgetHostListener {
|
||||
|
||||
@NonNull
|
||||
private final Map<QuickstepWidgetHolder, AppWidgetHostView> mView = new WeakHashMap<>();
|
||||
// Static listeners should use a set that is backed by WeakHashMap to avoid memory leak
|
||||
private final Set<QuickstepWidgetHolder> mListeningHolders = Collections.newSetFromMap(
|
||||
new WeakHashMap<>());
|
||||
|
||||
private final int mWidgetId;
|
||||
|
||||
@Nullable private RemoteViews mRemoteViews = null;
|
||||
private @Nullable RemoteViews mRemoteViews;
|
||||
|
||||
QuickstepWidgetHolderListener(int widgetId, @NonNull QuickstepWidgetHolder holder,
|
||||
@NonNull LauncherAppWidgetHostView view) {
|
||||
QuickstepWidgetHolderListener(int widgetId) {
|
||||
mWidgetId = widgetId;
|
||||
mView.put(holder, view);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void resetView(@NonNull QuickstepWidgetHolder holder,
|
||||
@NonNull AppWidgetHostView view) {
|
||||
mView.put(holder, view);
|
||||
view.updateAppWidget(mRemoteViews);
|
||||
@Nullable
|
||||
public RemoteViews addHolder(@NonNull QuickstepWidgetHolder holder) {
|
||||
mListeningHolders.add(holder);
|
||||
return mRemoteViews;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -295,13 +308,8 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
|
|||
}
|
||||
|
||||
private <T> void executeOnMainExecutor(UpdateKey<T> key, T data) {
|
||||
MAIN_EXECUTOR.execute(() -> mView.forEach((holder, view) -> {
|
||||
if (holder.isListening()) {
|
||||
key.accept(view, data);
|
||||
} else {
|
||||
holder.addPendingAction(mWidgetId, key, data);
|
||||
}
|
||||
}));
|
||||
MAIN_EXECUTOR.execute(() -> mListeningHolders.forEach(holder ->
|
||||
holder.onWidgetUpdate(mWidgetId, key, data)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue