Showing notifications after taking a screenshot (Bug: 5333706)

- Fixing issue where ticker and swipe-to-remove wasn't working for updated notifications

Change-Id: Ib8ef07778a11028c9c2627830ee1dd4561d2ae3b
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 6cb8799..6d70135 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -25,6 +25,7 @@
         android:id="@+id/global_screenshot_container"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_gravity="center"
         android:background="@drawable/global_screenshot_background"
         android:visibility="gone">
         <ImageView android:id="@+id/global_screenshot"
@@ -32,9 +33,4 @@
             android:layout_height="wrap_content"
             android:adjustViewBounds="true" />
     </FrameLayout>
-    <ImageView android:id="@+id/global_screenshot_flash"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="#FFFFFFFF"
-        android:visibility="gone" />
 </FrameLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 65d5138..a717b57 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -168,10 +168,20 @@
     <!-- Compatibility mode help screen: body text. [CHAR LIMIT=150] -->
     <string name="compat_mode_help_body">When an app was designed for a smaller screen, a zoom control will appear by the clock.</string>
 
-    <!-- toast message displayed when a screenshot is saved to the Gallery. -->
-    <string name="screenshot_saving_toast">Screenshot saved to Gallery</string>
-    <!-- toast message displayed when we fail to take a screenshot. -->
-    <string name="screenshot_failed_toast">Could not save screenshot. External storage may be in use.</string>
+    <!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] -->
+    <string name="screenshot_saving_ticker">Saving...</string>
+    <!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] -->
+    <string name="screenshot_saving_title">Saving screenshot...</string>
+    <!-- Notification text displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=100] -->
+    <string name="screenshot_saving_text">Please wait for screenshot to be saved</string>
+    <!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
+    <string name="screenshot_saved_title">Screenshot captured</string>
+    <!-- Notification text displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=100] -->
+    <string name="screenshot_saved_text">Touch to view your screenshot</string>
+    <!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
+    <string name="screenshot_failed_title">Screenshot failed</string>
+    <!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
+    <string name="screenshot_failed_text">Failed to save screenshot. External storage may be in use.</string>
 
     <!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] -->
     <string name="usb_preference_title">USB file transfer options</string>
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 3fa3078..cf073c4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -19,27 +19,27 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
-import android.media.MediaScannerConnection;
 import android.net.Uri;
 import android.os.AsyncTask;
-import android.os.Binder;
 import android.os.Environment;
 import android.os.ServiceManager;
 import android.provider.MediaStore;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.view.Display;
 import android.view.IWindowManager;
 import android.view.LayoutInflater;
@@ -50,16 +50,11 @@
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.systemui.R;
 
 import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
 import java.io.OutputStream;
-import java.lang.Thread;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 
@@ -83,6 +78,46 @@
     private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
     private static final String SCREENSHOT_FILE_PATH_TEMPLATE = "%s/%s/%s";
 
+    private int mNotificationId;
+    private NotificationManager mNotificationManager;
+    private Notification.Builder mNotificationBuilder;
+    private Intent mLaunchIntent;
+    private String mImageDir;
+    private String mImageFileName;
+    private String mImageFilePath;
+    private String mImageDate;
+    private long mImageTime;
+
+    SaveImageInBackgroundTask(Context context, NotificationManager nManager, int nId) {
+        Resources r = context.getResources();
+
+        // Prepare all the output metadata
+        mImageTime = System.currentTimeMillis();
+        mImageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(mImageTime));
+        mImageDir = Environment.getExternalStoragePublicDirectory(
+                Environment.DIRECTORY_PICTURES).getAbsolutePath();
+        mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, mImageDate);
+        mImageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE, mImageDir,
+                SCREENSHOTS_DIR_NAME, mImageFileName);
+
+        // Show the intermediate notification
+        mLaunchIntent = new Intent(Intent.ACTION_VIEW);
+        mLaunchIntent.setDataAndType(Uri.fromFile(new File(mImageFilePath)), "image/png");
+        mLaunchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mNotificationId = nId;
+        mNotificationBuilder = new Notification.Builder(context)
+            .setTicker(r.getString(R.string.screenshot_saving_ticker))
+            .setContentTitle(r.getString(R.string.screenshot_saving_title))
+            .setContentText(r.getString(R.string.screenshot_saving_text))
+            .setSmallIcon(android.R.drawable.ic_menu_gallery)
+            .setWhen(System.currentTimeMillis());
+        Notification n = mNotificationBuilder.getNotification();
+        n.flags |= Notification.FLAG_NO_CLEAR;
+
+        mNotificationManager = nManager;
+        mNotificationManager.notify(nId, n);
+    }
+
     @Override
     protected SaveImageInBackgroundData doInBackground(SaveImageInBackgroundData... params) {
         if (params.length != 1) return null;
@@ -91,23 +126,15 @@
         Bitmap image = params[0].image;
 
         try {
-            long currentTime = System.currentTimeMillis();
-            String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(currentTime));
-            String imageDir = Environment.getExternalStoragePublicDirectory(
-                    Environment.DIRECTORY_PICTURES).getAbsolutePath();
-            String imageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, date);
-            String imageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE, imageDir,
-                    SCREENSHOTS_DIR_NAME, imageFileName);
-
             // Save the screenshot to the MediaStore
             ContentValues values = new ContentValues();
             ContentResolver resolver = context.getContentResolver();
