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

Commit 430e9fba authored by Sunny Goyal's avatar Sunny Goyal Committed by Android (Google) Code Review
Browse files

Merge "Cleaning up some synchronous event registration in Views" into udc-qpr-dev

parents 3975b984 c9b86af7
Loading
Loading
Loading
Loading
+2 −39
Original line number Diff line number Diff line
@@ -16,10 +16,7 @@

package android.widget;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.os.Message;
import android.util.AttributeSet;
@@ -48,7 +45,6 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
    private boolean mRunning = false;
    private boolean mStarted = false;
    private boolean mVisible = false;
    private boolean mUserPresent = true;
    private boolean mAdvancedByHost = false;

    public AdapterViewFlipper(Context context) {
@@ -82,40 +78,10 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
        a.recycle();
    }

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                mUserPresent = false;
                updateRunning();
            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                mUserPresent = true;
                updateRunning(false);
            }
        }
    };

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        // Listen for broadcasts related to user-presence
        final IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_PRESENT);

        // OK, this is gross but needed. This class is supported by the
        // remote views machanism and as a part of that the remote views
        // can be inflated by a context for another user without the app
        // having interact users permission - just for loading resources.
        // For exmaple, when adding widgets from a user profile to the
        // home screen. Therefore, we register the receiver as the current
        // user not the one the context is for.
        getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
                filter, null, getHandler());


        if (mAutoStart) {
            // Automatically start when requested
            startFlipping();
@@ -126,8 +92,6 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mVisible = false;

        getContext().unregisterReceiver(mReceiver);
        updateRunning();
    }

@@ -235,8 +199,7 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
     *            true.
     */
    private void updateRunning(boolean flipNow) {
        boolean running = !mAdvancedByHost && mVisible && mStarted && mUserPresent
                && mAdapter != null;
        boolean running = !mAdvancedByHost && mVisible && mStarted && mAdapter != null;
        if (running != mRunning) {
            if (running) {
                showOnly(mWhichChild, flipNow);
@@ -248,7 +211,7 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
        }
        if (LOGD) {
            Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
                    + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
                    + ", mRunning=" + mRunning);
        }
    }

+18 −15
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.BlendMode;
@@ -37,6 +36,9 @@ import android.view.RemotableViewMethod;
import android.view.View;
import android.view.inspector.InspectableProperty;
import android.widget.RemoteViews.RemoteView;
import android.widget.TextClock.ClockEventDelegate;

import com.android.internal.util.Preconditions;

