blob: ce38ebb9bea7e8d4e04334188c0230f11d02363e [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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 android.text;
18
19import android.graphics.Canvas;
20import android.graphics.Paint;
21import android.graphics.Path;
Eric Fischer86fcef82009-08-17 17:16:44 -070022import android.text.style.ParagraphStyle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023
24/**
25 * A BoringLayout is a very simple Layout implementation for text that
26 * fits on a single line and is all left-to-right characters.
27 * You will probably never want to make one of these yourself;
28 * if you do, be sure to call {@link #isBoring} first to make sure
29 * the text meets the criteria.
30 * <p>This class is used by widgets to control text layout. You should not need
31 * to use this class directly unless you are implementing your own widget
32 * or custom display object, in which case
33 * you are encouraged to use a Layout instead of calling
34 * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
35 * Canvas.drawText()} directly.</p>
36 */
37public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback {
Siyamed Sinir08fac622017-07-19 18:23:11 -070038
39 /**
40 * Utility function to construct a BoringLayout instance.
41 *
42 * @param source the text to render
43 * @param paint the default paint for the layout
44 * @param outerWidth the wrapping width for the text
45 * @param align whether to left, right, or center the text
46 * @param spacingMult this value is no longer used by BoringLayout
47 * @param spacingAdd this value is no longer used by BoringLayout
48 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
49 * line width
50 * @param includePad set whether to include extra space beyond font ascent and descent which is
51 * needed to avoid clipping in some scripts
52 */
53 public static BoringLayout make(CharSequence source, TextPaint paint, int outerWidth,
54 Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics,
55 boolean includePad) {
56 return new BoringLayout(source, paint, outerWidth, align, spacingMult, spacingAdd, metrics,
57 includePad);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 }
59
Siyamed Sinir08fac622017-07-19 18:23:11 -070060 /**
61 * Utility function to construct a BoringLayout instance.
62 *
63 * @param source the text to render
64 * @param paint the default paint for the layout
65 * @param outerWidth the wrapping width for the text
66 * @param align whether to left, right, or center the text
67 * @param spacingmult this value is no longer used by BoringLayout
68 * @param spacingadd this value is no longer used by BoringLayout
69 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
70 * line width
71 * @param includePad set whether to include extra space beyond font ascent and descent which is
72 * needed to avoid clipping in some scripts
73 * @param ellipsize whether to ellipsize the text if width of the text is longer than the
74 * requested width
75 * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
76 * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
77 * not used, {@code outerWidth} is used instead
78 */
79 public static BoringLayout make(CharSequence source, TextPaint paint, int outerWidth,
80 Alignment align, float spacingmult, float spacingadd, BoringLayout.Metrics metrics,
81 boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
82 return new BoringLayout(source, paint, outerWidth, align, spacingmult, spacingadd, metrics,
83 includePad, ellipsize, ellipsizedWidth);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 }
85
86 /**
87 * Returns a BoringLayout for the specified text, potentially reusing
88 * this one if it is already suitable. The caller must make sure that
89 * no one is still using this Layout.
Siyamed Sinir08fac622017-07-19 18:23:11 -070090 *
91 * @param source the text to render
92 * @param paint the default paint for the layout
93 * @param outerwidth the wrapping width for the text
94 * @param align whether to left, right, or center the text
95 * @param spacingMult this value is no longer used by BoringLayout
96 * @param spacingAdd this value is no longer used by BoringLayout
97 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
98 * line width
99 * @param includePad set whether to include extra space beyond font ascent and descent which is
100 * needed to avoid clipping in some scripts
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 */
Siyamed Sinir08fac622017-07-19 18:23:11 -0700102 public BoringLayout replaceOrMake(CharSequence source, TextPaint paint, int outerwidth,
103 Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics,
104 boolean includePad) {
105 replaceWith(source, paint, outerwidth, align, spacingMult, spacingAdd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106
107 mEllipsizedWidth = outerwidth;
108 mEllipsizedStart = 0;
109 mEllipsizedCount = 0;
110
Siyamed Sinir08fac622017-07-19 18:23:11 -0700111 init(source, paint, align, metrics, includePad, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 return this;
113 }
114
115 /**
116 * Returns a BoringLayout for the specified text, potentially reusing
117 * this one if it is already suitable. The caller must make sure that
118 * no one is still using this Layout.
Siyamed Sinir08fac622017-07-19 18:23:11 -0700119 *
120 * @param source the text to render
121 * @param paint the default paint for the layout
122 * @param outerWidth the wrapping width for the text
123 * @param align whether to left, right, or center the text
124 * @param spacingMult this value is no longer used by BoringLayout
125 * @param spacingAdd this value is no longer used by BoringLayout
126 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
127 * line width
128 * @param includePad set whether to include extra space beyond font ascent and descent which is
129 * needed to avoid clipping in some scripts
130 * @param ellipsize whether to ellipsize the text if width of the text is longer than the
131 * requested width
132 * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
133 * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
134 * not used, {@code outerwidth} is used instead
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 */
Siyamed Sinir08fac622017-07-19 18:23:11 -0700136 public BoringLayout replaceOrMake(CharSequence source, TextPaint paint, int outerWidth,
137 Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics,
138 boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 boolean trust;
140
141 if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
Siyamed Sinir08fac622017-07-19 18:23:11 -0700142 replaceWith(source, paint, outerWidth, align, spacingMult, spacingAdd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143
Siyamed Sinir08fac622017-07-19 18:23:11 -0700144 mEllipsizedWidth = outerWidth;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 mEllipsizedStart = 0;
146 mEllipsizedCount = 0;
147 trust = true;
148 } else {
Siyamed Sinir08fac622017-07-19 18:23:11 -0700149 replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this),
150 paint, outerWidth, align, spacingMult, spacingAdd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151
152 mEllipsizedWidth = ellipsizedWidth;
153 trust = false;
154 }
155
Siyamed Sinir08fac622017-07-19 18:23:11 -0700156 init(getText(), paint, align, metrics, includePad, trust);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 return this;
158 }
159
Siyamed Sinir08fac622017-07-19 18:23:11 -0700160 /**
161 * @param source the text to render
162 * @param paint the default paint for the layout
163 * @param outerwidth the wrapping width for the text
164 * @param align whether to left, right, or center the text
165 * @param spacingMult this value is no longer used by BoringLayout
166 * @param spacingAdd this value is no longer used by BoringLayout
167 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
168 * line width
169 * @param includePad set whether to include extra space beyond font ascent and descent which is
170 * needed to avoid clipping in some scripts
171 */
172 public BoringLayout(CharSequence source, TextPaint paint, int outerwidth, Alignment align,
173 float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad) {
174 super(source, paint, outerwidth, align, spacingMult, spacingAdd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175
176 mEllipsizedWidth = outerwidth;
177 mEllipsizedStart = 0;
178 mEllipsizedCount = 0;
179
Siyamed Sinir08fac622017-07-19 18:23:11 -0700180 init(source, paint, align, metrics, includePad, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 }
182
Siyamed Sinir08fac622017-07-19 18:23:11 -0700183 /**
184 *
185 * @param source the text to render
186 * @param paint the default paint for the layout
187 * @param outerWidth the wrapping width for the text
188 * @param align whether to left, right, or center the text
189 * @param spacingMult this value is no longer used by BoringLayout
190 * @param spacingAdd this value is no longer used by BoringLayout
191 * @param metrics {@code #Metrics} instance that contains information about FontMetrics and
192 * line width
193 * @param includePad set whether to include extra space beyond font ascent and descent which is
194 * needed to avoid clipping in some scripts
195 * @param ellipsize whether to ellipsize the text if width of the text is longer than the
196 * requested {@code outerwidth}
197 * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
198 * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
199 * not used, {@code outerwidth} is used instead
200 */
201 public BoringLayout(CharSequence source, TextPaint paint, int outerWidth, Alignment align,
202 float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad,
203 TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 /*
205 * It is silly to have to call super() and then replaceWith(),
206 * but we can't use "this" for the callback until the call to
207 * super() finishes.
208 */
Siyamed Sinir08fac622017-07-19 18:23:11 -0700209 super(source, paint, outerWidth, align, spacingMult, spacingAdd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210
211 boolean trust;
212
213 if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
Siyamed Sinir08fac622017-07-19 18:23:11 -0700214 mEllipsizedWidth = outerWidth;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 mEllipsizedStart = 0;
216 mEllipsizedCount = 0;
217 trust = true;
218 } else {
Siyamed Sinir08fac622017-07-19 18:23:11 -0700219 replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this),
220 paint, outerWidth, align, spacingMult, spacingAdd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221
222 mEllipsizedWidth = ellipsizedWidth;
223 trust = false;
224 }
225
Siyamed Sinir08fac622017-07-19 18:23:11 -0700226 init(getText(), paint, align, metrics, includePad, trust);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 }
228
Siyamed Sinir08fac622017-07-19 18:23:11 -0700229 /* package */ void init(CharSequence source, TextPaint paint, Alignment align,
230 BoringLayout.Metrics metrics, boolean includePad, boolean trustWidth) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 int spacing;
232
233 if (source instanceof String && align == Layout.Alignment.ALIGN_NORMAL) {
234 mDirect = source.toString();
235 } else {
236 mDirect = null;
237 }
238
239 mPaint = paint;
240
Siyamed Sinir08fac622017-07-19 18:23:11 -0700241 if (includePad) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 spacing = metrics.bottom - metrics.top;
Raph Levien07e6c232016-04-04 12:34:06 -0700243 mDesc = metrics.bottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 } else {
245 spacing = metrics.descent - metrics.ascent;
Raph Levien07e6c232016-04-04 12:34:06 -0700246 mDesc = metrics.descent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 }
248
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 mBottom = spacing;
250
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 if (trustWidth) {
252 mMax = metrics.width;
253 } else {
254 /*
255 * If we have ellipsized, we have to actually calculate the
256 * width because the width that was passed in was for the
257 * full text, not the ellipsized form.
258 */
Doug Felte8e45f22010-03-29 14:58:40 -0700259 TextLine line = TextLine.obtain();
260 line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT,
261 Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
Neil Fuller33253a42014-10-01 11:55:10 +0100262 mMax = (int) Math.ceil(line.metrics(null));
Doug Felte8e45f22010-03-29 14:58:40 -0700263 TextLine.recycle(line);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 }
265
Siyamed Sinir08fac622017-07-19 18:23:11 -0700266 if (includePad) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 mTopPadding = metrics.top - metrics.ascent;
268 mBottomPadding = metrics.bottom - metrics.descent;
269 }
270 }
271
272 /**
273 * Returns null if not boring; the width, ascent, and descent if boring.
274 */
Siyamed Sinir08fac622017-07-19 18:23:11 -0700275 public static Metrics isBoring(CharSequence text, TextPaint paint) {
Doug Feltcb3791202011-07-07 11:57:48 -0700276 return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, null);
277 }
278
279 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 * Returns null if not boring; the width, ascent, and descent in the
281 * provided Metrics object (or a new one if the provided one was null)
282 * if boring.
283 */
Gilles Debunnef483e512011-04-28 15:08:54 -0700284 public static Metrics isBoring(CharSequence text, TextPaint paint, Metrics metrics) {
Doug Feltcb3791202011-07-07 11:57:48 -0700285 return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, metrics);
286 }
287
288 /**
Roozbeh Pournader5536c4f2016-06-01 16:49:27 -0700289 * Returns true if the text contains any RTL characters, bidi format characters, or surrogate
290 * code units.
291 */
292 private static boolean hasAnyInterestingChars(CharSequence text, int textLength) {
293 final int MAX_BUF_LEN = 500;
294 final char[] buffer = TextUtils.obtain(MAX_BUF_LEN);
295 try {
296 for (int start = 0; start < textLength; start += MAX_BUF_LEN) {
297 final int end = Math.min(start + MAX_BUF_LEN, textLength);
298
299 // No need to worry about getting half codepoints, since we consider surrogate code
300 // units "interesting" as soon we see one.
301 TextUtils.getChars(text, start, end, buffer, 0);
302
303 final int len = end - start;
304 for (int i = 0; i < len; i++) {
305 final char c = buffer[i];
Roozbeh Pournader8823c852016-06-09 18:36:47 -0700306 if (c == '\n' || c == '\t' || TextUtils.couldAffectRtl(c)) {
Roozbeh Pournader5536c4f2016-06-01 16:49:27 -0700307 return true;
308 }
309 }
310 }
311 return false;
312 } finally {
313 TextUtils.recycle(buffer);
314 }
315 }
316
317 /**
Doug Feltcb3791202011-07-07 11:57:48 -0700318 * Returns null if not boring; the width, ascent, and descent in the
319 * provided Metrics object (or a new one if the provided one was null)
320 * if boring.
321 * @hide
322 */
323 public static Metrics isBoring(CharSequence text, TextPaint paint,
324 TextDirectionHeuristic textDir, Metrics metrics) {
Roozbeh Pournader3cf82082016-03-16 14:33:51 -0700325 final int textLength = text.length();
Roozbeh Pournader5536c4f2016-06-01 16:49:27 -0700326 if (hasAnyInterestingChars(text, textLength)) {
327 return null; // There are some interesting characters. Not boring.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 }
Roozbeh Pournader5536c4f2016-06-01 16:49:27 -0700329 if (textDir != null && textDir.isRtl(text, 0, textLength)) {
330 return null; // The heuristic considers the whole text RTL. Not boring.
331 }
332 if (text instanceof Spanned) {
Eric Fischer86fcef82009-08-17 17:16:44 -0700333 Spanned sp = (Spanned) text;
Roozbeh Pournader3cf82082016-03-16 14:33:51 -0700334 Object[] styles = sp.getSpans(0, textLength, ParagraphStyle.class);
Eric Fischer86fcef82009-08-17 17:16:44 -0700335 if (styles.length > 0) {
Roozbeh Pournader5536c4f2016-06-01 16:49:27 -0700336 return null; // There are some PargraphStyle spans. Not boring.
Eric Fischer86fcef82009-08-17 17:16:44 -0700337 }
338 }
339
Roozbeh Pournader5536c4f2016-06-01 16:49:27 -0700340 Metrics fm = metrics;
341 if (fm == null) {
342 fm = new Metrics();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 } else {
Roozbeh Pournader5536c4f2016-06-01 16:49:27 -0700344 fm.reset();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 }
Roozbeh Pournader5536c4f2016-06-01 16:49:27 -0700346
347 TextLine line = TextLine.obtain();
348 line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
349 Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
350 fm.width = (int) Math.ceil(line.metrics(fm));
351 TextLine.recycle(line);
352
353 return fm;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 }
355
Gilles Debunnef483e512011-04-28 15:08:54 -0700356 @Override
357 public int getHeight() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 return mBottom;
359 }
360
Gilles Debunnef483e512011-04-28 15:08:54 -0700361 @Override
362 public int getLineCount() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 return 1;
364 }
365
Gilles Debunnef483e512011-04-28 15:08:54 -0700366 @Override
367 public int getLineTop(int line) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 if (line == 0)
369 return 0;
370 else
371 return mBottom;
372 }
373
Gilles Debunnef483e512011-04-28 15:08:54 -0700374 @Override
375 public int getLineDescent(int line) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 return mDesc;
377 }
378
Gilles Debunnef483e512011-04-28 15:08:54 -0700379 @Override
380 public int getLineStart(int line) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 if (line == 0)
382 return 0;
383 else
384 return getText().length();
385 }
386
Gilles Debunnef483e512011-04-28 15:08:54 -0700387 @Override
388 public int getParagraphDirection(int line) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 return DIR_LEFT_TO_RIGHT;
390 }
391
Gilles Debunnef483e512011-04-28 15:08:54 -0700392 @Override
393 public boolean getLineContainsTab(int line) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 return false;
395 }
396
Gilles Debunnef483e512011-04-28 15:08:54 -0700397 @Override
398 public float getLineMax(int line) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 return mMax;
400 }
401
Gilles Debunnef483e512011-04-28 15:08:54 -0700402 @Override
John Reck44e8d602015-10-05 15:44:04 -0700403 public float getLineWidth(int line) {
404 return (line == 0 ? mMax : 0);
405 }
406
407 @Override
Gilles Debunnef483e512011-04-28 15:08:54 -0700408 public final Directions getLineDirections(int line) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 return Layout.DIRS_ALL_LEFT_TO_RIGHT;
410 }
411
Gilles Debunnef483e512011-04-28 15:08:54 -0700412 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 public int getTopPadding() {
414 return mTopPadding;
415 }
416
Gilles Debunnef483e512011-04-28 15:08:54 -0700417 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 public int getBottomPadding() {
419 return mBottomPadding;
420 }
421
422 @Override
423 public int getEllipsisCount(int line) {
424 return mEllipsizedCount;
425 }
426
427 @Override
428 public int getEllipsisStart(int line) {
429 return mEllipsizedStart;
430 }
431
432 @Override
433 public int getEllipsizedWidth() {
434 return mEllipsizedWidth;
435 }
436
437 // Override draw so it will be faster.
438 @Override
439 public void draw(Canvas c, Path highlight, Paint highlightpaint,
440 int cursorOffset) {
441 if (mDirect != null && highlight == null) {
442 c.drawText(mDirect, 0, mBottom - mDesc, mPaint);
443 } else {
444 super.draw(c, highlight, highlightpaint, cursorOffset);
445 }
446 }
447
448 /**
449 * Callback for the ellipsizer to report what region it ellipsized.
450 */
451 public void ellipsized(int start, int end) {
452 mEllipsizedStart = start;
453 mEllipsizedCount = end - start;
454 }
455
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 private String mDirect;
457 private Paint mPaint;
458
459 /* package */ int mBottom, mDesc; // for Direct
460 private int mTopPadding, mBottomPadding;
461 private float mMax;
462 private int mEllipsizedWidth, mEllipsizedStart, mEllipsizedCount;
463
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 public static class Metrics extends Paint.FontMetricsInt {
465 public int width;
Doug Felte8e45f22010-03-29 14:58:40 -0700466
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 @Override public String toString() {
468 return super.toString() + " width=" + width;
469 }
Siyamed Sinir70f660f2016-03-29 11:56:53 -0700470
471 private void reset() {
472 top = 0;
473 bottom = 0;
474 ascent = 0;
475 descent = 0;
476 width = 0;
477 leading = 0;
478 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 }
480}