-            values.put(MediaStore.Images.ImageColumns.DATA, imageFilePath);
-            values.put(MediaStore.Images.ImageColumns.TITLE, imageFileName);
-            values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, imageFileName);
-            values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, currentTime);
-            values.put(MediaStore.Images.ImageColumns.DATE_ADDED, currentTime);
-            values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, currentTime);
+            values.put(MediaStore.Images.ImageColumns.DATA, mImageFilePath);
+            values.put(MediaStore.Images.ImageColumns.TITLE, mImageFileName);
+            values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, mImageFileName);
+            values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, mImageTime);
+            values.put(MediaStore.Images.ImageColumns.DATE_ADDED, mImageTime);
+            values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, mImageTime);
             values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png");
             Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
 
@@ -118,7 +145,7 @@
 
             // update file size in the database
             values.clear();
-            values.put(MediaStore.Images.ImageColumns.SIZE, new File(imageFilePath).length());
+            values.put(MediaStore.Images.ImageColumns.SIZE, new File(mImageFilePath).length());
             resolver.update(uri, values, null, null);
 
             params[0].result = 0;
@@ -135,12 +162,22 @@
     protected void onPostExecute(SaveImageInBackgroundData params) {
         if (params.result > 0) {
             // Show a message that we've failed to save the image to disk
-            Toast.makeText(params.context, R.string.screenshot_failed_toast,
-                    Toast.LENGTH_SHORT).show();
+            GlobalScreenshot.notifyScreenshotError(params.context, mNotificationManager);
         } else {
-            // Show a message that we've saved the screenshot to disk
-            Toast.makeText(params.context, R.string.screenshot_saving_toast,
-                    Toast.LENGTH_SHORT).show();
+            // Show the final notification to indicate screenshot saved
+            Resources r = params.context.getResources();
+
+            mNotificationBuilder
+                .setTicker(r.getString(R.string.screenshot_saved_title))
+                .setContentTitle(r.getString(R.string.screenshot_saved_title))
+                .setContentText(r.getString(R.string.screenshot_saved_text))
+                .setContentIntent(PendingIntent.getActivity(params.context, 0, mLaunchIntent, 0))
+                .setWhen(System.currentTimeMillis())
+                .setAutoCancel(true);
+
+            Notification n = mNotificationBuilder.getNotification();
+            n.flags &= ~Notification.FLAG_NO_CLEAR;
+            mNotificationManager.notify(mNotificationId, n);
         }
         params.finisher.run();
     };