import java.time.Clock;
import java.time.DateTimeException;
@@ -112,6 +114,7 @@ public class AnalogClock extends View {
    public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        mClockEventDelegate = new ClockEventDelegate(context);
        mSecondsHandFps = AppGlobals.getIntCoreSetting(
                WidgetFlags.KEY_ANALOG_CLOCK_SECONDS_HAND_FPS,
                context.getResources()
@@ -584,21 +587,9 @@ public class AnalogClock extends View {
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        IntentFilter filter = new IntentFilter();

        if (!mReceiverAttached) {
            filter.addAction(Intent.ACTION_TIME_CHANGED);
            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);

            // OK, this is gross but needed. This class is supported by the
            // remote views mechanism and as a part of that the remote views
            // can be inflated by a context for another user without the app
            // having interact users permission - just for loading resources.
            // For example, when adding widgets from a user profile to the
            // home screen. Therefore, we register the receiver as the current
            // user not the one the context is for.
            getContext().registerReceiverAsUser(mIntentReceiver,
                    android.os.Process.myUserHandle(), filter, null, getHandler());
            mClockEventDelegate.registerTimeChangeReceiver(mIntentReceiver, getHandler());
            mReceiverAttached = true;
        }

@@ -615,12 +606,23 @@ public class AnalogClock extends View {
    @Override
    protected void onDetachedFromWindow() {
        if (mReceiverAttached) {
            getContext().unregisterReceiver(mIntentReceiver);
            mClockEventDelegate.unregisterTimeChangeReceiver(mIntentReceiver);
            mReceiverAttached = false;
        }
        super.onDetachedFromWindow();
    }

    /**
     * Sets a delegate to handle clock event registration. This must be called before the view is
     * attached to the window
     *
     * @hide
     */
    public void setClockEventDelegate(ClockEventDelegate delegate) {
        Preconditions.checkState(!mReceiverAttached, "Clock events already registered");
        mClockEventDelegate = delegate;
    }

    private void onVisible() {
        if (!mVisible) {
            mVisible = true;
@@ -797,6 +799,7 @@ public class AnalogClock extends View {
        }
    };
    private boolean mReceiverAttached;
    private ClockEventDelegate mClockEventDelegate;

    private final Runnable mTick = new Runnable() {
        @Override
+85 −45
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.widget;

import static android.os.Process.myUserHandle;
import static android.view.ViewDebug.ExportedProperty;
import static android.widget.RemoteViews.RemoteView;

@@ -24,7 +25,6 @@ import android.annotation.TestApi;
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -43,6 +43,7 @@ import android.view.ViewHierarchyEncoder;
import android.view.inspector.InspectableProperty;

import com.android.internal.R;
import com.android.internal.util.Preconditions;

import java.time.Duration;
import java.time.Instant;
@@ -141,6 +142,8 @@ public class TextClock extends TextView {
    private boolean mRegistered;
    private boolean mShouldRunTicker;

    private ClockEventDelegate mClockEventDelegate;

    private Calendar mTime;
    private String mTimeZone;

@@ -178,8 +181,7 @@ public class TextClock extends TextView {
            if (mTimeZone == null && Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
                final String timeZone = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
                createTime(timeZone);
            } else if (!mShouldRunTicker && (Intent.ACTION_TIME_TICK.equals(intent.getAction())
                    || Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))) {
            } else if (!mShouldRunTicker && Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
                return;
            }
            onTimeChanged();
@@ -282,6 +284,7 @@ public class TextClock extends TextView {
        if (mFormat24 == null) {
            mFormat24 = getBestDateTimePattern("Hm");
        }
        mClockEventDelegate = new ClockEventDelegate(getContext());

        createTime(mTimeZone);
        chooseFormat();
@@ -430,6 +433,17 @@ public class TextClock extends TextView {
        registerObserver();
    }

    /**
     * Sets a delegate to handle clock event registration. This must be called before the view is
     * attached to the window
     *
     * @hide
     */
    public void setClockEventDelegate(ClockEventDelegate delegate) {
        Preconditions.checkState(!mRegistered, "Clock events already registered");
        mClockEventDelegate = delegate;
    }

    /**
     * Update the displayed time if necessary and invalidate the view.
     */
@@ -557,7 +571,7 @@ public class TextClock extends TextView {
        if (!mRegistered) {
            mRegistered = true;

            registerReceiver();
            mClockEventDelegate.registerTimeChangeReceiver(mIntentReceiver, getHandler());
            registerObserver();

            createTime(mTimeZone);
@@ -582,7 +596,7 @@ public class TextClock extends TextView {
        super.onDetachedFromWindow();

        if (mRegistered) {
            unregisterReceiver();
            mClockEventDelegate.unregisterTimeChangeReceiver(mIntentReceiver);
            unregisterObserver();

            mRegistered = false;
@@ -598,34 +612,11 @@ public class TextClock extends TextView {
        mStopTicking = true;
    }

    private void registerReceiver() {
        final IntentFilter filter = new IntentFilter();

        filter.addAction(Intent.ACTION_TIME_CHANGED);
        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);

        // OK, this is gross but needed. This class is supported by the
        // remote views mechanism and as a part of that the remote views
        // can be inflated by a context for another user without the app
        // having interact users permission - just for loading resources.
        // For example, when adding widgets from a managed profile to the
        // home screen. Therefore, we register the receiver as the user
        // the app is running as not the one the context is for.
        getContext().registerReceiverAsUser(mIntentReceiver, android.os.Process.myUserHandle(),
                filter, null, getHandler());
    }

    private void registerObserver() {
        if (mRegistered) {
            if (mFormatChangeObserver == null) {
                mFormatChangeObserver = new FormatChangeObserver(getHandler());
            }
            final ContentResolver resolver = getContext().getContentResolver();
            Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
            if (mShowCurrentUserTime) {
                resolver.registerContentObserver(uri, true,
                        mFormatChangeObserver, UserHandle.USER_ALL);
            } else {
            // UserHandle.myUserId() is needed. This class is supported by the
            // remote views mechanism and as a part of that the remote views
            // can be inflated by a context for another user without the app
@@ -634,20 +625,14 @@ public class TextClock extends TextView {
            // home screen. Therefore, we register the ContentObserver with the user
            // the app is running (e.g. the launcher) and not the user of the
            // context (e.g. the widget's profile).
                resolver.registerContentObserver(uri, true,
                        mFormatChangeObserver, UserHandle.myUserId());
            int userHandle = mShowCurrentUserTime ? UserHandle.USER_ALL : UserHandle.myUserId();
            mClockEventDelegate.registerFormatChangeObserver(mFormatChangeObserver, userHandle);
        }
    }
    }

    private void unregisterReceiver() {
        getContext().unregisterReceiver(mIntentReceiver);
    }

    private void unregisterObserver() {
        if (mFormatChangeObserver != null) {
            final ContentResolver resolver = getContext().getContentResolver();
            resolver.unregisterContentObserver(mFormatChangeObserver);
            mClockEventDelegate.unregisterFormatChangeObserver(mFormatChangeObserver);
        }
    }

@@ -674,4 +659,59 @@ public class TextClock extends TextView {
        stream.addProperty("format", mFormat == null ? null : mFormat.toString());
        stream.addProperty("hasSeconds", mHasSeconds);
    }

    /**
     * Utility class to delegate some system event handling to allow overring the default behavior
     *
     * @hide
     */
    public static class ClockEventDelegate {

        private final Context mContext;

        public ClockEventDelegate(Context context) {
            mContext = context;
        }

        /**
         * Registers a receiver for actions {@link Intent#ACTION_TIME_CHANGED} and
         * {@link Intent#ACTION_TIMEZONE_CHANGED}
         *
         * OK, this is gross but needed. This class is supported by the remote views mechanism and
         * as a part of that the remote views can be inflated by a context for another user without
         * the app having interact users permission - just for loading resources. For example,
         * when adding widgets from a managed profile to the home screen. Therefore, we register
         * the receiver as the user the app is running as not the one the context is for.
         */
        public void registerTimeChangeReceiver(BroadcastReceiver receiver, Handler handler) {
            final IntentFilter filter = new IntentFilter();

            filter.addAction(Intent.ACTION_TIME_CHANGED);
            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);

            mContext.registerReceiverAsUser(receiver, myUserHandle(), filter, null, handler);
        }

        /**
         * Unregisters a previously registered receiver
         */
        public void unregisterTimeChangeReceiver(BroadcastReceiver receiver) {
            mContext.unregisterReceiver(receiver);
        }

        /**
         * Registers an observer for time format changes
         */
        public void registerFormatChangeObserver(ContentObserver observer, int userHandle) {
            Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
            mContext.getContentResolver().registerContentObserver(uri, true, observer, userHandle);
        }

        /**
         * Unregisters a previously registered observer
         */
        public void unregisterFormatChangeObserver(ContentObserver observer) {
            mContext.getContentResolver().unregisterContentObserver(observer);
        }
    }
}
+2 −38
Original line number Diff line number Diff line
@@ -18,10 +18,7 @@ package android.widget;

import android.annotation.IntRange;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Message;
@@ -51,8 +48,6 @@ public class ViewFlipper extends ViewAnimator {
    private boolean mRunning = false;
    private boolean mStarted = false;
    private boolean mVisible = false;
    @UnsupportedAppUsage
    private boolean mUserPresent = true;

    public ViewFlipper(Context context) {
        super(context);
@@ -70,39 +65,10 @@ public class ViewFlipper extends ViewAnimator {
        a.recycle();
    }

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                mUserPresent = false;
                updateRunning();
            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                mUserPresent = true;
                updateRunning(false);
            }
        }
    };

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        // Listen for broadcasts related to user-presence
        final IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_PRESENT);

        // OK, this is gross but needed. This class is supported by the
        // remote views machanism and as a part of that the remote views
        // can be inflated by a context for another user without the app
        // having interact users permission - just for loading resources.
        // For exmaple, when adding widgets from a user profile to the
        // home screen. Therefore, we register the receiver as the current
        // user not the one the context is for.
        getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
                filter, null, getHandler());

        if (mAutoStart) {
            // Automatically start when requested
            startFlipping();
@@ -113,8 +79,6 @@ public class ViewFlipper extends ViewAnimator {
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mVisible = false;

        getContext().unregisterReceiver(mReceiver);
        updateRunning();
    }

@@ -186,7 +150,7 @@ public class ViewFlipper extends ViewAnimator {
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private void updateRunning(boolean flipNow) {
        boolean running = mVisible && mStarted && mUserPresent;
        boolean running = mVisible && mStarted;
        if (running != mRunning) {
            if (running) {
                showOnly(mWhichChild, flipNow);
@@ -198,7 +162,7 @@ public class ViewFlipper extends ViewAnimator {
        }
        if (LOGD) {
            Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
                    + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
                    + ", mRunning=" + mRunning);
        }
    }