Merge "Unify toast presentation code" into rvc-dev

This commit is contained in:
Bernardo Rufino
2020-02-28 21:09:36 +00:00
committed by Android (Google) Code Review
3 changed files with 140 additions and 124 deletions

View File

@@ -31,9 +31,7 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter; import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage; import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.Binder; import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
@@ -43,11 +41,8 @@ import android.os.Message;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.util.Log; import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
@@ -122,6 +117,7 @@ public class Toast {
private final Binder mToken; private final Binder mToken;
private final Context mContext; private final Context mContext;
private final Handler mHandler; private final Handler mHandler;
private final ToastPresenter mPresenter;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
final TN mTN; final TN mTN;
@UnsupportedAppUsage @UnsupportedAppUsage
@@ -172,7 +168,8 @@ public class Toast {
looper = getLooper(looper); looper = getLooper(looper);
mHandler = new Handler(looper); mHandler = new Handler(looper);
mCallbacks = new ArrayList<>(); mCallbacks = new ArrayList<>();
mTN = new TN(context.getPackageName(), mToken, mCallbacks, looper); mPresenter = new ToastPresenter(context, AccessibilityManager.getInstance(context));
mTN = new TN(mPresenter, context.getPackageName(), mToken, mCallbacks, looper);
mTN.mY = context.getResources().getDimensionPixelSize( mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset); com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger( mTN.mGravity = context.getResources().getInteger(
@@ -504,13 +501,7 @@ public class Toast {
return result; return result;
} else { } else {
Toast result = new Toast(context, looper); Toast result = new Toast(context, looper);
View v = result.mPresenter.getTextToastView(text);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView) v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v; result.mNextView = v;
result.mDuration = duration; result.mDuration = duration;
@@ -611,34 +602,20 @@ public class Toast {
final String mPackageName; final String mPackageName;
final Binder mToken; final Binder mToken;
private final ToastPresenter mPresenter;
@GuardedBy("mCallbacks") @GuardedBy("mCallbacks")
private final List<Callback> mCallbacks; private final List<Callback> mCallbacks;
static final long SHORT_DURATION_TIMEOUT = 4000;
static final long LONG_DURATION_TIMEOUT = 7000;
/** /**
* Creates a {@link ITransientNotification} object. * Creates a {@link ITransientNotification} object.
* *
* The parameter {@code callbacks} is not copied and is accessed with itself as its own * The parameter {@code callbacks} is not copied and is accessed with itself as its own
* lock. * lock.
*/ */
TN(String packageName, Binder token, List<Callback> callbacks, @Nullable Looper looper) { TN(ToastPresenter presenter, String packageName, Binder token, List<Callback> callbacks,
// XXX This should be changed to use a Dialog, with a Theme.Toast @Nullable Looper looper) {
// defined that sets up the layout params appropriately. mPresenter = presenter;
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setFitInsetsIgnoringVisibility(true);
params.setTitle("Toast");
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
mPackageName = packageName; mPackageName = packageName;
mToken = token; mToken = token;
mCallbacks = callbacks; mCallbacks = callbacks;
@@ -673,6 +650,8 @@ public class Toast {
} }
} }
}; };
presenter.startLayoutParams(mParams);
} }
private List<Callback> getCallbacks() { private List<Callback> getCallbacks() {
@@ -718,30 +697,12 @@ public class Toast {
handleHide(); handleHide();
mView = mNextView; mView = mNextView;
Context context = mView.getContext().getApplicationContext(); Context context = mView.getContext().getApplicationContext();
String packageName = mView.getContext().getOpPackageName();
if (context == null) { if (context == null) {
context = mView.getContext(); context = mView.getContext();
} }
mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
// We can resolve the Gravity here by using the Locale for getting mPresenter.adjustLayoutParams(mParams, windowToken, mDuration, mGravity, mX, mY,
// the layout direction mHorizontalMargin, mVerticalMargin);
final Configuration config = mView.getContext().getResources().getConfiguration();
final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
mParams.horizontalWeight = 1.0f;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
mParams.verticalWeight = 1.0f;
}
mParams.x = mX;
mParams.y = mY;
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = mDuration ==
Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
mParams.token = windowToken;
if (mView.getParent() != null) { if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView); mWM.removeView(mView);
@@ -753,7 +714,7 @@ public class Toast {
// invalidated. Let us hedge against that. // invalidated. Let us hedge against that.
try { try {
mWM.addView(mView, mParams); mWM.addView(mView, mParams);
trySendAccessibilityEvent(); mPresenter.trySendAccessibilityEvent(mView, mPackageName);
for (Callback callback : getCallbacks()) { for (Callback callback : getCallbacks()) {
callback.onToastShown(); callback.onToastShown();
} }
@@ -763,22 +724,6 @@ public class Toast {
} }
} }
private void trySendAccessibilityEvent() {
AccessibilityManager accessibilityManager =
AccessibilityManager.getInstance(mView.getContext());
if (!accessibilityManager.isEnabled()) {
return;
}
// treat toasts as notifications since they are used to
// announce a transient piece of information to the user
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setClassName(getClass().getName());
event.setPackageName(mView.getContext().getPackageName());
mView.dispatchPopulateAccessibilityEvent(event);
accessibilityManager.sendAccessibilityEvent(event);
}
@UnsupportedAppUsage @UnsupportedAppUsage
public void handleHide() { public void handleHide() {
if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView); if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 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.widget;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.R;
/**
* Class responsible for toast presentation inside app's process and in system UI.
*
* @hide
*/
public class ToastPresenter {
private static final long SHORT_DURATION_TIMEOUT = 4000;
private static final long LONG_DURATION_TIMEOUT = 7000;
private final Context mContext;
private final AccessibilityManager mAccessibilityManager;
public ToastPresenter(Context context, AccessibilityManager accessibilityManager) {
mContext = context;
mAccessibilityManager = accessibilityManager;
}
/**
* Initializes {@code params} with default values for toasts.
*/
public void startLayoutParams(WindowManager.LayoutParams params) {
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setFitInsetsIgnoringVisibility(true);
params.setTitle("Toast");
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
}
/**
* Customizes {@code params} according to other parameters, ready to be passed to {@link
* WindowManager#addView(View, ViewGroup.LayoutParams)}.
*/
public void adjustLayoutParams(WindowManager.LayoutParams params, IBinder windowToken,
int duration, int gravity, int xOffset, int yOffset, float horizontalMargin,
float verticalMargin) {
Configuration config = mContext.getResources().getConfiguration();
int absGravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection());
params.gravity = absGravity;
if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
params.horizontalWeight = 1.0f;
}
if ((absGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
params.verticalWeight = 1.0f;
}
params.x = xOffset;
params.y = yOffset;
params.horizontalMargin = horizontalMargin;
params.verticalMargin = verticalMargin;
params.packageName = mContext.getPackageName();
params.hideTimeoutMilliseconds =
(duration == Toast.LENGTH_LONG) ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
params.token = windowToken;
}
/**
* Returns the default text toast view for message {@code text}.
*/
public View getTextToastView(CharSequence text) {
View view = LayoutInflater.from(mContext).inflate(
R.layout.transient_notification, null);
TextView textView = view.findViewById(com.android.internal.R.id.message);
textView.setText(text);
return view;
}
/**
* Sends {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED} event if accessibility is
* enabled.
*/
public void trySendAccessibilityEvent(View view, String packageName) {
if (!mAccessibilityManager.isEnabled()) {
return;
}
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setClassName(Toast.class.getName());
event.setPackageName(packageName);
view.dispatchPopulateAccessibilityEvent(event);
mAccessibilityManager.sendAccessibilityEvent(event);
}
}

View File

@@ -21,21 +21,16 @@ import android.annotation.Nullable;
import android.app.INotificationManager; import android.app.INotificationManager;
import android.app.ITransientNotificationCallback; import android.app.ITransientNotificationCallback;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.util.Log; import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.widget.ToastPresenter;
import com.android.internal.R; import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -64,6 +59,7 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
private final WindowManager mWindowManager; private final WindowManager mWindowManager;
private final INotificationManager mNotificationManager; private final INotificationManager mNotificationManager;
private final AccessibilityManager mAccessibilityManager; private final AccessibilityManager mAccessibilityManager;
private final ToastPresenter mPresenter;
private ToastEntry mCurrentToast; private ToastEntry mCurrentToast;
@Inject @Inject
@@ -83,6 +79,7 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
mWindowManager = windowManager; mWindowManager = windowManager;
mNotificationManager = notificationManager; mNotificationManager = notificationManager;
mAccessibilityManager = accessibilityManager; mAccessibilityManager = accessibilityManager;
mPresenter = new ToastPresenter(context, accessibilityManager);
} }
@Override @Override
@@ -97,7 +94,7 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
if (mCurrentToast != null) { if (mCurrentToast != null) {
hideCurrentToast(); hideCurrentToast();
} }
View view = getView(text); View view = mPresenter.getTextToastView(text);
LayoutParams params = getLayoutParams(windowToken, duration); LayoutParams params = getLayoutParams(windowToken, duration);
mCurrentToast = new ToastEntry(packageName, token, view, windowToken, callback); mCurrentToast = new ToastEntry(packageName, token, view, windowToken, callback);
try { try {
@@ -106,7 +103,7 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
Log.w(TAG, "Error while attempting to show toast from " + packageName, e); Log.w(TAG, "Error while attempting to show toast from " + packageName, e);
return; return;
} }
trySendAccessibilityEvent(view, packageName); mPresenter.trySendAccessibilityEvent(view, packageName);
if (callback != null) { if (callback != null) {
try { try {
callback.onToastShown(); callback.onToastShown();
@@ -148,56 +145,13 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
mCurrentToast = null; mCurrentToast = null;
} }
private void trySendAccessibilityEvent(View view, String packageName) {
if (!mAccessibilityManager.isEnabled()) {
return;
}
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setClassName(Toast.class.getName());
event.setPackageName(packageName);
view.dispatchPopulateAccessibilityEvent(event);
mAccessibilityManager.sendAccessibilityEvent(event);
}
private View getView(CharSequence text) {
View view = LayoutInflater.from(mContext).inflate(
R.layout.transient_notification, null);
TextView textView = view.findViewById(com.android.internal.R.id.message);
textView.setText(text);
return view;
}
private LayoutParams getLayoutParams(IBinder windowToken, int duration) { private LayoutParams getLayoutParams(IBinder windowToken, int duration) {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(); WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.height = WindowManager.LayoutParams.WRAP_CONTENT; mPresenter.startLayoutParams(params);
params.width = WindowManager.LayoutParams.WRAP_CONTENT; int gravity = mContext.getResources().getInteger(
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
Configuration config = mContext.getResources().getConfiguration();
int specificGravity = mContext.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity); com.android.internal.R.integer.config_toastDefaultGravity);
int gravity = Gravity.getAbsoluteGravity(specificGravity, config.getLayoutDirection()); int yOffset = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
params.gravity = gravity; mPresenter.adjustLayoutParams(params, windowToken, duration, gravity, 0, yOffset, 0, 0);
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
params.horizontalWeight = 1.0f;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
params.verticalWeight = 1.0f;
}
params.x = 0;
params.y = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
params.verticalMargin = 0;
params.horizontalMargin = 0;
params.packageName = mContext.getPackageName();
params.hideTimeoutMilliseconds =
(duration == Toast.LENGTH_LONG) ? DURATION_LONG : DURATION_SHORT;
params.token = windowToken;
return params; return params;
} }