TabletTicker.java revision 29fc2c9705e1bb8ae098fca016032d2325031587
1/* 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 19import java.util.Arrays; 20 21import android.app.Notification; 22import android.content.Context; 23import android.content.res.Resources; 24import android.graphics.Bitmap; 25import android.graphics.PixelFormat; 26import android.graphics.drawable.Drawable; 27import android.os.Handler; 28import android.os.Message; 29import android.util.Slog; 30import android.view.Gravity; 31import android.view.LayoutInflater; 32import android.view.View; 33import android.view.ViewGroup; 34import android.view.WindowManager; 35import android.view.WindowManagerImpl; 36import android.widget.FrameLayout; 37import android.widget.ImageView; 38import android.widget.LinearLayout; 39import android.widget.TextView; 40 41import com.android.internal.statusbar.StatusBarIcon; 42import com.android.internal.statusbar.StatusBarNotification; 43 44import com.android.systemui.R; 45import com.android.systemui.statusbar.StatusBarIconView; 46 47public class TabletTicker extends Handler { 48 private static final String TAG = "StatusBar.TabletTicker"; 49 50 private static final int MSG_ADVANCE = 1; 51 52 private static final int ADVANCE_DELAY = 5000; // 5 seconds 53 54 private Context mContext; 55 56 private ViewGroup mWindow; 57 private StatusBarNotification mCurrentNotification; 58 private View mCurrentView; 59 60 private StatusBarNotification[] mQueue; 61 private int mQueuePos; 62 63 public TabletTicker(Context context) { 64 mContext = context; 65 66 // TODO: Make this a configuration value. 67 // 3 is enough to let us see most cases, but not get so far behind that it's annoying. 68 mQueue = new StatusBarNotification[3]; 69 } 70 71 public void add(StatusBarNotification notification) { 72 if (false) { 73 Slog.d(TAG, "add mCurrentNotification=" + mCurrentNotification 74 + " mQueuePos=" + mQueuePos + " mQueue=" + Arrays.toString(mQueue)); 75 } 76 mQueue[mQueuePos] = notification; 77 78 // If nothing is running now, start the next one 79 if (mCurrentNotification == null) { 80 sendEmptyMessage(MSG_ADVANCE); 81 } 82 83 if (mQueuePos < mQueue.length - 1) { 84 mQueuePos++; 85 } 86 } 87 88 public void halt() { 89 removeMessages(MSG_ADVANCE); 90 if (mCurrentView != null) { 91 final int N = mQueue.length; 92 for (int i=0; i<N; i++) { 93 mQueue[i] = null; 94 } 95 mQueuePos = 0; 96 sendEmptyMessage(MSG_ADVANCE); 97 } 98 } 99 100 public void handleMessage(Message msg) { 101 switch (msg.what) { 102 case MSG_ADVANCE: 103 advance(); 104 break; 105 } 106 } 107 108 private void advance() { 109 // Out with the old... 110 if (mCurrentView != null) { 111 mWindow.removeView(mCurrentView); 112 mCurrentView = null; 113 mCurrentNotification = null; 114 } 115 116 // In with the new... 117 StatusBarNotification next = dequeue(); 118 while (next != null) { 119 mCurrentNotification = next; 120 mCurrentView = makeTickerView(next); 121 if (mCurrentView != null) { 122 if (mWindow == null) { 123 mWindow = makeWindow(); 124 WindowManagerImpl.getDefault().addView(mWindow, mWindow.getLayoutParams()); 125 } 126 mWindow.addView(mCurrentView); 127 sendEmptyMessageDelayed(MSG_ADVANCE, ADVANCE_DELAY); 128 break; 129 } 130 next = dequeue(); 131 } 132 133 // if there's nothing left, close the window 134 // TODO: Do this when the animation is done instead 135 if (mCurrentView == null) { 136 WindowManagerImpl.getDefault().removeView(mWindow); 137 mWindow = null; 138 } 139 } 140 141 private StatusBarNotification dequeue() { 142 StatusBarNotification notification = mQueue[0]; 143 if (false) { 144 Slog.d(TAG, "dequeue mQueuePos=" + mQueuePos + " mQueue=" + Arrays.toString(mQueue)); 145 } 146 final int N = mQueuePos; 147 for (int i=0; i<N; i++) { 148 mQueue[i] = mQueue[i+1]; 149 } 150 mQueue[N] = null; 151 if (mQueuePos > 0) { 152 mQueuePos--; 153 } 154 return notification; 155 } 156 157 private ViewGroup makeWindow() { 158 final Resources res = mContext.getResources(); 159 final FrameLayout view = new FrameLayout(mContext); 160 final int width = res.getDimensionPixelSize(R.dimen.notification_ticker_width); 161 final int height = res.getDimensionPixelSize(R.dimen.notification_large_icon_height); 162 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, 163 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, 164 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 165 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 166 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 167 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, 168 PixelFormat.TRANSLUCENT); 169 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; 170 lp.setTitle("NotificationTicker"); 171 view.setLayoutParams(lp); 172 return view; 173 } 174 175 private View makeTickerView(StatusBarNotification notification) { 176 final Notification n = notification.notification; 177 178 LayoutInflater inflater = (LayoutInflater)mContext.getSystemService( 179 Context.LAYOUT_INFLATER_SERVICE); 180 181 ViewGroup group; 182 int layoutId; 183 int iconId; 184 if (n.largeIcon != null) { 185 iconId = R.id.right_icon; 186 } else { 187 iconId = R.id.left_icon; 188 } 189 if (n.tickerView != null) { 190 group = (ViewGroup)inflater.inflate(R.layout.ticker, null, false); 191 View expanded = null; 192 Exception exception = null; 193 try { 194 expanded = n.tickerView.apply(mContext, group); 195 } 196 catch (RuntimeException e) { 197 exception = e; 198 } 199 if (expanded == null) { 200 final String ident = notification.pkg 201 + "/0x" + Integer.toHexString(notification.id); 202 Slog.e(TAG, "couldn't inflate view for notification " + ident, exception); 203 return null; 204 } 205 final int statusBarHeight = mContext.getResources().getDimensionPixelSize( 206 com.android.internal.R.dimen.status_bar_height); 207 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 208 ViewGroup.LayoutParams.WRAP_CONTENT, statusBarHeight, 1.0f); 209 lp.gravity = Gravity.BOTTOM; 210 group.addView(expanded, lp); 211 } else if (n.tickerText != null) { 212 group = (ViewGroup)inflater.inflate(R.layout.ticker_compat, mWindow, false); 213 final Drawable icon = StatusBarIconView.getIcon(mContext, 214 new StatusBarIcon(notification.pkg, n.icon, n.iconLevel, 0)); 215 ImageView iv = (ImageView)group.findViewById(iconId); 216 iv.setImageDrawable(icon); 217 iv.setVisibility(View.VISIBLE); 218 TextView tv = (TextView)group.findViewById(R.id.text); 219 tv.setText(n.tickerText); 220 } else { 221 throw new RuntimeException("tickerView==null && tickerText==null"); 222 } 223 ImageView largeIcon = (ImageView)group.findViewById(R.id.large_icon); 224 if (n.largeIcon != null) { 225 largeIcon.setImageBitmap(n.largeIcon); 226 largeIcon.setVisibility(View.VISIBLE); 227 } 228 return group; 229 } 230} 231 232