/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
- /*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.android.systemui.statusbar.phone;
- import android.content.Context;
- import android.content.res.Resources;
- import android.graphics.drawable.Drawable;
- import android.os.Handler;
- import android.service.notification.StatusBarNotification;
- import android.text.Layout.Alignment;
- import android.text.StaticLayout;
- import android.text.TextPaint;
- import android.view.View;
- import android.view.animation.AnimationUtils;
- import android.widget.ImageSwitcher;
- import android.widget.TextSwitcher;
- import android.widget.TextView;
- import com.android.internal.statusbar.StatusBarIcon;
- import com.android.systemui.R;
- import com.android.systemui.statusbar.StatusBarIconView;
- import java.util.ArrayList;
- public abstract class Ticker {
- private static final int TICKER_SEGMENT_DELAY = 3000;
- private Context mContext;
- private Handler mHandler = new Handler();
- private ArrayList<Segment> mSegments = new ArrayList();
- private TextPaint mPaint;
- private View mTickerView;
- private ImageSwitcher mIconSwitcher;
- private TextSwitcher mTextSwitcher;
- private float mIconScale;
- public static boolean isGraphicOrEmoji(char c) {
- int gc = Character.getType(c);
- return gc != Character.CONTROL
- && gc != Character.FORMAT
- && gc != Character.UNASSIGNED
- && gc != Character.LINE_SEPARATOR
- && gc != Character.PARAGRAPH_SEPARATOR
- && gc != Character.SPACE_SEPARATOR;
- }
- private final class Segment {
- StatusBarNotification notification;
- Drawable icon;
- CharSequence text;
- int current;
- int next;
- boolean first;
- StaticLayout getLayout(CharSequence substr) {
- int w = mTextSwitcher.getWidth() - mTextSwitcher.getPaddingLeft()
- - mTextSwitcher.getPaddingRight();
- return new StaticLayout(substr, mPaint, w, Alignment.ALIGN_NORMAL, 1, 0, true);
- }
- CharSequence rtrim(CharSequence substr, int start, int end) {
- while (end > start && !isGraphicOrEmoji(substr.charAt(end-1))) {
- end--;
- }
- if (end > start) {
- return substr.subSequence(start, end);
- }
- return null;
- }
- /** returns null if there is no more text */
- CharSequence getText() {
- if (this.current > this.text.length()) {
- return null;
- }
- CharSequence substr = this.text.subSequence(this.current, this.text.length());
- StaticLayout l = getLayout(substr);
- int lineCount = l.getLineCount();
- if (lineCount > 0) {
- int start = l.getLineStart(0);
- int end = l.getLineEnd(0);
- this.next = this.current + end;
- return rtrim(substr, start, end);
- } else {
- throw new RuntimeException("lineCount=" + lineCount + " current=" + current +
- " text=" + text);
- }
- }
- /** returns null if there is no more text */
- CharSequence advance() {
- this.first = false;
- int index = this.next;
- final int len = this.text.length();
- while (index < len && !isGraphicOrEmoji(this.text.charAt(index))) {
- index++;
- }
- if (index >= len) {
- return null;
- }
- CharSequence substr = this.text.subSequence(index, this.text.length());
- StaticLayout l = getLayout(substr);
- final int lineCount = l.getLineCount();
- int i;
- for (i=0; i<lineCount; i++) {
- int start = l.getLineStart(i);
- int end = l.getLineEnd(i);
- if (i == lineCount-1) {
- this.next = len;
- } else {
- this.next = index + l.getLineStart(i+1);
- }
- CharSequence result = rtrim(substr, start, end);
- if (result != null) {
- this.current = index + start;
- return result;
- }
- }
- this.current = len;
- return null;
- }
- Segment(StatusBarNotification n, Drawable icon, CharSequence text) {
- this.notification = n;
- this.icon = icon;
- this.text = text;
- int index = 0;
- final int len = text.length();
- while (index < len && !isGraphicOrEmoji(text.charAt(index))) {
- index++;
- }
- this.current = index;
- this.next = index;
- this.first = true;
- }
- };
- public Ticker(Context context, View sb) {
- mContext = context;
- final Resources res = context.getResources();
- final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
- final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
- mIconScale = (float)imageBounds / (float)outerBounds;
- mTickerView = sb.findViewById(R.id.ticker);
- mIconSwitcher = (ImageSwitcher)sb.findViewById(R.id.tickerIcon);
- mIconSwitcher.setInAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_in));
- mIconSwitcher.setOutAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_out));
- mIconSwitcher.setScaleX(mIconScale);
- mIconSwitcher.setScaleY(mIconScale);
- mTextSwitcher = (TextSwitcher)sb.findViewById(R.id.tickerText);
- mTextSwitcher.setInAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_in));
- mTextSwitcher.setOutAnimation(
- AnimationUtils.loadAnimation(context, com.android.internal.R.anim.push_up_out));
- // Copy the paint style of one of the TextSwitchers children to use later for measuring
- TextView text = (TextView)mTextSwitcher.getChildAt(0);
- mPaint = text.getPaint();
- }
- public void addEntry(StatusBarNotification n) {
- int initialCount = mSegments.size();
- // If what's being displayed has the same text and icon, just drop it
- // (which will let the current one finish, this happens when apps do
- // a notification storm).
- if (initialCount > 0) {
- final Segment seg = mSegments.get(0);
- if (n.getPackageName().equals(seg.notification.getPackageName())
- && n.getNotification().icon == seg.notification.getNotification().icon
- && n.getNotification().iconLevel == seg.notification.getNotification().iconLevel
- && charSequencesEqual(seg.notification.getNotification().tickerText,
- n.getNotification().tickerText)) {
- return;
- }
- }
- final Drawable icon = StatusBarIconView.getIcon(mContext,
- new StatusBarIcon(n.getPackageName(), n.getUser(), n.getNotification().icon, n.getNotification().iconLevel, 0,
- n.getNotification().tickerText));
- final CharSequence text = n.getNotification().tickerText;
- final Segment newSegment = new Segment(n, icon, text);
- // If there's already a notification schedule for this package and id, remove it.
- for (int i=0; i<mSegments.size(); i++) {
- Segment seg = mSegments.get(i);
- if (n.getId() == seg.notification.getId() && n.getPackageName().equals(seg.notification.getPackageName())) {
- // just update that one to use this new data instead
- mSegments.remove(i--); // restart iteration here
- }
- }
- mSegments.add(newSegment);
- if (initialCount == 0 && mSegments.size() > 0) {
- Segment seg = mSegments.get(0);
- seg.first = false;
- mIconSwitcher.setAnimateFirstView(false);
- mIconSwitcher.reset();
- mIconSwitcher.setImageDrawable(seg.icon);
- mTextSwitcher.setAnimateFirstView(false);
- mTextSwitcher.reset();
- mTextSwitcher.setText(seg.getText());
- tickerStarting();
- scheduleAdvance();
- }
- }
- private static boolean charSequencesEqual(CharSequence a, CharSequence b) {
- if (a.length() != b.length()) {
- return false;
- }
- int length = a.length();
- for (int i = 0; i < length; i++) {
- if (a.charAt(i) != b.charAt(i)) {
- return false;
- }
- }
- return true;
- }
- public void removeEntry(StatusBarNotification n) {
- for (int i=mSegments.size()-1; i>=0; i--) {
- Segment seg = mSegments.get(i);
- if (n.getId() == seg.notification.getId() && n.getPackageName().equals(seg.notification.getPackageName())) {
- mSegments.remove(i);
- }
- }
- }
- public void halt() {
- mHandler.removeCallbacks(mAdvanceTicker);
- mSegments.clear();
- tickerHalting();
- }
- public void reflowText() {
- if (mSegments.size() > 0) {
- Segment seg = mSegments.get(0);
- CharSequence text = seg.getText();
- mTextSwitcher.setCurrentText(text);
- }
- }
- private Runnable mAdvanceTicker = new Runnable() {
- public void run() {
- while (mSegments.size() > 0) {
- Segment seg = mSegments.get(0);
- if (seg.first) {
- // this makes the icon slide in for the first one for a given
- // notification even if there are two notifications with the
- // same icon in a row
- mIconSwitcher.setImageDrawable(seg.icon);
- }
- CharSequence text = seg.advance();
- if (text == null) {
- mSegments.remove(0);
- continue;
- }
- mTextSwitcher.setText(text);
- scheduleAdvance();
- break;
- }
- if (mSegments.size() == 0) {
- tickerDone();
- }
- }
- };
- private void scheduleAdvance() {
- mHandler.postDelayed(mAdvanceTicker, TICKER_SEGMENT_DELAY);
- }
- public abstract void tickerStarting();
- public abstract void tickerDone();
- public abstract void tickerHalting();
- }