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

Commit cf1c81b9 authored by Bernardo Rufino's avatar Bernardo Rufino Committed by Android (Google) Code Review
Browse files

Merge "Move text toast creation to system UI"

parents f0265e72 ab338ac4
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
package android.app;

import android.app.ITransientNotification;
import android.app.ITransientNotificationCallback;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -45,8 +46,7 @@ interface INotificationManager
    void cancelAllNotifications(String pkg, int userId);

    void clearData(String pkg, int uid, boolean fromApp);
    // TODO: Replace parameter (ITransientNotification callback) with (CharSequence text)
    void enqueueTextToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
    void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @nullable ITransientNotificationCallback callback);
    void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
    void cancelToast(String pkg, IBinder token);
    void finishToken(String pkg, IBinder token);
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020, 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 android.app;

/**
 * Callback object to be called when the associated toast is shown or hidden.
 *
 * @hide
 */
oneway interface ITransientNotificationCallback {
    void onToastShown();
    void onToastHidden();
}
+167 −45
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.widget;

import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkState;

import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -24,6 +25,9 @@ import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.INotificationManager;
import android.app.ITransientNotification;
import android.app.ITransientNotificationCallback;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
@@ -105,15 +109,45 @@ public class Toast {
     */
    public static final int LENGTH_LONG = 1;

    /**
     * Text toasts will be rendered by SystemUI instead of in-app, so apps can't circumvent
     * background custom toast restrictions.
     *
     * TODO(b/144152069): Add @EnabledAfter(Q) to target R+ after assessing impact on dogfood
     */
    @ChangeId
    // @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
    private static final long CHANGE_TEXT_TOASTS_IN_THE_SYSTEM = 147798919L;


    private final Binder mToken;
    private final Context mContext;
    private final Handler mHandler;
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
    final TN mTN;
    @UnsupportedAppUsage
    int mDuration;
    View mNextView;
    // TODO(b/128611929): Remove this and check for null view when toast creation is in the system
    boolean mIsCustomToast = false;

    /**
     * This is also passed to {@link TN} object, where it's also accessed with itself as its own
     * lock.
     */
    @GuardedBy("mCallbacks")
    private final List<Callback> mCallbacks;

    /**
     * View to be displayed, in case this is a custom toast (e.g. not created with {@link
     * #makeText(Context, int, int)} or its variants).
     */
    @Nullable
    private View mNextView;

    /**
     * Text to be shown, in case this is NOT a custom toast (e.g. created with {@link
     * #makeText(Context, int, int)} or its variants).
     */
    @Nullable
    private CharSequence mText;

    /**
     * Construct an empty Toast object.  You must call {@link #setView} before you
@@ -133,20 +167,35 @@ public class Toast {
    public Toast(@NonNull Context context, @Nullable Looper looper) {
        mContext = context;
        mToken = new Binder();
        mTN = new TN(context.getPackageName(), mToken, looper);
        looper = getLooper(looper);
        mHandler = new Handler(looper);
        mCallbacks = new ArrayList<>();
        mTN = new TN(context.getPackageName(), mToken, mCallbacks, looper);
        mTN.mY = context.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.toast_y_offset);
        mTN.mGravity = context.getResources().getInteger(
                com.android.internal.R.integer.config_toastDefaultGravity);
    }

    private Looper getLooper(@Nullable Looper looper) {
        if (looper != null) {
            return looper;
        }
        return checkNotNull(Looper.myLooper(),
                "Can't toast on a thread that has not called Looper.prepare()");
    }

    /**
     * Show the view for the specified duration.
     */
    public void show() {
        if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
            checkState(mNextView != null || mText != null, "You must either set a text or a view");
        } else {
            if (mNextView == null) {
                throw new RuntimeException("setView must have been called");
            }
        }

        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
@@ -155,10 +204,18 @@ public class Toast {
        final int displayId = mContext.getDisplayId();

        try {
            if (mIsCustomToast) {
            if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
                if (mNextView != null) {
                    // It's a custom toast
                    service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
                } else {
                service.enqueueTextToast(pkg, mToken, tn, mDuration, displayId);
                    // It's a text toast
                    ITransientNotificationCallback callback =
                            new CallbackBinder(mCallbacks, mHandler);
                    service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
                }
            } else {
                service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
            }
        } catch (RemoteException e) {
            // Empty
@@ -171,8 +228,17 @@ public class Toast {
     * after the appropriate duration.
     */
    public void cancel() {
        if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)
                && mNextView == null) {
            try {
                getService().cancelToast(mContext.getOpPackageName(), mToken);
            } catch (RemoteException e) {
                // Empty
            }
        } else {
            mTN.cancel();
        }
    }

    /**
     * Set the view to show.
@@ -187,7 +253,6 @@ public class Toast {
     */
    @Deprecated
    public void setView(View view) {
        mIsCustomToast = true;
        mNextView = view;
    }

@@ -203,7 +268,6 @@ public class Toast {
     *      will not have custom toast views displayed.
     */
    public View getView() {
        mIsCustomToast = true;
        return mNextView;
    }

@@ -298,8 +362,8 @@ public class Toast {
     */
    public void addCallback(@NonNull Callback callback) {
        checkNotNull(callback);
        synchronized (mTN.mCallbacks) {
            mTN.mCallbacks.add(callback);
        synchronized (mCallbacks) {
            mCallbacks.add(callback);
        }
    }

@@ -307,8 +371,8 @@ public class Toast {
     * Removes a callback previously added with {@link #addCallback(Callback)}.
     */
    public void removeCallback(@NonNull Callback callback) {
        synchronized (mTN.mCallbacks) {
            mTN.mCallbacks.remove(callback);
        synchronized (mCallbacks) {
            mCallbacks.remove(callback);
        }
    }

@@ -338,10 +402,17 @@ public class Toast {
    /**
     * Make a standard toast to display using the specified looper.
     * If looper is null, Looper.myLooper() is used.
     *
     * @hide
     */
    public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
            @NonNull CharSequence text, @Duration int duration) {
        if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
            Toast result = new Toast(context, looper);
            result.mText = text;
            result.mDuration = duration;
            return result;
        } else {
            Toast result = new Toast(context, looper);

            LayoutInflater inflate = (LayoutInflater)
@@ -355,6 +426,7 @@ public class Toast {

            return result;
        }
    }

    /**
     * Make a standard toast that just contains a text view with the text from a resource.
@@ -385,6 +457,14 @@ public class Toast {
     * @param s The new text for the Toast.
     */
    public void setText(CharSequence s) {
        if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
            if (mNextView != null) {
                throw new IllegalStateException(
                        "Text provided for custom toast, remove previous setView() calls if you "
                                + "want a text toast instead.");
            }
            mText = s;
        } else {
            if (mNextView == null) {
                throw new RuntimeException("This Toast was not created with Toast.makeText()");
            }
@@ -394,6 +474,7 @@ public class Toast {
            }
            tv.setText(s);
        }
    }

    // =======================================================================================
    // All the gunk below is the interaction with the Notification Service, which handles
@@ -442,12 +523,18 @@ public class Toast {
        final Binder mToken;

        @GuardedBy("mCallbacks")
        private final List<Callback> mCallbacks = new ArrayList<>();
        private final List<Callback> mCallbacks;

        static final long SHORT_DURATION_TIMEOUT = 4000;
        static final long LONG_DURATION_TIMEOUT = 7000;

        TN(String packageName, Binder token, @Nullable Looper looper) {
        /**
         * Creates a {@link ITransientNotification} object.
         *
         * The parameter {@code callbacks} is not copied and is accessed with itself as its own
         * lock.
         */
        TN(String packageName, Binder token, List<Callback> callbacks, @Nullable Looper looper) {
            // XXX This should be changed to use a Dialog, with a Theme.Toast
            // defined that sets up the layout params appropriately.
            final WindowManager.LayoutParams params = mParams;
@@ -464,15 +551,8 @@ public class Toast {

            mPackageName = packageName;
            mToken = token;
            mCallbacks = callbacks;

            if (looper == null) {
                // Use Looper.myLooper() if looper is not specified.
                looper = Looper.myLooper();
                if (looper == null) {
                    throw new RuntimeException(
                            "Can't toast on a thread that has not called Looper.prepare()");
                }
            }
            mHandler = new Handler(looper, null) {
                @Override
                public void handleMessage(Message msg) {
@@ -655,4 +735,46 @@ public class Toast {
         */
        public void onToastHidden() {}
    }

    private static class CallbackBinder extends ITransientNotificationCallback.Stub {
        private final Handler mHandler;

        @GuardedBy("mCallbacks")
        private final List<Callback> mCallbacks;

        /**
         * Creates a {@link ITransientNotificationCallback} object.
         *
         * The parameter {@code callbacks} is not copied and is accessed with itself as its own
         * lock.
         */
        private CallbackBinder(List<Callback> callbacks, Handler handler) {
            mCallbacks = callbacks;
            mHandler = handler;
        }

        @Override
        public void onToastShown() {
            mHandler.post(() -> {
                for (Callback callback : getCallbacks()) {
                    callback.onToastShown();
                }
            });
        }

        @Override
        public void onToastHidden() {
            mHandler.post(() -> {
                for (Callback callback : getCallbacks()) {
                    callback.onToastHidden();
                }
            });
        }

        private List<Callback> getCallbacks() {
            synchronized (mCallbacks) {
                return new ArrayList<>(mCallbacks);
            }
        }
    }
}
+12 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.internal.statusbar;

import android.app.ITransientNotificationCallback;
import android.content.ComponentName;
import android.graphics.Rect;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
@@ -198,4 +199,15 @@ oneway interface IStatusBar
     * Dismiss the warning that the device is about to go to sleep due to user inactivity.
     */
    void dismissInattentiveSleepWarning(boolean animated);

    /**
     * Displays a text toast.
     */
    void showToast(String packageName, IBinder token, CharSequence text, IBinder windowToken,
            int duration, @nullable ITransientNotificationCallback callback);

    /**
     * Cancels toast with token {@code token} in {@code packageName}.
     */
    void hideToast(String packageName, IBinder token);
}
+1 −0
Original line number Diff line number Diff line
@@ -80,5 +80,6 @@
        <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
        <item>com.android.systemui.theme.ThemeOverlayController</item>
        <item>com.android.systemui.navigationbar.car.CarNavigationBar</item>
        <item>com.android.systemui.toast.ToastUI</item>
    </string-array>
</resources>
Loading