PageRenderTime 71ms CodeModel.GetById 18ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 0ms

/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java

https://github.com/aizuzi/platform_frameworks_base
Java | 305 lines | 239 code | 40 blank | 26 comment | 58 complexity | 6522f44c079c3d3a7674dcd60fbd234f MD5 | raw file
  1/*
  2 * Copyright (C) 2008 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.phone;
 18
 19import android.content.Context;
 20import android.content.res.Resources;
 21import android.graphics.drawable.Drawable;
 22import android.os.Handler;
 23import android.service.notification.StatusBarNotification;
 24import android.text.Layout.Alignment;
 25import android.text.StaticLayout;
 26import android.text.TextPaint;
 27import android.view.View;
 28import android.view.animation.AnimationUtils;
 29import android.widget.ImageSwitcher;
 30import android.widget.TextSwitcher;
 31import android.widget.TextView;
 32
 33import com.android.internal.statusbar.StatusBarIcon;
 34import com.android.systemui.R;
 35import com.android.systemui.statusbar.StatusBarIconView;
 36
 37import java.util.ArrayList;
 38
 39public abstract class Ticker {
 40    private static final int TICKER_SEGMENT_DELAY = 3000;
 41
 42    private Context mContext;
 43    private Handler mHandler = new Handler();
 44    private ArrayList<Segment> mSegments = new ArrayList();
 45    private TextPaint mPaint;
 46    private View mTickerView;
 47    private ImageSwitcher mIconSwitcher;
 48    private TextSwitcher mTextSwitcher;
 49    private float mIconScale;
 50
 51    public static boolean isGraphicOrEmoji(char c) {
 52        int gc = Character.getType(c);
 53        return     gc != Character.CONTROL
 54                && gc != Character.FORMAT
 55                && gc != Character.UNASSIGNED
 56                && gc != Character.LINE_SEPARATOR
 57                && gc != Character.PARAGRAPH_SEPARATOR
 58                && gc != Character.SPACE_SEPARATOR;
 59    }
 60
 61    private final class Segment {
 62        StatusBarNotification notification;
 63        Drawable icon;
 64        CharSequence text;
 65        int current;
 66        int next;
 67        boolean first;
 68
 69        StaticLayout getLayout(CharSequence substr) {
 70            int w = mTextSwitcher.getWidth() - mTextSwitcher.getPaddingLeft()
 71                    - mTextSwitcher.getPaddingRight();
 72            return new StaticLayout(substr, mPaint, w, Alignment.ALIGN_NORMAL, 1, 0, true);
 73        }
 74
 75        CharSequence rtrim(CharSequence substr, int start, int end) {
 76            while (end > start && !isGraphicOrEmoji(substr.charAt(end-1))) {
 77                end--;
 78            }
 79            if (end > start) {
 80                return substr.subSequence(start, end);
 81            }
 82            return null;
 83        }
 84
 85        /** returns null if there is no more text */
 86        CharSequence getText() {
 87            if (this.current > this.text.length()) {
 88                return null;
 89            }
 90            CharSequence substr = this.text.subSequence(this.current, this.text.length());
 91            StaticLayout l = getLayout(substr);
 92            int lineCount = l.getLineCount();
 93            if (lineCount > 0) {
 94                int start = l.getLineStart(0);
 95                int end = l.getLineEnd(0);
 96                this.next = this.current + end;
 97                return rtrim(substr, start, end);
 98            } else {
 99                throw new RuntimeException("lineCount=" + lineCount + " current=" + current +
100                        " text=" + text);
101            }
102        }
103
104        /** returns null if there is no more text */
105        CharSequence advance() {
106            this.first = false;
107            int index = this.next;
108            final int len = this.text.length();
109            while (index < len && !isGraphicOrEmoji(this.text.charAt(index))) {
110                index++;
111            }
112            if (index >= len) {
113                return null;
114            }
115
116            CharSequence substr = this.text.subSequence(index, this.text.length());
117            StaticLayout l = getLayout(substr);
118            final int lineCount = l.getLineCount();
119            int i;
120            for (i=0; i<lineCount; i++) {
121                int start = l.getLineStart(i);
122                int end = l.getLineEnd(i);
123                if (i == lineCount-1) {
124                    this.next = len;
125                } else {
126                    this.next = index + l.getLineStart(i+1);
127                }
128                CharSequence result = rtrim(substr, start, end);
129                if (result != null) {
130                    this.current = index + start;
131                    return result;
132                }
133            }
134            this.current = len;
135            return null;
136        }
137
138        Segment(StatusBarNotification n, Drawable icon, CharSequence text) {
139            this.notification = n;
140            this.icon = icon;
141            this.text = text;
142            int index = 0;
143            final int len = text.length();
144            while (index < len && !isGraphicOrEmoji(text.charAt(index))) {
145                index++;
146            }
147            this.current = index;
148            this.next = index;
149            this.first = true;
150        }
151    };
152
153    public Ticker(Context context, View sb) {
154        mContext = context;
155        final Resources res = context.getResources();
156        final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
157        final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
158        mIconScale = (float)imageBounds / (float)outerBounds;
159
160        mTickerView = sb.findViewById(R.id.ticker);
161
162        mIconSwitcher = (ImageSwitcher)sb.findViewById(R.id.tickerIcon);
163        mIconSwitcher.setInAnimation(
164                    AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_in));
165        mIconSwitcher.setOutAnimation(
166                    AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_out));
167        mIconSwitcher.setScaleX(mIconScale);
168        mIconSwitcher.setScaleY(mIconScale);
169
170        mTextSwitcher = (TextSwitcher)sb.findViewById(R.id.tickerText);
171        mTextSwitcher.setInAnimation(
172                    AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_in));
173        mTextSwitcher.setOutAnimation(
174                    AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_out));
175
176        // Copy the paint style of one of the TextSwitchers children to use later for measuring
177        TextView text = (TextView)mTextSwitcher.getChildAt(0);
178        mPaint = text.getPaint();
179    }
180
181
182    public void addEntry(StatusBarNotification n) {
183        int initialCount = mSegments.size();
184
185        // If what's being displayed has the same text and icon, just drop it
186        // (which will let the current one finish, this happens when apps do
187        // a notification storm).
188        if (initialCount > 0) {
189            final Segment seg = mSegments.get(0);
190            if (n.getPackageName().equals(seg.notification.getPackageName())
191                    && n.getNotification().icon == seg.notification.getNotification().icon
192                    && n.getNotification().iconLevel == seg.notification.getNotification().iconLevel
193                    && charSequencesEqual(seg.notification.getNotification().tickerText,
194                        n.getNotification().tickerText)) {
195                return;
196            }
197        }
198
199        final Drawable icon = StatusBarIconView.getIcon(mContext,
200                new StatusBarIcon(n.getPackageName(), n.getUser(), n.getNotification().icon, n.getNotification().iconLevel, 0,
201                        n.getNotification().tickerText));
202        final CharSequence text = n.getNotification().tickerText;
203        final Segment newSegment = new Segment(n, icon, text);
204
205        // If there's already a notification schedule for this package and id, remove it.
206        for (int i=0; i<mSegments.size(); i++) {
207            Segment seg = mSegments.get(i);
208            if (n.getId() == seg.notification.getId() && n.getPackageName().equals(seg.notification.getPackageName())) {
209                // just update that one to use this new data instead
210                mSegments.remove(i--); // restart iteration here
211            }
212        }
213
214        mSegments.add(newSegment);
215
216        if (initialCount == 0 && mSegments.size() > 0) {
217            Segment seg = mSegments.get(0);
218            seg.first = false;
219
220            mIconSwitcher.setAnimateFirstView(false);
221            mIconSwitcher.reset();
222            mIconSwitcher.setImageDrawable(seg.icon);
223
224            mTextSwitcher.setAnimateFirstView(false);
225            mTextSwitcher.reset();
226            mTextSwitcher.setText(seg.getText());
227
228            tickerStarting();
229            scheduleAdvance();
230        }
231    }
232
233    private static boolean charSequencesEqual(CharSequence a, CharSequence b) {
234        if (a.length() != b.length()) {
235            return false;
236        }
237
238        int length = a.length();
239        for (int i = 0; i < length; i++) {
240            if (a.charAt(i) != b.charAt(i)) {
241                return false;
242            }
243        }
244        return true;
245    }
246
247    public void removeEntry(StatusBarNotification n) {
248        for (int i=mSegments.size()-1; i>=0; i--) {
249            Segment seg = mSegments.get(i);
250            if (n.getId() == seg.notification.getId() && n.getPackageName().equals(seg.notification.getPackageName())) {
251                mSegments.remove(i);
252            }
253        }
254    }
255
256    public void halt() {
257        mHandler.removeCallbacks(mAdvanceTicker);
258        mSegments.clear();
259        tickerHalting();
260    }
261
262    public void reflowText() {
263        if (mSegments.size() > 0) {
264            Segment seg = mSegments.get(0);
265            CharSequence text = seg.getText();
266            mTextSwitcher.setCurrentText(text);
267        }
268    }
269
270    private Runnable mAdvanceTicker = new Runnable() {
271        public void run() {
272            while (mSegments.size() > 0) {
273                Segment seg = mSegments.get(0);
274
275                if (seg.first) {
276                    // this makes the icon slide in for the first one for a given
277                    // notification even if there are two notifications with the
278                    // same icon in a row
279                    mIconSwitcher.setImageDrawable(seg.icon);
280                }
281                CharSequence text = seg.advance();
282                if (text == null) {
283                    mSegments.remove(0);
284                    continue;
285                }
286                mTextSwitcher.setText(text);
287
288                scheduleAdvance();
289                break;
290            }
291            if (mSegments.size() == 0) {
292                tickerDone();
293            }
294        }
295    };
296
297    private void scheduleAdvance() {
298        mHandler.postDelayed(mAdvanceTicker, TICKER_SEGMENT_DELAY);
299    }
300
301    public abstract void tickerStarting();
302    public abstract void tickerDone();
303    public abstract void tickerHalting();
304}
305