Loading packages/SystemUI/customization/src/com/android/systemui/util/Assert.java +33 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,39 @@ public class Assert { sTestThread = thread; } /** * Run {@code mainThreadWork} synchronously, ensuring that {@link #isMainThread()} will return * {@code true} while it is running. * <ol> * <li>If {@link #isMainThread()} already passes, the work is simply run. * <li>If the test thread is {@code null}, it will be set, the work run, and then cleared. * <li>If the test thread is already set to a different thread, this call will fail the test to * avoid causing spurious errors on other thread * </ol> */ @VisibleForTesting public static void runWithCurrentThreadAsMainThread(Runnable mainThreadWork) { if (sMainLooper.isCurrentThread()) { // Already on the main thread; just run mainThreadWork.run(); return; } Thread currentThread = Thread.currentThread(); Thread originalThread = sTestThread; if (originalThread == currentThread) { // test thread is already set; just run mainThreadWork.run(); return; } if (originalThread != null) { throw new AssertionError("Can't run with current thread (" + currentThread + ") as main thread; test thread is already set to " + originalThread); } sTestThread = currentThread; mainThreadWork.run(); sTestThread = null; } public static void isMainThread() { if (!sMainLooper.isCurrentThread() && (sTestThread == null || sTestThread != Thread.currentThread())) { Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +7 −0 Original line number Diff line number Diff line Loading @@ -74,6 +74,8 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor; import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl; import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory; import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule; Loading Loading @@ -232,6 +234,11 @@ public interface NotificationsModule { @Binds NotifInflater bindNotifInflater(NotifInflaterImpl notifInflaterImpl); /** */ @Binds NotificationEntryProcessorFactory bindNotificationEntryProcessorFactory( NotificationEntryProcessorFactoryLooperImpl factoryImpl); /** */ @Binds ConversationIconManager bindConversationIconManager(IconManager iconManager); Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java +5 −33 Original line number Diff line number Diff line Loading @@ -16,9 +16,6 @@ package com.android.systemui.statusbar.notification.row; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.ArrayMap; import android.util.ArraySet; import android.widget.FrameLayout; Loading @@ -29,7 +26,6 @@ import androidx.annotation.Nullable; import androidx.core.os.CancellationSignal; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; Loading Loading @@ -82,17 +78,17 @@ public final class NotifBindPipeline { private final Map<NotificationEntry, BindEntry> mBindEntries = new ArrayMap<>(); private final NotifBindPipelineLogger mLogger; private final List<BindCallback> mScratchCallbacksList = new ArrayList<>(); private final Handler mMainHandler; private final Processor<NotificationEntry> mStartProcessor; private BindStage mStage; @Inject NotifBindPipeline( CommonNotifCollection collection, NotifBindPipelineLogger logger, @Main Looper mainLooper) { NotificationEntryProcessorFactory processorFactory) { collection.addCollectionListener(mCollectionListener); mLogger = logger; mMainHandler = new NotifBindPipelineHandler(mainLooper); mStartProcessor = processorFactory.create(this::startPipeline); } /** Loading Loading @@ -167,10 +163,7 @@ public final class NotifBindPipeline { // Abort any existing pipeline run mStage.abortStage(entry, bindEntry.row); if (!mMainHandler.hasMessages(START_PIPELINE_MSG, entry)) { Message msg = Message.obtain(mMainHandler, START_PIPELINE_MSG, entry); mMainHandler.sendMessage(msg); } mStartProcessor.request(entry); } /** Loading Loading @@ -223,7 +216,7 @@ public final class NotifBindPipeline { mStage.abortStage(entry, row); } mStage.deleteStageParams(entry); mMainHandler.removeMessages(START_PIPELINE_MSG, entry); mStartProcessor.cancel(entry); } }; Loading @@ -247,25 +240,4 @@ public final class NotifBindPipeline { public final Set<BindCallback> callbacks = new ArraySet<>(); public boolean invalidated; } private static final int START_PIPELINE_MSG = 1; private class NotifBindPipelineHandler extends Handler { NotifBindPipelineHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case START_PIPELINE_MSG: NotificationEntry entry = (NotificationEntry) msg.obj; startPipeline(entry); break; default: throw new IllegalArgumentException("Unknown message type: " + msg.what); } } } } packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationEntryProcessorFactory.kt 0 → 100644 +25 −0 Original line number Diff line number Diff line /* * 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.systemui.statusbar.notification.row import com.android.systemui.statusbar.notification.collection.NotificationEntry import java.util.function.Consumer /** an interface for [NotifBindPipeline] to build a main thread message processor */ interface NotificationEntryProcessorFactory { /** Creates a [Processor] that will call the given consumer */ fun create(consumer: Consumer<NotificationEntry>): Processor<NotificationEntry> } packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationEntryProcessorFactoryExecutorImpl.kt 0 → 100644 +56 −0 Original line number Diff line number Diff line /* * 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.systemui.statusbar.notification.row import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.util.concurrency.DelayableExecutor import java.util.concurrent.ConcurrentHashMap import java.util.function.Consumer import javax.inject.Inject class NotificationEntryProcessorFactoryExecutorImpl @Inject constructor(@Main private val mMainExecutor: DelayableExecutor) : NotificationEntryProcessorFactory { override fun create(consumer: Consumer<NotificationEntry>): Processor<NotificationEntry> { return ExecutorProcessor(mMainExecutor, consumer) } private class ExecutorProcessor( private val executor: DelayableExecutor, private val consumer: Consumer<NotificationEntry>, ) : Processor<NotificationEntry> { val cancellationsByEntry = ConcurrentHashMap<NotificationEntry, Runnable>() override fun request(obj: NotificationEntry) { cancellationsByEntry.computeIfAbsent(obj) { entry -> executor.executeDelayed({ processEntry(entry) }, 0L) } } private fun processEntry(entry: NotificationEntry) { val cancellation = cancellationsByEntry.remove(entry) if (cancellation != null) { consumer.accept(entry) } } override fun cancel(obj: NotificationEntry) { cancellationsByEntry.remove(obj)?.run() } } } Loading
packages/SystemUI/customization/src/com/android/systemui/util/Assert.java +33 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,39 @@ public class Assert { sTestThread = thread; } /** * Run {@code mainThreadWork} synchronously, ensuring that {@link #isMainThread()} will return * {@code true} while it is running. * <ol> * <li>If {@link #isMainThread()} already passes, the work is simply run. * <li>If the test thread is {@code null}, it will be set, the work run, and then cleared. * <li>If the test thread is already set to a different thread, this call will fail the test to * avoid causing spurious errors on other thread * </ol> */ @VisibleForTesting public static void runWithCurrentThreadAsMainThread(Runnable mainThreadWork) { if (sMainLooper.isCurrentThread()) { // Already on the main thread; just run mainThreadWork.run(); return; } Thread currentThread = Thread.currentThread(); Thread originalThread = sTestThread; if (originalThread == currentThread) { // test thread is already set; just run mainThreadWork.run(); return; } if (originalThread != null) { throw new AssertionError("Can't run with current thread (" + currentThread + ") as main thread; test thread is already set to " + originalThread); } sTestThread = currentThread; mainThreadWork.run(); sTestThread = null; } public static void isMainThread() { if (!sMainLooper.isCurrentThread() && (sTestThread == null || sTestThread != Thread.currentThread())) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +7 −0 Original line number Diff line number Diff line Loading @@ -74,6 +74,8 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor; import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl; import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory; import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule; Loading Loading @@ -232,6 +234,11 @@ public interface NotificationsModule { @Binds NotifInflater bindNotifInflater(NotifInflaterImpl notifInflaterImpl); /** */ @Binds NotificationEntryProcessorFactory bindNotificationEntryProcessorFactory( NotificationEntryProcessorFactoryLooperImpl factoryImpl); /** */ @Binds ConversationIconManager bindConversationIconManager(IconManager iconManager); Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java +5 −33 Original line number Diff line number Diff line Loading @@ -16,9 +16,6 @@ package com.android.systemui.statusbar.notification.row; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.ArrayMap; import android.util.ArraySet; import android.widget.FrameLayout; Loading @@ -29,7 +26,6 @@ import androidx.annotation.Nullable; import androidx.core.os.CancellationSignal; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; Loading Loading @@ -82,17 +78,17 @@ public final class NotifBindPipeline { private final Map<NotificationEntry, BindEntry> mBindEntries = new ArrayMap<>(); private final NotifBindPipelineLogger mLogger; private final List<BindCallback> mScratchCallbacksList = new ArrayList<>(); private final Handler mMainHandler; private final Processor<NotificationEntry> mStartProcessor; private BindStage mStage; @Inject NotifBindPipeline( CommonNotifCollection collection, NotifBindPipelineLogger logger, @Main Looper mainLooper) { NotificationEntryProcessorFactory processorFactory) { collection.addCollectionListener(mCollectionListener); mLogger = logger; mMainHandler = new NotifBindPipelineHandler(mainLooper); mStartProcessor = processorFactory.create(this::startPipeline); } /** Loading Loading @@ -167,10 +163,7 @@ public final class NotifBindPipeline { // Abort any existing pipeline run mStage.abortStage(entry, bindEntry.row); if (!mMainHandler.hasMessages(START_PIPELINE_MSG, entry)) { Message msg = Message.obtain(mMainHandler, START_PIPELINE_MSG, entry); mMainHandler.sendMessage(msg); } mStartProcessor.request(entry); } /** Loading Loading @@ -223,7 +216,7 @@ public final class NotifBindPipeline { mStage.abortStage(entry, row); } mStage.deleteStageParams(entry); mMainHandler.removeMessages(START_PIPELINE_MSG, entry); mStartProcessor.cancel(entry); } }; Loading @@ -247,25 +240,4 @@ public final class NotifBindPipeline { public final Set<BindCallback> callbacks = new ArraySet<>(); public boolean invalidated; } private static final int START_PIPELINE_MSG = 1; private class NotifBindPipelineHandler extends Handler { NotifBindPipelineHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case START_PIPELINE_MSG: NotificationEntry entry = (NotificationEntry) msg.obj; startPipeline(entry); break; default: throw new IllegalArgumentException("Unknown message type: " + msg.what); } } } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationEntryProcessorFactory.kt 0 → 100644 +25 −0 Original line number Diff line number Diff line /* * 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.systemui.statusbar.notification.row import com.android.systemui.statusbar.notification.collection.NotificationEntry import java.util.function.Consumer /** an interface for [NotifBindPipeline] to build a main thread message processor */ interface NotificationEntryProcessorFactory { /** Creates a [Processor] that will call the given consumer */ fun create(consumer: Consumer<NotificationEntry>): Processor<NotificationEntry> }
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationEntryProcessorFactoryExecutorImpl.kt 0 → 100644 +56 −0 Original line number Diff line number Diff line /* * 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.systemui.statusbar.notification.row import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.util.concurrency.DelayableExecutor import java.util.concurrent.ConcurrentHashMap import java.util.function.Consumer import javax.inject.Inject class NotificationEntryProcessorFactoryExecutorImpl @Inject constructor(@Main private val mMainExecutor: DelayableExecutor) : NotificationEntryProcessorFactory { override fun create(consumer: Consumer<NotificationEntry>): Processor<NotificationEntry> { return ExecutorProcessor(mMainExecutor, consumer) } private class ExecutorProcessor( private val executor: DelayableExecutor, private val consumer: Consumer<NotificationEntry>, ) : Processor<NotificationEntry> { val cancellationsByEntry = ConcurrentHashMap<NotificationEntry, Runnable>() override fun request(obj: NotificationEntry) { cancellationsByEntry.computeIfAbsent(obj) { entry -> executor.executeDelayed({ processEntry(entry) }, 0L) } } private fun processEntry(entry: NotificationEntry) { val cancellation = cancellationsByEntry.remove(entry) if (cancellation != null) { consumer.accept(entry) } } override fun cancel(obj: NotificationEntry) { cancellationsByEntry.remove(obj)?.run() } } }