@@ -154,22 +191,21 @@
  */
 class GlobalScreenshot {
     private static final String TAG = "GlobalScreenshot";
-    private static final int SCREENSHOT_FADE_IN_DURATION = 900;
+    private static final int SCREENSHOT_NOTIFICATION_ID = 789;
+    private static final int SCREENSHOT_FADE_IN_DURATION = 500;
     private static final int SCREENSHOT_FADE_OUT_DELAY = 1000;
-    private static final int SCREENSHOT_FADE_OUT_DURATION = 450;
-    private static final int TOAST_FADE_IN_DURATION = 500;
-    private static final int TOAST_FADE_OUT_DELAY = 1000;
-    private static final int TOAST_FADE_OUT_DURATION = 500;
+    private static final int SCREENSHOT_FADE_OUT_DURATION = 300;
     private static final float BACKGROUND_ALPHA = 0.65f;
-    private static final float SCREENSHOT_SCALE = 0.85f;
-    private static final float SCREENSHOT_MIN_SCALE = 0.7f;
-    private static final float SCREENSHOT_ROTATION = -6.75f; // -12.5f;
+    private static final float SCREENSHOT_SCALE_FUDGE = 0.075f; // To account for the border padding
+    private static final float SCREENSHOT_SCALE = 0.8f;
+    private static final float SCREENSHOT_MIN_SCALE = 0.775f;
 
     private Context mContext;
     private LayoutInflater mLayoutInflater;
     private IWindowManager mIWindowManager;
     private WindowManager mWindowManager;
     private WindowManager.LayoutParams mWindowLayoutParams;
+    private NotificationManager mNotificationManager;
     private Display mDisplay;
     private DisplayMetrics mDisplayMetrics;
     private Matrix mDisplayMatrix;
@@ -182,10 +218,15 @@
 
     private AnimatorSet mScreenshotAnimation;
 
-    // General use cubic interpolator
-    final TimeInterpolator mCubicInterpolator = new TimeInterpolator() {
+    // Fade interpolators
+    final TimeInterpolator mFadeInInterpolator = new TimeInterpolator() {
         public float getInterpolation(float t) {
-            return t*t*t;
+            return (float) Math.pow(t, 1.5f);
+        }
+    };
+    final TimeInterpolator mFadeOutInterpolator = new TimeInterpolator() {
+        public float getInterpolation(float t) {
+            return (float) t;
         }
     };
     // The interpolator used to control the background alpha at the start of the animation
@@ -237,6 +278,8 @@
                 PixelFormat.TRANSLUCENT);
         mWindowLayoutParams.setTitle("ScreenshotAnimation");
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        mNotificationManager =
+            (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         mDisplay = mWindowManager.getDefaultDisplay();
     }
 
@@ -248,7 +291,8 @@
         data.context = mContext;
         data.image = mScreenBitmap;
         data.finisher = finisher;
-        new SaveImageInBackgroundTask().execute(data);
+        new SaveImageInBackgroundTask(mContext, mNotificationManager, SCREENSHOT_NOTIFICATION_ID)
+                .execute(data);
     }
 
     /**
@@ -300,8 +344,7 @@
 
         // If we couldn't take the screenshot, notify the user
         if (mScreenBitmap == null) {
-            Toast.makeText(mContext, R.string.screenshot_failed_toast,
-                    Toast.LENGTH_SHORT).show();
+            notifyScreenshotError(mContext, mNotificationManager);
             finisher.run();
             return;
         }
@@ -341,12 +384,13 @@
     }
     private ValueAnimator createScreenshotFadeInAnimation() {
         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-        anim.setInterpolator(mCubicInterpolator);
+        anim.setInterpolator(mFadeInInterpolator);
         anim.setDuration(SCREENSHOT_FADE_IN_DURATION);
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
                 mBackgroundView.setVisibility(View.VISIBLE);
+                mScreenshotContainerView.setTranslationY(0f);
                 mScreenshotContainerView.setVisibility(View.VISIBLE);
             }
         });
@@ -356,18 +400,19 @@
                 float t = ((Float) animation.getAnimatedValue()).floatValue();
                 mBackgroundView.setAlpha(mBackgroundViewAlphaInterpolator.getInterpolation(t) *
                         BACKGROUND_ALPHA);
-                float scaleT = SCREENSHOT_SCALE + (1f - t) * SCREENSHOT_SCALE;
+                float scaleT = SCREENSHOT_SCALE
+                        + (1f - t) * (1f - SCREENSHOT_SCALE)
+                        + SCREENSHOT_SCALE_FUDGE;
                 mScreenshotContainerView.setAlpha(t*t*t*t);
                 mScreenshotContainerView.setScaleX(scaleT);
                 mScreenshotContainerView.setScaleY(scaleT);
-                mScreenshotContainerView.setRotation(t * SCREENSHOT_ROTATION);
             }
         });
         return anim;
     }
     private ValueAnimator createScreenshotFadeOutAnimation() {
         ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
-        anim.setInterpolator(mCubicInterpolator);
+        anim.setInterpolator(mFadeOutInterpolator);
         anim.setStartDelay(SCREENSHOT_FADE_OUT_DELAY);
         anim.setDuration(SCREENSHOT_FADE_OUT_DURATION);
         anim.addListener(new AnimatorListenerAdapter() {
@@ -381,8 +426,9 @@
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 float t = ((Float) animation.getAnimatedValue()).floatValue();
-                float scaleT = SCREENSHOT_MIN_SCALE +
-                        t*(SCREENSHOT_SCALE - SCREENSHOT_MIN_SCALE);
+                float scaleT = SCREENSHOT_MIN_SCALE
+                        + t * (SCREENSHOT_SCALE - SCREENSHOT_MIN_SCALE)
+                        + SCREENSHOT_SCALE_FUDGE;
                 mScreenshotContainerView.setAlpha(t);
                 mScreenshotContainerView.setScaleX(scaleT);
                 mScreenshotContainerView.setScaleY(scaleT);
@@ -391,4 +437,19 @@
         });
         return anim;
     }
+
+    static void notifyScreenshotError(Context context, NotificationManager nManager) {
+        Resources r = context.getResources();
+
+        // Clear all existing notification, compose the new notification and show it
+        Notification n = new Notification.Builder(context)
+            .setTicker(r.getString(R.string.screenshot_failed_title))
+            .setContentTitle(r.getString(R.string.screenshot_failed_title))
+            .setContentText(r.getString(R.string.screenshot_failed_text))
+            .setSmallIcon(android.R.drawable.ic_menu_report_image)
+            .setWhen(System.currentTimeMillis())
+            .setAutoCancel(true)
+            .getNotification();
+        nManager.notify(SCREENSHOT_NOTIFICATION_ID, n);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 05ff8be..d112b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -17,26 +17,12 @@
 package com.android.systemui.screenshot;
 
 import android.app.Service;
-import android.app.AlertDialog;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
-import android.net.Uri;
-import android.hardware.usb.UsbAccessory;
-import android.hardware.usb.UsbManager;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
-
-import com.android.systemui.R;
 
 public class TakeScreenshotService extends Service {
     private static final String TAG = "TakeScreenshotService";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
index 5959537..2e1803e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
@@ -146,4 +146,26 @@
 
         mDoNotDisturb = new DoNotDisturb(mContext);
     }
+
+    protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
+        View vetoButton = row.findViewById(R.id.veto);
+        if (n.isClearable()) {
+            final String _pkg = n.pkg;
+            final String _tag = n.tag;
+            final int _id = n.id;
+            vetoButton.setOnClickListener(new View.OnClickListener() {
+                    public void onClick(View v) {
+                        try {
+                            mBarService.onNotificationClear(_pkg, _tag, _id);
+                        } catch (RemoteException ex) {
+                            // system process is dead if we're here.
+                        }
+                    }
+                });
+            vetoButton.setVisibility(View.VISIBLE);
+        } else {
+            vetoButton.setVisibility(View.GONE);
+        }
+        return vetoButton;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9c8c229..2ec5fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -618,6 +618,10 @@
         boolean orderUnchanged = notification.notification.when==oldNotification.notification.when
                 && notification.priority == oldNotification.priority;
                 // priority now encompasses isOngoing()
+
+        boolean updateTicker = notification.notification.tickerText != null
+                && !TextUtils.equals(notification.notification.tickerText,
+                        oldEntry.notification.notification.tickerText);
         boolean isFirstAnyway = rowParent.indexOfChild(oldEntry.row) == 0;
         if (contentsUnchanged && (orderUnchanged || isFirstAnyway)) {
             if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
@@ -665,10 +669,13 @@
             addNotificationViews(key, notification);
         }
 
+        // Update the veto button accordingly (and as a result, whether this row is
+        // swipe-dismissable)
+        updateNotificationVetoButton(oldEntry.row, notification);
+
         // Restart the ticker if it's still running
-        if (notification.notification.tickerText != null
-                && !TextUtils.equals(notification.notification.tickerText,
-                    oldEntry.notification.notification.tickerText)) {
+        if (updateTicker) {
+            mTicker.halt();
             tick(notification);
         }
 
@@ -711,23 +718,7 @@
         View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
 
         // wire up the veto button
-        View vetoButton = row.findViewById(R.id.veto);
-        if (notification.isClearable()) {
-            final String _pkg = notification.pkg;
-            final String _tag = notification.tag;
-            final int _id = notification.id;
-            vetoButton.setOnClickListener(new View.OnClickListener() {
-                    public void onClick(View v) {
-                        try {
-                            mBarService.onNotificationClear(_pkg, _tag, _id);
-                        } catch (RemoteException ex) {
-                            // system process is dead if we're here.
-                        }
-                    }
-                });
-        } else {
-            vetoButton.setVisibility(View.GONE);
-        }
+        View vetoButton = updateNotificationVetoButton(row, notification);
         vetoButton.setContentDescription(mContext.getString(
                 R.string.accessibility_remove_notification));
 
@@ -897,23 +888,7 @@
         LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
         View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
-        View vetoButton = row.findViewById(R.id.veto);
-        if (entry.notification.isClearable()) {
-            final String _pkg = sbn.pkg;
-            final String _tag = sbn.tag;
-            final int _id = sbn.id;
-            vetoButton.setOnClickListener(new View.OnClickListener() {
-                    public void onClick(View v) {
-                        try {
-                            mBarService.onNotificationClear(_pkg, _tag, _id);
-                        } catch (RemoteException ex) {
-                            // system process is dead if we're here.
-                        }
-                    }
-                });
-        } else {
-            vetoButton.setVisibility(View.GONE);
-        }
+        View vetoButton = updateNotificationVetoButton(row, sbn);
         vetoButton.setContentDescription(mContext.getString(
                 R.string.accessibility_remove_notification));
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 415a9a4..f0a10f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -840,6 +840,9 @@
         boolean orderUnchanged = notification.notification.when==oldNotification.notification.when
                 && notification.priority == oldNotification.priority;
                 // priority now encompasses isOngoing()
+        boolean updateTicker = notification.notification.tickerText != null
+                && !TextUtils.equals(notification.notification.tickerText,
+                        oldEntry.notification.notification.tickerText);
         boolean isLastAnyway = rowParent.indexOfChild(oldEntry.row) == rowParent.getChildCount()-1;
         if (contentsUnchanged && (orderUnchanged || isLastAnyway)) {
             if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
@@ -896,9 +899,8 @@
         }
 
         // Restart the ticker if it's still running
-        if (notification.notification.tickerText != null
-                && !TextUtils.equals(notification.notification.tickerText,
-                    oldEntry.notification.notification.tickerText)) {
+        if (updateTicker) {
+            mTicker.halt();
             tick(key, notification, false);
         }
 
@@ -1736,23 +1738,7 @@
                 Context.LAYOUT_INFLATER_SERVICE);
         View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
         workAroundBadLayerDrawableOpacity(row);
-        View vetoButton = row.findViewById(R.id.veto);
-        if (entry.notification.isClearable()) {
-            final String _pkg = sbn.pkg;
-            final String _tag = sbn.tag;
-            final int _id = sbn.id;
-            vetoButton.setOnClickListener(new View.OnClickListener() {
-                    public void onClick(View v) {
-                        try {
-                            mBarService.onNotificationClear(_pkg, _tag, _id);
-                        } catch (RemoteException ex) {
-                            // system process is dead if we're here.
-                        }
-                    }
-                });
-        } else {
-            vetoButton.setVisibility(View.GONE);
-        }
+        View vetoButton = updateNotificationVetoButton(row, entry.notification);
         vetoButton.setContentDescription(mContext.getString(
                 R.string.accessibility_remove_notification));