summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/widget/ToastPresenter.java25
-rw-r--r--packages/SystemUI/docs/plugin_hooks.md31
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java107
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/ToastLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java139
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java83
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/ToastUI.java74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java36
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java1
11 files changed, 566 insertions, 34 deletions
diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java
index fb5d55dd2141..b484dfacbf6c 100644
--- a/core/java/android/widget/ToastPresenter.java
+++ b/core/java/android/widget/ToastPresenter.java
@@ -48,6 +48,8 @@ import com.android.internal.util.ArrayUtils;
public class ToastPresenter {
private static final String TAG = "ToastPresenter";
private static final String WINDOW_TITLE = "Toast";
+
+ // exclusively used to guarantee window timeouts
private static final long SHORT_DURATION_TIMEOUT = 4000;
private static final long LONG_DURATION_TIMEOUT = 7000;
@@ -145,7 +147,7 @@ public class ToastPresenter {
*/
private void adjustLayoutParams(WindowManager.LayoutParams params, IBinder windowToken,
int duration, int gravity, int xOffset, int yOffset, float horizontalMargin,
- float verticalMargin) {
+ float verticalMargin, boolean removeWindowAnimations) {
Configuration config = mResources.getConfiguration();
int absGravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection());
params.gravity = absGravity;
@@ -163,6 +165,10 @@ public class ToastPresenter {
params.hideTimeoutMilliseconds =
(duration == Toast.LENGTH_LONG) ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
params.token = windowToken;
+
+ if (removeWindowAnimations && params.windowAnimations == R.style.Animation_Toast) {
+ params.windowAnimations = 0;
+ }
}
/**
@@ -193,16 +199,28 @@ public class ToastPresenter {
/**
* Shows the toast in {@code view} with the parameters passed and callback {@code callback}.
+ * Uses window animations to animate the toast.
*/
public void show(View view, IBinder token, IBinder windowToken, int duration, int gravity,
int xOffset, int yOffset, float horizontalMargin, float verticalMargin,
@Nullable ITransientNotificationCallback callback) {
+ show(view, token, windowToken, duration, gravity, xOffset, yOffset, horizontalMargin,
+ verticalMargin, callback, false /* removeWindowAnimations */);
+ }
+
+ /**
+ * Shows the toast in {@code view} with the parameters passed and callback {@code callback}.
+ * Can optionally remove window animations from the toast window.
+ */
+ public void show(View view, IBinder token, IBinder windowToken, int duration, int gravity,
+ int xOffset, int yOffset, float horizontalMargin, float verticalMargin,
+ @Nullable ITransientNotificationCallback callback, boolean removeWindowAnimations) {
checkState(mView == null, "Only one toast at a time is allowed, call hide() first.");
mView = view;
mToken = token;
adjustLayoutParams(mParams, windowToken, duration, gravity, xOffset, yOffset,
- horizontalMargin, verticalMargin);
+ horizontalMargin, verticalMargin, removeWindowAnimations);
if (mView.getParent() != null) {
mWindowManager.removeView(mView);
}
@@ -247,7 +265,8 @@ public class ToastPresenter {
try {
callback.onToastHidden();
} catch (RemoteException e) {
- Log.w(TAG, "Error calling back " + mPackageName + " to notify onToastHide()", e);
+ Log.w(TAG, "Error calling back " + mPackageName + " to notify onToastHide()",
+ e);
}
}
mView = null;
diff --git a/packages/SystemUI/docs/plugin_hooks.md b/packages/SystemUI/docs/plugin_hooks.md
index 9fe2e181971a..1b1bb37ca0b1 100644
--- a/packages/SystemUI/docs/plugin_hooks.md
+++ b/packages/SystemUI/docs/plugin_hooks.md
@@ -1,33 +1,34 @@
# Plugin hooks
### Action: com.android.systemui.action.PLUGIN_OVERLAY
-Expected interface: [OverlayPlugin](/packages/SystemUI/plugin/src/com/android/systemui/plugins/OverlayPlugin.java)
+Expected interface: [OverlayPlugin](/frameworks/base/packages/SystemUI/plugin/src/com/android
+/systemui/plugins/OverlayPlugin.java)
Use: Allows plugin access to the status bar and nav bar window for whatever nefarious purposes you can imagine.
### Action: com.android.systemui.action.PLUGIN_QS
-Expected interface: [QS](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java)
+Expected interface: [QS](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java)
Use: Allows the entire QS panel to be replaced with something else that is optionally expandable.
Notes: To not mess up the notification panel interaction, much of the QSContainer interface needs to actually be implemented.
### Action: com.android.systemui.action.PLUGIN_QS_FACTORY
-Expected interface: [QSFactory](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java)
+Expected interface: [QSFactory](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java)
Use: Controls the creation of QS Tiles and their views, can used to add or change system QS tiles, can also be used to change the layout/interaction of the tile views.
### Action: com.android.systemui.action.PLUGIN_NAV_BUTTON
-Expected interface: [NavBarButtonProvider](/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java)
+Expected interface: [NavBarButtonProvider](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java)
Use: Allows a plugin to create a new nav bar button, or override an existing one with a view of its own.
### Action: com.android.systemui.action.PLUGIN_NAV_GESTURE
-Expected interface: [NavGesture](/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java)
+Expected interface: [NavGesture](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java)
Use: Allows touch events from the nav bar to be intercepted and used for other gestures.
### Action: com.android.systemui.action.PLUGIN_LOCKSCREEN_RIGHT_BUTTON
-Expected interface: [IntentButtonProvider](/packages/SystemUI/plugin/src/com/android/systemui/plugins/IntentButtonProvider.java)
+Expected interface: [IntentButtonProvider](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/IntentButtonProvider.java)
Use: Allows a plugin to specify the icon for the bottom right lock screen button, and the intent that gets launched when it is activated.
@@ -37,28 +38,34 @@ Expected interface: [IntentButtonProvider](/packages/SystemUI/plugin/src/com/and
Use: Allows a plugin to specify the icon for the bottom left lock screen button, and the intent that gets launched when it is activated.
### Action: com.android.systemui.action.PLUGIN_GLOBAL_ACTIONS
-Expected interface: [GlobalActions](/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java)
+Expected interface: [GlobalActions](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java)
Use: Allows the long-press power menu to be completely replaced.
### Action: com.android.systemui.action.PLUGIN_VOLUME
-Expected interface: [VolumeDialog](/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialog.java)
+Expected interface: [VolumeDialog](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialog.java)
Use: Allows replacement of the volume dialog.
### Action: com.android.systemui.action.PLUGIN_NOTIFICATION_SWIPE_ACTION
-Expected interface: [NotificationSwipeActionHelper](/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java)
+Expected interface: [NotificationSwipeActionHelper](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java)
Use: Control over swipes/input for notification views, can be used to control what happens when you swipe/long-press
### Action: com.android.systemui.action.PLUGIN_CLOCK
-Expected interface: [ClockPlugin](/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java)
+Expected interface: [ClockPlugin](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java)
Use: Allows replacement of the keyguard main clock.
+### Action: com.android.systemui.action.PLUGIN_TOAST
+Expected interface: [ToastPlugin](/frameworks/base/packages/SystemUI/plugin/src/com/android
+/systemui/plugins/ClockPlugin.java)
+
+Use: Allows replacement of uncustomized toasts created via Toast.makeText().
+
# Global plugin dependencies
These classes can be accessed by any plugin using PluginDependency as long as they @Requires them.
-[VolumeDialogController](/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java) - Mostly just API for the volume plugin
+[VolumeDialogController](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java) - Mostly just API for the volume plugin
-[ActivityStarter](/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java) - Allows starting of intents while co-operating with keyguard unlocks.
+[ActivityStarter](/frameworks/base/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java) - Allows starting of intents while co-operating with keyguard unlocks.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java
new file mode 100644
index 000000000000..0831e0ef7795
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java
@@ -0,0 +1,107 @@
+/*
+ * 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 com.android.systemui.plugins;
+
+import android.animation.Animator;
+import android.annotation.NonNull;
+import android.view.View;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Customize toasts displayed by SystemUI (via Toast#makeText)
+ */
+@ProvidesInterface(action = ToastPlugin.ACTION, version = ToastPlugin.VERSION)
+public interface ToastPlugin extends Plugin {
+
+ String ACTION = "com.android.systemui.action.PLUGIN_TOAST";
+ int VERSION = 1;
+
+ /**
+ * Creates a CustomPluginToast.
+ */
+ @NonNull Toast createToast(CharSequence text, String packageName, int userId);
+
+ /**
+ * Custom Toast with the ability to change toast positioning, styling and animations.
+ */
+ interface Toast {
+ /**
+ * Retrieve the Toast view's gravity.
+ * If no changes, returns null.
+ */
+ default Integer getGravity() {
+ return null;
+ }
+
+ /**
+ * Retrieve the Toast view's X-offset.
+ * If no changes, returns null.
+ */
+ default Integer getXOffset() {
+ return null;
+ }
+
+ /**
+ * Retrieve the Toast view's Y-offset.
+ * If no changes, returns null.
+ */
+ default Integer getYOffset() {
+ return null;
+ }
+
+ /**
+ * Retrieve the Toast view's horizontal margin.
+ * If no changes, returns null.
+ */
+ default Integer getHorizontalMargin() {
+ return null;
+ }
+
+ /**
+ * Retrieve the Toast view's vertical margin.
+ * If no changes, returns null.
+ */
+ default Integer getVerticalMargin() {
+ return null;
+ }
+
+ /**
+ * Retrieve the Toast view to show.
+ * If no changes, returns null.
+ */
+ default View getView() {
+ return null;
+ }
+
+ /**
+ * Retrieve the Toast's animate in.
+ * If no changes, returns null.
+ */
+ default Animator getInAnimation() {
+ return null;
+ }
+
+ /**
+ * Retrieve the Toast's animate out.
+ * If no changes, returns null.
+ */
+ default Animator getOutAnimation() {
+ return null;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 6db408659c96..e3ee2a10821b 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -108,6 +108,18 @@ public class LogModule {
return buffer;
}
+ /** Provides a logging buffer for all logs related to Toasts shown by SystemUI. */
+ @Provides
+ @SysUISingleton
+ @ToastLog
+ public static LogBuffer provideToastLogBuffer(
+ LogcatEchoTracker bufferFilter,
+ DumpManager dumpManager) {
+ LogBuffer buffer = new LogBuffer("ToastLog", 50, 10, bufferFilter);
+ buffer.attach(dumpManager);
+ return buffer;
+ }
+
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/ToastLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/ToastLog.java
new file mode 100644
index 000000000000..8671dbfdf1fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/ToastLog.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for ToastLog-related messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface ToastLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
new file mode 100644
index 000000000000..e9fcf1aa9598
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -0,0 +1,139 @@
+/*
+ * 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 com.android.systemui.toast;
+
+import android.animation.Animator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.view.View;
+import android.widget.ToastPresenter;
+
+import com.android.internal.R;
+import com.android.systemui.plugins.ToastPlugin;
+
+/**
+ * SystemUI TextToast that can be customized by ToastPlugins. Should never instantiate this class
+ * directly. Instead, use {@link ToastFactory#createToast}.
+ */
+public class SystemUIToast implements ToastPlugin.Toast {
+ final Context mContext;
+ final CharSequence mText;
+ final ToastPlugin.Toast mPluginToast;
+
+ final int mDefaultGravity;
+ final int mDefaultY;
+ final int mDefaultX = 0;
+ final int mDefaultHorizontalMargin = 0;
+ final int mDefaultVerticalMargin = 0;
+
+ SystemUIToast(Context context, CharSequence text) {
+ this(context, text, null);
+ }
+
+ SystemUIToast(Context context, CharSequence text, ToastPlugin.Toast pluginToast) {
+ mContext = context;
+ mText = text;
+ mPluginToast = pluginToast;
+
+ mDefaultGravity = context.getResources().getInteger(R.integer.config_toastDefaultGravity);
+ mDefaultY = context.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
+ }
+
+ @Override
+ @NonNull
+ public Integer getGravity() {
+ if (isPluginToast() && mPluginToast.getGravity() != null) {
+ return mPluginToast.getGravity();
+ }
+ return mDefaultGravity;
+ }
+
+ @Override
+ @NonNull
+ public Integer getXOffset() {
+ if (isPluginToast() && mPluginToast.getXOffset() != null) {
+ return mPluginToast.getXOffset();
+ }
+ return mDefaultX;
+ }
+
+ @Override
+ @NonNull
+ public Integer getYOffset() {
+ if (isPluginToast() && mPluginToast.getYOffset() != null) {
+ return mPluginToast.getYOffset();
+ }
+ return mDefaultY;
+ }
+
+ @Override
+ @NonNull
+ public Integer getHorizontalMargin() {
+ if (isPluginToast() && mPluginToast.getHorizontalMargin() != null) {
+ return mPluginToast.getHorizontalMargin();
+ }
+ return mDefaultHorizontalMargin;
+ }
+
+ @Override
+ @NonNull
+ public Integer getVerticalMargin() {
+ if (isPluginToast() && mPluginToast.getVerticalMargin() != null) {
+ return mPluginToast.getVerticalMargin();
+ }
+ return mDefaultVerticalMargin;
+ }
+
+ @Override
+ @NonNull
+ public View getView() {
+ if (isPluginToast() && mPluginToast.getView() != null) {
+ return mPluginToast.getView();
+ }
+ return ToastPresenter.getTextToastView(mContext, mText);
+ }
+
+ @Override
+ @Nullable
+ public Animator getInAnimation() {
+ if (isPluginToast() && mPluginToast.getInAnimation() != null) {
+ return mPluginToast.getInAnimation();
+ }
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public Animator getOutAnimation() {
+ if (isPluginToast() && mPluginToast.getOutAnimation() != null) {
+ return mPluginToast.getOutAnimation();
+ }
+ return null;
+ }
+
+ /**
+ * Whether this toast has a custom animation.
+ */
+ public boolean hasCustomAnimation() {
+ return getInAnimation() != null || getOutAnimation() != null;
+ }
+
+ private boolean isPluginToast() {
+ return mPluginToast != null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
new file mode 100644
index 000000000000..d8cb61c6b349
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
@@ -0,0 +1,83 @@
+/*
+ * 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 com.android.systemui.toast;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.ToastPlugin;
+import com.android.systemui.shared.plugins.PluginManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+
+/**
+ * Factory for creating toasts to be shown by ToastUI.
+ * These toasts can be customized by {@link ToastPlugin}.
+ */
+@SysUISingleton
+public class ToastFactory implements Dumpable {
+ // only one ToastPlugin can be connected at a time.
+ private ToastPlugin mPlugin;
+
+ @Inject
+ public ToastFactory(PluginManager pluginManager, DumpManager dumpManager) {
+ dumpManager.registerDumpable("ToastFactory", this);
+ pluginManager.addPluginListener(
+ new PluginListener<ToastPlugin>() {
+ @Override
+ public void onPluginConnected(ToastPlugin plugin, Context pluginContext) {
+ mPlugin = plugin;
+ }
+
+ @Override
+ public void onPluginDisconnected(ToastPlugin plugin) {
+ if (plugin.equals(mPlugin)) {
+ mPlugin = null;
+ }
+ }
+ }, ToastPlugin.class, false /* Allow multiple plugins */);
+ }
+
+ /**
+ * Create a toast to be shown by ToastUI.
+ */
+ public SystemUIToast createToast(Context context, CharSequence text, String packageName,
+ int userId) {
+ if (isPluginAvailable()) {
+ return new SystemUIToast(context, text, mPlugin.createToast(text, packageName, userId));
+ }
+ return new SystemUIToast(context, text);
+ }
+
+ private boolean isPluginAvailable() {
+ return mPlugin != null;
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("ToastFactory:");
+ pw.println(" mAttachedPlugin=" + mPlugin);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
new file mode 100644
index 000000000000..78173cf62a93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
@@ -0,0 +1,59 @@
+/*
+ * 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 com.android.systemui.toast
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogMessage
+import com.android.systemui.log.dagger.ToastLog
+import javax.inject.Inject
+
+private const val TAG = "ToastLog"
+
+class ToastLogger @Inject constructor(
+ @ToastLog private val buffer: LogBuffer
+) {
+
+ fun logOnShowToast(uid: Int, packageName: String, text: String, token: String) {
+ log(DEBUG, {
+ int1 = uid
+ str1 = packageName
+ str2 = text
+ str3 = token
+ }, {
+ "[$str3] Show toast for ($str1, $int1). msg=\'$str2\'"
+ })
+ }
+
+ fun logOnHideToast(packageName: String, token: String) {
+ log(DEBUG, {
+ str1 = packageName
+ str2 = token
+ }, {
+ "[$str2] Hide toast for [$str1]"
+ })
+ }
+
+ private inline fun log(
+ logLevel: LogLevel,
+ initializer: LogMessage.() -> Unit,
+ noinline printer: LogMessage.() -> String
+ ) {
+ buffer.log(TAG, logLevel, initializer, printer)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index a2203732c47c..1c682e3bb7dc 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -16,25 +16,27 @@
package com.android.systemui.toast;
+import android.animation.Animator;
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.ITransientNotificationCallback;
import android.content.Context;
-import android.content.res.Resources;
import android.os.IBinder;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log;
-import android.view.View;
+import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
+import android.widget.Toast;
import android.widget.ToastPresenter;
-import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.Objects;
@@ -45,35 +47,53 @@ import javax.inject.Inject;
*/
@SysUISingleton
public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
+ // values from NotificationManagerService#LONG_DELAY and NotificationManagerService#SHORT_DELAY
+ private static final int TOAST_LONG_TIME = 3500; // 3.5 seconds
+ private static final int TOAST_SHORT_TIME = 2000; // 2 seconds
+
private static final String TAG = "ToastUI";
private final CommandQueue mCommandQueue;
private final INotificationManager mNotificationManager;
- private final IAccessibilityManager mAccessibilityManager;
- private final int mGravity;
- private final int mY;
+ private final IAccessibilityManager mIAccessibilityManager;
+ private final AccessibilityManager mAccessibilityManager;
+ private final ToastFactory mToastFactory;
+ private final DelayableExecutor mMainExecutor;
+ private final ToastLogger mToastLogger;
+ private SystemUIToast mToast;
@Nullable private ToastPresenter mPresenter;
@Nullable private ITransientNotificationCallback mCallback;
@Inject
- public ToastUI(Context context, CommandQueue commandQueue) {
+ public ToastUI(
+ Context context,
+ CommandQueue commandQueue,
+ ToastFactory toastFactory,
+ @Main DelayableExecutor mainExecutor,
+ ToastLogger toastLogger) {
this(context, commandQueue,
INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
IAccessibilityManager.Stub.asInterface(
- ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)));
+ ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)),
+ toastFactory,
+ mainExecutor,
+ toastLogger);
}
@VisibleForTesting
ToastUI(Context context, CommandQueue commandQueue, INotificationManager notificationManager,
- @Nullable IAccessibilityManager accessibilityManager) {
+ @Nullable IAccessibilityManager accessibilityManager,
+ ToastFactory toastFactory, DelayableExecutor mainExecutor, ToastLogger toastLogger
+ ) {
super(context);
mCommandQueue = commandQueue;
mNotificationManager = notificationManager;
- mAccessibilityManager = accessibilityManager;
- Resources resources = mContext.getResources();
- mGravity = resources.getInteger(R.integer.config_toastDefaultGravity);
- mY = resources.getDimensionPixelSize(R.dimen.toast_y_offset);
+ mIAccessibilityManager = accessibilityManager;
+ mToastFactory = toastFactory;
+ mMainExecutor = mainExecutor;
+ mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
+ mToastLogger = toastLogger;
}
@Override
@@ -88,12 +108,31 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
if (mPresenter != null) {
hideCurrentToast();
}
- Context context = mContext.createContextAsUser(UserHandle.getUserHandleForUid(uid), 0);
- View view = ToastPresenter.getTextToastView(context, text);
+ UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+ Context context = mContext.createContextAsUser(userHandle, 0);
+ mToast = mToastFactory.createToast(context, text, packageName, userHandle.getIdentifier());
+
+ if (mToast.hasCustomAnimation()) {
+ if (mToast.getInAnimation() != null) {
+ mToast.getInAnimation().start();
+ }
+ final Animator hideAnimator = mToast.getOutAnimation();
+ if (hideAnimator != null) {
+ final long durationMillis = duration == Toast.LENGTH_LONG
+ ? TOAST_LONG_TIME : TOAST_SHORT_TIME;
+ final long updatedDuration = mAccessibilityManager.getRecommendedTimeoutMillis(
+ (int) durationMillis, AccessibilityManager.FLAG_CONTENT_TEXT);
+ mMainExecutor.executeDelayed(() -> hideAnimator.start(),
+ updatedDuration - hideAnimator.getTotalDuration());
+ }
+ }
mCallback = callback;
- mPresenter = new ToastPresenter(context, mAccessibilityManager, mNotificationManager,
+ mPresenter = new ToastPresenter(context, mIAccessibilityManager, mNotificationManager,
packageName);
- mPresenter.show(view, token, windowToken, duration, mGravity, 0, mY, 0, 0, mCallback);
+ mToastLogger.logOnShowToast(uid, packageName, text.toString(), token.toString());
+ mPresenter.show(mToast.getView(), token, windowToken, duration, mToast.getGravity(),
+ mToast.getXOffset(), mToast.getYOffset(), mToast.getHorizontalMargin(),
+ mToast.getVerticalMargin(), mCallback, mToast.hasCustomAnimation());
}
@Override
@@ -104,6 +143,7 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
Log.w(TAG, "Attempt to hide non-current toast from package " + packageName);
return;
}
+ mToastLogger.logOnHideToast(packageName, token.toString());
hideCurrentToast();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index 0a10ab2fbf02..c743fd07c492 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -54,7 +54,11 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.util.IntPair;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -84,6 +88,7 @@ public class ToastUITest extends SysuiTestCase {
private static final String TEXT = "Hello World";
private static final int MESSAGE_RES_ID = R.id.message;
+ private FakeExecutor mFakeDelayableExecutor = new FakeExecutor(new FakeSystemClock());
private Context mContextSpy;
private ToastUI mToastUI;
@Mock private LayoutInflater mLayoutInflater;
@@ -91,6 +96,10 @@ public class ToastUITest extends SysuiTestCase {
@Mock private WindowManager mWindowManager;
@Mock private INotificationManager mNotificationManager;
@Mock private IAccessibilityManager mAccessibilityManager;
+ @Mock private PluginManager mPluginManager;
+ @Mock private DumpManager mDumpManager;
+ @Mock private ToastLogger mToastLogger;
+
@Mock private ITransientNotificationCallback mCallback;
@Captor private ArgumentCaptor<View> mViewCaptor;
@Captor private ArgumentCaptor<ViewGroup.LayoutParams> mParamsCaptor;
@@ -109,8 +118,10 @@ public class ToastUITest extends SysuiTestCase {
mContextSpy = spy(mContext);
doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt());
+ doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt());
mToastUI = new ToastUI(mContextSpy, mCommandQueue, mNotificationManager,
- mAccessibilityManager);
+ mAccessibilityManager, new ToastFactory(mPluginManager, mDumpManager),
+ mFakeDelayableExecutor, mToastLogger);
}
@Test
@@ -271,6 +282,29 @@ public class ToastUITest extends SysuiTestCase {
verify(mCallback).onToastHidden();
}
+ @Test
+ public void testShowToast_logs() {
+ mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+ mCallback);
+
+ verify(mToastLogger).logOnShowToast(UID_1, PACKAGE_NAME_1, TEXT, TOKEN_1.toString());
+ }
+
+ @Test
+ public void testHideToast_logs() {
+ mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+ mCallback);
+ mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1);
+ verify(mToastLogger).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString());
+ }
+
+ @Test
+ public void testHideToast_error_noLog() {
+ // no toast was shown, so this hide is invalid
+ mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1);
+ verify(mToastLogger, never()).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString());
+ }
+
private View verifyWmAddViewAndAttachToParent() {
ArgumentCaptor<View> viewCaptor = ArgumentCaptor.forClass(View.class);
verify(mWindowManager).addView(viewCaptor.capture(), any());
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index dce798e81a86..3ff369a34764 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -929,7 +929,6 @@ public class DisplayPolicy {
attrs.hideTimeoutMilliseconds = mAccessibilityManager.getRecommendedTimeoutMillis(
(int) attrs.hideTimeoutMilliseconds,
AccessibilityManager.FLAG_CONTENT_TEXT);
- attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
// Toasts can't be clickable
attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
break;