• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.server.status;
2 
3 import com.android.internal.R;
4 
5 import android.content.Context;
6 import android.graphics.drawable.Drawable;
7 import android.os.Handler;
8 import android.text.StaticLayout;
9 import android.text.Layout.Alignment;
10 import android.text.TextPaint;
11 import android.text.TextUtils;
12 import android.util.Log;
13 import android.view.View;
14 import android.view.animation.Animation;
15 import android.view.animation.AnimationUtils;
16 import android.widget.TextSwitcher;
17 import android.widget.TextView;
18 import android.widget.ImageSwitcher;
19 
20 import java.util.ArrayList;
21 
22 
23 abstract class Ticker {
24     private static final int TICKER_SEGMENT_DELAY = 3000;
25 
26     private final class Segment {
27         NotificationData notificationData;
28         Drawable icon;
29         CharSequence text;
30         int current;
31         int next;
32         boolean first;
33 
getLayout(CharSequence substr)34         StaticLayout getLayout(CharSequence substr) {
35             int w = mTextSwitcher.getWidth() - mTextSwitcher.getPaddingLeft()
36                     - mTextSwitcher.getPaddingRight();
37             return new StaticLayout(substr, mPaint, w, Alignment.ALIGN_NORMAL, 1, 0, true);
38         }
39 
rtrim(CharSequence substr, int start, int end)40         CharSequence rtrim(CharSequence substr, int start, int end) {
41             while (end > start && !TextUtils.isGraphic(substr.charAt(end-1))) {
42                 end--;
43             }
44             if (end > start) {
45                 return substr.subSequence(start, end);
46             }
47             return null;
48         }
49 
50         /** returns null if there is no more text */
getText()51         CharSequence getText() {
52             if (this.current > this.text.length()) {
53                 return null;
54             }
55             CharSequence substr = this.text.subSequence(this.current, this.text.length());
56             StaticLayout l = getLayout(substr);
57             int lineCount = l.getLineCount();
58             if (lineCount > 0) {
59                 int start = l.getLineStart(0);
60                 int end = l.getLineEnd(0);
61                 this.next = this.current + end;
62                 return rtrim(substr, start, end);
63             } else {
64                 throw new RuntimeException("lineCount=" + lineCount + " current=" + current +
65                         " text=" + text);
66             }
67         }
68 
69         /** returns null if there is no more text */
advance()70         CharSequence advance() {
71             this.first = false;
72             int index = this.next;
73             final int len = this.text.length();
74             while (index < len && !TextUtils.isGraphic(this.text.charAt(index))) {
75                 index++;
76             }
77             if (index >= len) {
78                 return null;
79             }
80 
81             CharSequence substr = this.text.subSequence(index, this.text.length());
82             StaticLayout l = getLayout(substr);
83             final int lineCount = l.getLineCount();
84             int i;
85             for (i=0; i<lineCount; i++) {
86                 int start = l.getLineStart(i);
87                 int end = l.getLineEnd(i);
88                 if (i == lineCount-1) {
89                     this.next = len;
90                 } else {
91                     this.next = index + l.getLineStart(i+1);
92                 }
93                 CharSequence result = rtrim(substr, start, end);
94                 if (result != null) {
95                     this.current = index + start;
96                     return result;
97                 }
98             }
99             this.current = len;
100             return null;
101         }
102 
Segment(NotificationData n, Drawable icon, CharSequence text)103         Segment(NotificationData n, Drawable icon, CharSequence text) {
104             this.notificationData = n;
105             this.icon = icon;
106             this.text = text;
107             int index = 0;
108             final int len = text.length();
109             while (index < len && !TextUtils.isGraphic(text.charAt(index))) {
110                 index++;
111             }
112             this.current = index;
113             this.next = index;
114             this.first = true;
115         }
116     };
117 
118     private Handler mHandler = new Handler();
119     private ArrayList<Segment> mSegments = new ArrayList();
120     private TextPaint mPaint;
121     private View mTickerView;
122     private ImageSwitcher mIconSwitcher;
123     private TextSwitcher mTextSwitcher;
124 
Ticker(Context context, StatusBarView sb)125     Ticker(Context context, StatusBarView sb) {
126         mTickerView = sb.findViewById(R.id.ticker);
127 
128         mIconSwitcher = (ImageSwitcher)sb.findViewById(R.id.tickerIcon);
129         mIconSwitcher.setInAnimation(
130                     AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_in));
131         mIconSwitcher.setOutAnimation(
132                     AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_out));
133 
134         mTextSwitcher = (TextSwitcher)sb.findViewById(R.id.tickerText);
135         mTextSwitcher.setInAnimation(
136                     AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_in));
137         mTextSwitcher.setOutAnimation(
138                     AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_out));
139 
140         // Copy the paint style of one of the TextSwitchers children to use later for measuring
141         TextView text = (TextView)mTextSwitcher.getChildAt(0);
142         mPaint = text.getPaint();
143     }
144 
addEntry(NotificationData n, Drawable icon, CharSequence text)145     void addEntry(NotificationData n, Drawable icon, CharSequence text) {
146         int initialCount = mSegments.size();
147 
148         Segment newSegment = new Segment(n, icon, text);
149 
150         // prune out any preexisting ones for this notification, but not the current one.
151         // let that finish, even if it's the same id
152         for (int i=1; i<initialCount; i++) {
153             Segment seg = mSegments.get(i);
154             if (n.id == seg.notificationData.id && n.pkg.equals(seg.notificationData.pkg)) {
155                 // just update that one to use this new data instead
156                 mSegments.set(i, newSegment);
157                 // and since we know initialCount != 0, just return
158                 return ;
159             }
160         }
161 
162         mSegments.add(newSegment);
163 
164         if (initialCount == 0 && mSegments.size() > 0) {
165             Segment seg = mSegments.get(0);
166             seg.first = false;
167 
168             mIconSwitcher.setAnimateFirstView(false);
169             mIconSwitcher.reset();
170             mIconSwitcher.setImageDrawable(seg.icon);
171 
172             mTextSwitcher.setAnimateFirstView(false);
173             mTextSwitcher.reset();
174             mTextSwitcher.setText(seg.getText());
175 
176             tickerStarting();
177             scheduleAdvance();
178         }
179     }
180 
halt()181     void halt() {
182         mHandler.removeCallbacks(mAdvanceTicker);
183         mSegments.clear();
184         tickerHalting();
185     }
186 
reflowText()187     void reflowText() {
188         if (mSegments.size() > 0) {
189             Segment seg = mSegments.get(0);
190             CharSequence text = seg.getText();
191             mTextSwitcher.setCurrentText(text);
192         }
193     }
194 
195     private Runnable mAdvanceTicker = new Runnable() {
196         public void run() {
197             while (mSegments.size() > 0) {
198                 Segment seg = mSegments.get(0);
199 
200                 if (seg.first) {
201                     // this makes the icon slide in for the first one for a given
202                     // notification even if there are two notifications with the
203                     // same icon in a row
204                     mIconSwitcher.setImageDrawable(seg.icon);
205                 }
206                 CharSequence text = seg.advance();
207                 if (text == null) {
208                     mSegments.remove(0);
209                     continue;
210                 }
211                 mTextSwitcher.setText(text);
212 
213                 scheduleAdvance();
214                 break;
215             }
216             if (mSegments.size() == 0) {
217                 tickerDone();
218             }
219         }
220     };
221 
scheduleAdvance()222     private void scheduleAdvance() {
223         mHandler.postDelayed(mAdvanceTicker, TICKER_SEGMENT_DELAY);
224     }
225 
tickerStarting()226     abstract void tickerStarting();
tickerDone()227     abstract void tickerDone();
tickerHalting()228     abstract void tickerHalting();
229 }
230 
231