Loading quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +33 −0 Original line number Diff line number Diff line Loading @@ -73,10 +73,13 @@ import android.os.Bundle; import android.os.IBinder; import android.os.SystemProperties; import android.os.Trace; import android.util.AttributeSet; import android.util.Log; import android.view.Display; import android.view.HapticFeedbackConstants; import android.view.View; import android.widget.AnalogClock; import android.widget.TextClock; import android.window.BackEvent; import android.window.OnBackAnimationCallback; import android.window.OnBackInvokedDispatcher; Loading Loading @@ -152,6 +155,7 @@ import com.android.quickstep.RecentsModel; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskUtils; import com.android.quickstep.TouchInteractionService.TISBinder; import com.android.quickstep.util.AsyncClockEventDelegate; import com.android.quickstep.util.GroupTask; import com.android.quickstep.util.LauncherUnfoldAnimationController; import com.android.quickstep.util.QuickstepOnboardingPrefs; Loading Loading @@ -213,6 +217,8 @@ public class QuickstepLauncher extends Launcher { private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController; private SplitToWorkspaceController mSplitToWorkspaceController; private AsyncClockEventDelegate mAsyncClockEventDelegate; /** * If Launcher restarted while in the middle of an Overview split select, it needs this data to * recover. In all other cases this will remain null. Loading Loading @@ -478,6 +484,10 @@ public class QuickstepLauncher extends Launcher { mSplitSelectStateController.onDestroy(); } if (mAsyncClockEventDelegate != null) { mAsyncClockEventDelegate.onDestroy(); } super.onDestroy(); mHotseatPredictionController.destroy(); mSplitWithKeyboardShortcutController.onDestroy(); Loading Loading @@ -1305,4 +1315,27 @@ public class QuickstepLauncher extends Launcher { mHotseatPredictionController.dump(prefix, writer); } } @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { switch (name) { case "TextClock", "android.widget.TextClock" -> { TextClock tc = new TextClock(context, attrs); if (mAsyncClockEventDelegate == null) { mAsyncClockEventDelegate = new AsyncClockEventDelegate(this); } tc.setClockEventDelegate(mAsyncClockEventDelegate); return tc; } case "AnalogClock", "android.widget.AnalogClock" -> { AnalogClock ac = new AnalogClock(context, attrs); if (mAsyncClockEventDelegate == null) { mAsyncClockEventDelegate = new AsyncClockEventDelegate(this); } ac.setClockEventDelegate(mAsyncClockEventDelegate); return ac; } } return super.onCreateView(parent, name, context, attrs); } } quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.quickstep.util; import static android.content.Intent.ACTION_TIMEZONE_CHANGED; import static android.content.Intent.ACTION_TIME_CHANGED; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.provider.Settings; import android.util.ArrayMap; import android.widget.TextClock.ClockEventDelegate; import androidx.annotation.WorkerThread; import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.SettingsCache.OnChangeListener; import com.android.launcher3.util.SimpleBroadcastReceiver; import java.util.ArrayList; import java.util.List; /** * Extension of {@link ClockEventDelegate} to support async event registration */ public class AsyncClockEventDelegate extends ClockEventDelegate implements OnChangeListener { private final Context mContext; private final SimpleBroadcastReceiver mReceiver = new SimpleBroadcastReceiver(this::onClockEventReceived); private final ArrayMap<BroadcastReceiver, Handler> mTimeEventReceivers = new ArrayMap<>(); private final List<ContentObserver> mFormatObservers = new ArrayList<>(); private final Uri mFormatUri = Settings.System.getUriFor(Settings.System.TIME_12_24); private boolean mFormatRegistered = false; private boolean mDestroyed = false; public AsyncClockEventDelegate(Context context) { super(context); mContext = context; UI_HELPER_EXECUTOR.execute(() -> mReceiver.register(mContext, ACTION_TIME_CHANGED, ACTION_TIMEZONE_CHANGED)); } @Override public void registerTimeChangeReceiver(BroadcastReceiver receiver, Handler handler) { synchronized (mTimeEventReceivers) { mTimeEventReceivers.put(receiver, handler == null ? new Handler() : handler); } } @Override public void unregisterTimeChangeReceiver(BroadcastReceiver receiver) { synchronized (mTimeEventReceivers) { mTimeEventReceivers.remove(receiver); } } @Override public void registerFormatChangeObserver(ContentObserver observer, int userHandle) { synchronized (mFormatObservers) { if (!mFormatRegistered && !mDestroyed) { SettingsCache.INSTANCE.get(mContext).register(mFormatUri, this); mFormatRegistered = true; } mFormatObservers.add(observer); } } @Override public void unregisterFormatChangeObserver(ContentObserver observer) { synchronized (mFormatObservers) { mFormatObservers.remove(observer); } } @Override public void onSettingsChanged(boolean isEnabled) { if (mDestroyed) { return; } synchronized (mFormatObservers) { mFormatObservers.forEach(o -> o.dispatchChange(false, mFormatUri)); } } @WorkerThread private void onClockEventReceived(Intent intent) { if (mDestroyed) { return; } synchronized (mReceiver) { mTimeEventReceivers.forEach((r, h) -> h.post(() -> r.onReceive(mContext, intent))); } } /** * Unregisters all system callbacks and destroys this delegate */ public void onDestroy() { mDestroyed = true; SettingsCache.INSTANCE.get(mContext).unregister(mFormatUri, this); UI_HELPER_EXECUTOR.execute(() -> mReceiver.unregisterReceiverSafely(mContext)); } } Loading
quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +33 −0 Original line number Diff line number Diff line Loading @@ -73,10 +73,13 @@ import android.os.Bundle; import android.os.IBinder; import android.os.SystemProperties; import android.os.Trace; import android.util.AttributeSet; import android.util.Log; import android.view.Display; import android.view.HapticFeedbackConstants; import android.view.View; import android.widget.AnalogClock; import android.widget.TextClock; import android.window.BackEvent; import android.window.OnBackAnimationCallback; import android.window.OnBackInvokedDispatcher; Loading Loading @@ -152,6 +155,7 @@ import com.android.quickstep.RecentsModel; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskUtils; import com.android.quickstep.TouchInteractionService.TISBinder; import com.android.quickstep.util.AsyncClockEventDelegate; import com.android.quickstep.util.GroupTask; import com.android.quickstep.util.LauncherUnfoldAnimationController; import com.android.quickstep.util.QuickstepOnboardingPrefs; Loading Loading @@ -213,6 +217,8 @@ public class QuickstepLauncher extends Launcher { private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController; private SplitToWorkspaceController mSplitToWorkspaceController; private AsyncClockEventDelegate mAsyncClockEventDelegate; /** * If Launcher restarted while in the middle of an Overview split select, it needs this data to * recover. In all other cases this will remain null. Loading Loading @@ -478,6 +484,10 @@ public class QuickstepLauncher extends Launcher { mSplitSelectStateController.onDestroy(); } if (mAsyncClockEventDelegate != null) { mAsyncClockEventDelegate.onDestroy(); } super.onDestroy(); mHotseatPredictionController.destroy(); mSplitWithKeyboardShortcutController.onDestroy(); Loading Loading @@ -1305,4 +1315,27 @@ public class QuickstepLauncher extends Launcher { mHotseatPredictionController.dump(prefix, writer); } } @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { switch (name) { case "TextClock", "android.widget.TextClock" -> { TextClock tc = new TextClock(context, attrs); if (mAsyncClockEventDelegate == null) { mAsyncClockEventDelegate = new AsyncClockEventDelegate(this); } tc.setClockEventDelegate(mAsyncClockEventDelegate); return tc; } case "AnalogClock", "android.widget.AnalogClock" -> { AnalogClock ac = new AnalogClock(context, attrs); if (mAsyncClockEventDelegate == null) { mAsyncClockEventDelegate = new AsyncClockEventDelegate(this); } ac.setClockEventDelegate(mAsyncClockEventDelegate); return ac; } } return super.onCreateView(parent, name, context, attrs); } }
quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.quickstep.util; import static android.content.Intent.ACTION_TIMEZONE_CHANGED; import static android.content.Intent.ACTION_TIME_CHANGED; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.provider.Settings; import android.util.ArrayMap; import android.widget.TextClock.ClockEventDelegate; import androidx.annotation.WorkerThread; import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.SettingsCache.OnChangeListener; import com.android.launcher3.util.SimpleBroadcastReceiver; import java.util.ArrayList; import java.util.List; /** * Extension of {@link ClockEventDelegate} to support async event registration */ public class AsyncClockEventDelegate extends ClockEventDelegate implements OnChangeListener { private final Context mContext; private final SimpleBroadcastReceiver mReceiver = new SimpleBroadcastReceiver(this::onClockEventReceived); private final ArrayMap<BroadcastReceiver, Handler> mTimeEventReceivers = new ArrayMap<>(); private final List<ContentObserver> mFormatObservers = new ArrayList<>(); private final Uri mFormatUri = Settings.System.getUriFor(Settings.System.TIME_12_24); private boolean mFormatRegistered = false; private boolean mDestroyed = false; public AsyncClockEventDelegate(Context context) { super(context); mContext = context; UI_HELPER_EXECUTOR.execute(() -> mReceiver.register(mContext, ACTION_TIME_CHANGED, ACTION_TIMEZONE_CHANGED)); } @Override public void registerTimeChangeReceiver(BroadcastReceiver receiver, Handler handler) { synchronized (mTimeEventReceivers) { mTimeEventReceivers.put(receiver, handler == null ? new Handler() : handler); } } @Override public void unregisterTimeChangeReceiver(BroadcastReceiver receiver) { synchronized (mTimeEventReceivers) { mTimeEventReceivers.remove(receiver); } } @Override public void registerFormatChangeObserver(ContentObserver observer, int userHandle) { synchronized (mFormatObservers) { if (!mFormatRegistered && !mDestroyed) { SettingsCache.INSTANCE.get(mContext).register(mFormatUri, this); mFormatRegistered = true; } mFormatObservers.add(observer); } } @Override public void unregisterFormatChangeObserver(ContentObserver observer) { synchronized (mFormatObservers) { mFormatObservers.remove(observer); } } @Override public void onSettingsChanged(boolean isEnabled) { if (mDestroyed) { return; } synchronized (mFormatObservers) { mFormatObservers.forEach(o -> o.dispatchChange(false, mFormatUri)); } } @WorkerThread private void onClockEventReceived(Intent intent) { if (mDestroyed) { return; } synchronized (mReceiver) { mTimeEventReceivers.forEach((r, h) -> h.post(() -> r.onReceive(mContext, intent))); } } /** * Unregisters all system callbacks and destroys this delegate */ public void onDestroy() { mDestroyed = true; SettingsCache.INSTANCE.get(mContext).unregister(mFormatUri, this); UI_HELPER_EXECUTOR.execute(() -> mReceiver.unregisterReceiverSafely(mContext)); } }