blob: 095c4410552d59efe60530c5b391dd2bc0ad401d [file] [log] [blame]
Joe Onoratoef1e7762010-09-17 18:38:38 -04001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.statusbar.tablet;
18
Joe Onorato2026e8d2010-11-20 13:20:56 -080019import java.util.Arrays;
20
Daniel Sandlerc51451a2010-12-16 19:06:46 -050021import android.animation.LayoutTransition;
Joe Onoratoef1e7762010-09-17 18:38:38 -040022import android.app.Notification;
Daniel Sandlerc51451a2010-12-16 19:06:46 -050023import android.app.PendingIntent;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070024import android.service.notification.StatusBarNotification;
Joe Onoratoef1e7762010-09-17 18:38:38 -040025import android.content.Context;
Joe Onorato52f80cd2010-11-21 15:34:48 -080026import android.content.res.Resources;
Joe Onorato52f80cd2010-11-21 15:34:48 -080027import android.graphics.PixelFormat;
Joe Onorato2026e8d2010-11-20 13:20:56 -080028import android.graphics.drawable.Drawable;
Joe Onoratoef1e7762010-09-17 18:38:38 -040029import android.os.Handler;
Joe Onorato50ec5ec2010-11-28 17:15:26 -080030import android.os.IBinder;
Joe Onoratoef1e7762010-09-17 18:38:38 -040031import android.os.Message;
32import android.util.Slog;
33import android.view.Gravity;
34import android.view.LayoutInflater;
35import android.view.View;
36import android.view.ViewGroup;
Joe Onorato52f80cd2010-11-21 15:34:48 -080037import android.view.WindowManager;
Joe Onoratoef1e7762010-09-17 18:38:38 -040038import android.widget.FrameLayout;
39import android.widget.ImageView;
Joe Onoratoef1e7762010-09-17 18:38:38 -040040import android.widget.TextView;
41
Joe Onorato2026e8d2010-11-20 13:20:56 -080042import com.android.internal.statusbar.StatusBarIcon;
Joe Onoratoef1e7762010-09-17 18:38:38 -040043
44import com.android.systemui.R;
Joe Onorato2026e8d2010-11-20 13:20:56 -080045import com.android.systemui.statusbar.StatusBarIconView;
Joe Onoratoef1e7762010-09-17 18:38:38 -040046
Daniel Sandlerc51451a2010-12-16 19:06:46 -050047public class TabletTicker
48 extends Handler
49 implements LayoutTransition.TransitionListener {
50
Joe Onoratoef1e7762010-09-17 18:38:38 -040051 private static final String TAG = "StatusBar.TabletTicker";
52
Daniel Sandlere2c251b2011-01-27 20:39:12 -050053 private static final boolean CLICKABLE_TICKER = true;
Daniel Sandlerc51451a2010-12-16 19:06:46 -050054
Joe Onorato50ec5ec2010-11-28 17:15:26 -080055 // 3 is enough to let us see most cases, but not get so far behind that it's too annoying.
56 private static final int QUEUE_LENGTH = 3;
57
Joe Onoratoef1e7762010-09-17 18:38:38 -040058 private static final int MSG_ADVANCE = 1;
59
Joe Onorato6c1af932010-11-21 17:07:20 -080060 private static final int ADVANCE_DELAY = 5000; // 5 seconds
Joe Onoratoef1e7762010-09-17 18:38:38 -040061
Jeff Brown98365d72012-08-19 20:30:52 -070062 private final Context mContext;
63 private final WindowManager mWindowManager;
Joe Onoratoef1e7762010-09-17 18:38:38 -040064
Joe Onorato52f80cd2010-11-21 15:34:48 -080065 private ViewGroup mWindow;
Joe Onorato50ec5ec2010-11-28 17:15:26 -080066 private IBinder mCurrentKey;
Joe Onoratoef1e7762010-09-17 18:38:38 -040067 private StatusBarNotification mCurrentNotification;
68 private View mCurrentView;
69
Joe Onorato50ec5ec2010-11-28 17:15:26 -080070 private IBinder[] mKeys = new IBinder[QUEUE_LENGTH];
71 private StatusBarNotification[] mQueue = new StatusBarNotification[QUEUE_LENGTH];
Joe Onoratoef1e7762010-09-17 18:38:38 -040072 private int mQueuePos;
73
Daniel Sandlera3850b62011-12-06 13:36:03 -050074 private final int mLargeIconHeight;
75
Daniel Sandlerc51451a2010-12-16 19:06:46 -050076 private TabletStatusBar mBar;
77
78 private LayoutTransition mLayoutTransition;
79 private boolean mWindowShouldClose;
80
81 public TabletTicker(TabletStatusBar bar) {
82 mBar = bar;
83 mContext = bar.getContext();
Jeff Brown98365d72012-08-19 20:30:52 -070084 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
Daniel Sandlera3850b62011-12-06 13:36:03 -050085 final Resources res = mContext.getResources();
86 mLargeIconHeight = res.getDimensionPixelSize(
87 android.R.dimen.notification_large_icon_height);
Joe Onoratoef1e7762010-09-17 18:38:38 -040088 }
89
Joe Onorato50ec5ec2010-11-28 17:15:26 -080090 public void add(IBinder key, StatusBarNotification notification) {
Joe Onoratoef1e7762010-09-17 18:38:38 -040091 if (false) {
Joe Onorato50ec5ec2010-11-28 17:15:26 -080092 Slog.d(TAG, "add 1 mCurrentNotification=" + mCurrentNotification
Joe Onoratoef1e7762010-09-17 18:38:38 -040093 + " mQueuePos=" + mQueuePos + " mQueue=" + Arrays.toString(mQueue));
94 }
Joe Onorato50ec5ec2010-11-28 17:15:26 -080095
96 // If it's already in here, remove whatever's in there and put the new one at the end.
97 remove(key, false);
98
99 mKeys[mQueuePos] = key;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400100 mQueue[mQueuePos] = notification;
101
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800102 // If nothing is running now, start the next one.
103 if (mQueuePos == 0 && mCurrentNotification == null) {
Joe Onoratoef1e7762010-09-17 18:38:38 -0400104 sendEmptyMessage(MSG_ADVANCE);
105 }
106
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800107 if (mQueuePos < QUEUE_LENGTH - 1) {
Joe Onoratoef1e7762010-09-17 18:38:38 -0400108 mQueuePos++;
109 }
110 }
111
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800112 public void remove(IBinder key) {
113 remove(key, true);
114 }
115
116 public void remove(IBinder key, boolean advance) {
117 if (mCurrentKey == key) {
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800118 // Showing now
119 if (advance) {
120 removeMessages(MSG_ADVANCE);
121 sendEmptyMessage(MSG_ADVANCE);
122 }
123 } else {
124 // In the queue
125 for (int i=0; i<QUEUE_LENGTH; i++) {
126 if (mKeys[i] == key) {
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800127 for (; i<QUEUE_LENGTH-1; i++) {
128 mKeys[i] = mKeys[i+1];
129 mQueue[i] = mQueue[i+1];
130 }
131 mKeys[QUEUE_LENGTH-1] = null;
132 mQueue[QUEUE_LENGTH-1] = null;
133 if (mQueuePos > 0) {
134 mQueuePos--;
135 }
136 break;
137 }
138 }
139 }
140 }
141
Joe Onorato091e1b82010-09-26 18:04:44 -0700142 public void halt() {
143 removeMessages(MSG_ADVANCE);
Joe Onoratoaf5e4da2010-11-27 17:24:14 -0800144 if (mCurrentView != null || mQueuePos != 0) {
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800145 for (int i=0; i<QUEUE_LENGTH; i++) {
146 mKeys[i] = null;
Joe Onorato091e1b82010-09-26 18:04:44 -0700147 mQueue[i] = null;
148 }
149 mQueuePos = 0;
150 sendEmptyMessage(MSG_ADVANCE);
151 }
152 }
153
Joe Onoratoef1e7762010-09-17 18:38:38 -0400154 public void handleMessage(Message msg) {
155 switch (msg.what) {
156 case MSG_ADVANCE:
157 advance();
158 break;
159 }
160 }
161
162 private void advance() {
163 // Out with the old...
164 if (mCurrentView != null) {
Daniel Sandlerbec7d632011-02-15 14:24:58 -0500165 if (mWindow != null) {
166 mWindow.removeView(mCurrentView);
167 }
Joe Onoratoef1e7762010-09-17 18:38:38 -0400168 mCurrentView = null;
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800169 mCurrentKey = null;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400170 mCurrentNotification = null;
171 }
172
173 // In with the new...
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800174 dequeue();
175 while (mCurrentNotification != null) {
176 mCurrentView = makeTickerView(mCurrentNotification);
Joe Onorato46439ce2010-11-19 13:56:21 -0800177 if (mCurrentView != null) {
Joe Onorato52f80cd2010-11-21 15:34:48 -0800178 if (mWindow == null) {
179 mWindow = makeWindow();
Jeff Brown98365d72012-08-19 20:30:52 -0700180 mWindowManager.addView(mWindow, mWindow.getLayoutParams());
Joe Onorato52f80cd2010-11-21 15:34:48 -0800181 }
Daniel Sandler6aa2d3a2011-01-26 11:52:48 -0500182
Joe Onorato52f80cd2010-11-21 15:34:48 -0800183 mWindow.addView(mCurrentView);
Joe Onorato46439ce2010-11-19 13:56:21 -0800184 sendEmptyMessageDelayed(MSG_ADVANCE, ADVANCE_DELAY);
185 break;
186 }
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800187 dequeue();
Joe Onoratoef1e7762010-09-17 18:38:38 -0400188 }
Joe Onorato52f80cd2010-11-21 15:34:48 -0800189
190 // if there's nothing left, close the window
Daniel Sandlerc51451a2010-12-16 19:06:46 -0500191 mWindowShouldClose = (mCurrentView == null && mWindow != null);
Joe Onoratoef1e7762010-09-17 18:38:38 -0400192 }
193
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800194 private void dequeue() {
195 mCurrentKey = mKeys[0];
196 mCurrentNotification = mQueue[0];
Joe Onoratoef1e7762010-09-17 18:38:38 -0400197 if (false) {
198 Slog.d(TAG, "dequeue mQueuePos=" + mQueuePos + " mQueue=" + Arrays.toString(mQueue));
199 }
200 final int N = mQueuePos;
201 for (int i=0; i<N; i++) {
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800202 mKeys[i] = mKeys[i+1];
Joe Onoratoef1e7762010-09-17 18:38:38 -0400203 mQueue[i] = mQueue[i+1];
204 }
Joe Onorato50ec5ec2010-11-28 17:15:26 -0800205 mKeys[N] = null;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400206 mQueue[N] = null;
207 if (mQueuePos > 0) {
208 mQueuePos--;
209 }
Joe Onoratoef1e7762010-09-17 18:38:38 -0400210 }
211
Joe Onorato52f80cd2010-11-21 15:34:48 -0800212 private ViewGroup makeWindow() {
213 final Resources res = mContext.getResources();
214 final FrameLayout view = new FrameLayout(mContext);
215 final int width = res.getDimensionPixelSize(R.dimen.notification_ticker_width);
Joe Onoratoec51a822011-01-04 16:33:01 -0800216 int windowFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Joe Onorato6c1af932010-11-21 17:07:20 -0800217 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
Joe Onoratoec51a822011-01-04 16:33:01 -0800218 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
219 if (CLICKABLE_TICKER) {
220 windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
221 } else {
222 windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
223 }
Daniel Sandlera3850b62011-12-06 13:36:03 -0500224 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, mLargeIconHeight,
Daniel Sandler4ce64fb2012-05-03 10:53:10 -0400225 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, windowFlags,
Joe Onorato52f80cd2010-11-21 15:34:48 -0800226 PixelFormat.TRANSLUCENT);
Fabrice Di Meglio8afcd142012-07-27 18:27:11 -0700227 lp.gravity = Gravity.BOTTOM | Gravity.END;
Daniel Sandlerc51451a2010-12-16 19:06:46 -0500228// lp.windowAnimations = com.android.internal.R.style.Animation_Toast;
229
230 mLayoutTransition = new LayoutTransition();
231 mLayoutTransition.addTransitionListener(this);
232 view.setLayoutTransition(mLayoutTransition);
Joe Onorato52f80cd2010-11-21 15:34:48 -0800233 lp.setTitle("NotificationTicker");
234 view.setLayoutParams(lp);
235 return view;
236 }
237
Daniel Sandlerc51451a2010-12-16 19:06:46 -0500238 public void startTransition(LayoutTransition transition, ViewGroup container,
239 View view, int transitionType) {}
240
241 public void endTransition(LayoutTransition transition, ViewGroup container,
242 View view, int transitionType) {
243 if (mWindowShouldClose) {
Jeff Brown98365d72012-08-19 20:30:52 -0700244 mWindowManager.removeView(mWindow);
Daniel Sandlerc51451a2010-12-16 19:06:46 -0500245 mWindow = null;
246 mWindowShouldClose = false;
Daniel Sandlerb8027d82010-12-16 19:35:54 -0500247 mBar.doneTicking();
Daniel Sandlerc51451a2010-12-16 19:06:46 -0500248 }
249 }
250
Joe Onoratoef1e7762010-09-17 18:38:38 -0400251 private View makeTickerView(StatusBarNotification notification) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400252 final Notification n = notification.getNotification();
Joe Onoratoef1e7762010-09-17 18:38:38 -0400253
254 LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
255 Context.LAYOUT_INFLATER_SERVICE);
256
Joe Onoratoef1e7762010-09-17 18:38:38 -0400257 ViewGroup group;
Joe Onorato46439ce2010-11-19 13:56:21 -0800258 int layoutId;
Joe Onorato561d3852010-11-20 18:09:34 -0800259 int iconId;
260 if (n.largeIcon != null) {
261 iconId = R.id.right_icon;
262 } else {
263 iconId = R.id.left_icon;
264 }
Joe Onorato46439ce2010-11-19 13:56:21 -0800265 if (n.tickerView != null) {
Daniel Sandler4a066c52012-04-20 14:49:13 -0400266 group = (ViewGroup)inflater.inflate(R.layout.system_bar_ticker_panel, null, false);
Daniel Sandler6aa2d3a2011-01-26 11:52:48 -0500267 ViewGroup content = (FrameLayout) group.findViewById(R.id.ticker_expanded);
Joe Onorato46439ce2010-11-19 13:56:21 -0800268 View expanded = null;
269 Exception exception = null;
270 try {
Daniel Sandler6aa2d3a2011-01-26 11:52:48 -0500271 expanded = n.tickerView.apply(mContext, content);
Joe Onoratoef1e7762010-09-17 18:38:38 -0400272 }
Joe Onorato46439ce2010-11-19 13:56:21 -0800273 catch (RuntimeException e) {
274 exception = e;
Joe Onoratoef1e7762010-09-17 18:38:38 -0400275 }
Joe Onorato46439ce2010-11-19 13:56:21 -0800276 if (expanded == null) {
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400277 final String ident = notification.getPackageName()
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400278 + "/0x" + Integer.toHexString(notification.getId());
Joe Onorato46439ce2010-11-19 13:56:21 -0800279 Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
280 return null;
281 }
Daniel Sandler6aa2d3a2011-01-26 11:52:48 -0500282 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
283 ViewGroup.LayoutParams.MATCH_PARENT,
284 ViewGroup.LayoutParams.MATCH_PARENT);
285 content.addView(expanded, lp);
Joe Onorato46439ce2010-11-19 13:56:21 -0800286 } else if (n.tickerText != null) {
Daniel Sandler4a066c52012-04-20 14:49:13 -0400287 group = (ViewGroup)inflater.inflate(R.layout.system_bar_ticker_compat, mWindow, false);
Joe Onorato2026e8d2010-11-20 13:20:56 -0800288 final Drawable icon = StatusBarIconView.getIcon(mContext,
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400289 new StatusBarIcon(notification.getPackageName(), notification.getUser(), n.icon, n.iconLevel, 0,
Amith Yamasani98edc952012-09-25 14:09:27 -0700290 n.tickerText));
Joe Onorato561d3852010-11-20 18:09:34 -0800291 ImageView iv = (ImageView)group.findViewById(iconId);
Joe Onorato2026e8d2010-11-20 13:20:56 -0800292 iv.setImageDrawable(icon);
Joe Onorato561d3852010-11-20 18:09:34 -0800293 iv.setVisibility(View.VISIBLE);
Joe Onoratoef1e7762010-09-17 18:38:38 -0400294 TextView tv = (TextView)group.findViewById(R.id.text);
295 tv.setText(n.tickerText);
Joe Onorato46439ce2010-11-19 13:56:21 -0800296 } else {
297 throw new RuntimeException("tickerView==null && tickerText==null");
Joe Onoratoef1e7762010-09-17 18:38:38 -0400298 }
Joe Onorato561d3852010-11-20 18:09:34 -0800299 ImageView largeIcon = (ImageView)group.findViewById(R.id.large_icon);
300 if (n.largeIcon != null) {
301 largeIcon.setImageBitmap(n.largeIcon);
302 largeIcon.setVisibility(View.VISIBLE);
Daniel Sandlera3850b62011-12-06 13:36:03 -0500303 final ViewGroup.LayoutParams lp = largeIcon.getLayoutParams();
304 final int statusBarHeight = mBar.getStatusBarHeight();
305 if (n.largeIcon.getHeight() <= statusBarHeight) {
306 // for smallish largeIcons, it looks a little odd to have them floating halfway up
307 // the ticker, so we vertically center them in the status bar area instead
308 lp.height = statusBarHeight;
309 } else {
310 lp.height = mLargeIconHeight;
311 }
312 largeIcon.setLayoutParams(lp);
Joe Onorato561d3852010-11-20 18:09:34 -0800313 }
Daniel Sandlerc51451a2010-12-16 19:06:46 -0500314
315 if (CLICKABLE_TICKER) {
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400316 PendingIntent contentIntent = notification.getNotification().contentIntent;
Daniel Sandlerc51451a2010-12-16 19:06:46 -0500317 if (contentIntent != null) {
Daniel Sandlere2c251b2011-01-27 20:39:12 -0500318 // create the usual notification clicker, but chain it together with a halt() call
319 // to abort the ticker too
320 final View.OnClickListener clicker = mBar.makeClicker(contentIntent,
Daniel Sandler4f91efd2013-04-25 16:38:41 -0400321 notification.getPackageName(), notification.getTag(), notification.getId());
Daniel Sandlere2c251b2011-01-27 20:39:12 -0500322 group.setOnClickListener(new View.OnClickListener() {
323 public void onClick(View v) {
324 halt();
325 clicker.onClick(v);
326 }
327 });
Daniel Sandlerc51451a2010-12-16 19:06:46 -0500328 } else {
329 group.setOnClickListener(null);
330 }
331 }
332
Joe Onoratoef1e7762010-09-17 18:38:38 -0400333 return group;
334 }
335}
336