Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a6aa1c94 authored by Sunny Goyal's avatar Sunny Goyal Committed by Automerger Merge Worker
Browse files

Overriding the default TextClock and AnalogClock behavior to avoid RPCs on...

Overriding the default TextClock and AnalogClock behavior to avoid RPCs on main thread am: 5b861628

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/24795935



Change-Id: I9f4d5a9eb2590951fbe4091ff8050ad72f37bbd4
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents e136b6a6 5b861628
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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.
@@ -478,6 +484,10 @@ public class QuickstepLauncher extends Launcher {
            mSplitSelectStateController.onDestroy();
        }

        if (mAsyncClockEventDelegate != null) {
            mAsyncClockEventDelegate.onDestroy();
        }

        super.onDestroy();
        mHotseatPredictionController.destroy();
        mSplitWithKeyboardShortcutController.onDestroy();
@@ -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);
    }
}
+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));
    }
}