diff options
73 files changed, 1356 insertions, 591 deletions
diff --git a/api/11.xml b/api/11.xml index 4a40c3670b0e..291d8dbdbcfd 100644 --- a/api/11.xml +++ b/api/11.xml @@ -29418,6 +29418,17 @@ <parameter name="uri" type="android.net.Uri"> </parameter> </method> +<method name="setDownloadToBeOtaUpdate" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="setMimeType" return="android.app.DownloadManager.Request" abstract="false" diff --git a/api/current.xml b/api/current.xml index 4a40c3670b0e..291d8dbdbcfd 100644 --- a/api/current.xml +++ b/api/current.xml @@ -29418,6 +29418,17 @@ <parameter name="uri" type="android.net.Uri"> </parameter> </method> +<method name="setDownloadToBeOtaUpdate" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="setMimeType" return="android.app.DownloadManager.Request" abstract="false" diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 297d24604d00..d37983f3feb9 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -339,6 +339,7 @@ public class DownloadManager { private boolean mIsVisibleInDownloadsUi = true; private boolean mScannable = false; private boolean mUseSystemCache = false; + private boolean mOtaUpdate = false; /** if a file is designated as a MediaScanner scannable file, the following value is * stored in the database column {@link Downloads.Impl#COLUMN_MEDIA_SCANNED}. */ @@ -605,6 +606,13 @@ public class DownloadManager { } /** + * Set whether the download request is OTA Update. By default, it is set to false. + */ + public void setDownloadToBeOtaUpdate() { + mOtaUpdate = true; + } + + /** * Set whether this download should be displayed in the system's Downloads UI. True by * default. * @param isVisible whether to display this download in the Downloads UI @@ -650,6 +658,7 @@ public class DownloadManager { values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes); values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed); values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, mIsVisibleInDownloadsUi); + values.put(Downloads.Impl.COLUMN_IGNORE_SIZE_LIMITS, mOtaUpdate); return values; } diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index 013edd395ae7..ad0140bd57d2 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -397,6 +397,12 @@ public final class Downloads { */ public static final String COLUMN_LAST_UPDATESRC = "lastUpdateSrc"; + /** Set this column to true if the given download should ignore the mobile network + * download limits. + * <P>Type: Boolean</P> + */ + public static final String COLUMN_IGNORE_SIZE_LIMITS = "otaupdate"; + /** * default value for {@link #COLUMN_LAST_UPDATESRC}. * This value is used when this column's value is not relevant. diff --git a/core/java/android/view/GLES20Layer.java b/core/java/android/view/GLES20Layer.java index 02304304c78d..6000a4a53bff 100644 --- a/core/java/android/view/GLES20Layer.java +++ b/core/java/android/view/GLES20Layer.java @@ -66,10 +66,11 @@ class GLES20Layer extends HardwareLayer { @Override void resize(int width, int height) { if (!isValid() || width <= 0 || height <= 0) return; - if (width > mLayerWidth || height > mLayerHeight) { - mWidth = width; - mHeight = height; + mWidth = width; + mHeight = height; + + if (width != mLayerWidth || height != mLayerHeight) { int[] layerInfo = new int[2]; GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 48f40c304307..71d55f7a476d 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -547,7 +547,7 @@ public abstract class HardwareRenderer { } onPreDraw(dirty); - + HardwareCanvas canvas = mCanvas; attachInfo.mHardwareCanvas = canvas; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 393412f0e6a0..1ecf320c235a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6407,6 +6407,53 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * @hide + */ + public void setFastX(float x) { + mTranslationX = x - mLeft; + mMatrixDirty = true; + } + + /** + * @hide + */ + public void setFastY(float y) { + mTranslationY = y - mTop; + mMatrixDirty = true; + } + + /** + * @hide + */ + public void setFastScaleX(float x) { + mScaleX = x; + mMatrixDirty = true; + } + + /** + * @hide + */ + public void setFastScaleY(float y) { + mScaleY = y; + mMatrixDirty = true; + } + + /** + * @hide + */ + public void setFastAlpha(float alpha) { + mAlpha = alpha; + } + + /** + * @hide + */ + public void setFastRotationY(float y) { + mRotationY = y; + mMatrixDirty = true; + } + + /** * Hit rectangle in parent's coordinates * * @param outRect The hit rectangle of the view. @@ -6947,6 +6994,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * @hide + */ + public void fastInvalidate() { + if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) || + (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID || + (mPrivateFlags & INVALIDATED) != INVALIDATED) { + if (mParent instanceof View) { + ((View) mParent).mPrivateFlags |= INVALIDATED; + } + mPrivateFlags &= ~DRAWN; + mPrivateFlags |= INVALIDATED; + mPrivateFlags &= ~DRAWING_CACHE_VALID; + if (mParent != null && mAttachInfo != null && mAttachInfo.mHardwareAccelerated) { + mParent.invalidateChild(this, null); + } + } + } + + /** * Used to indicate that the parent of this view should clear its caches. This functionality * is used to force the parent to rebuild its display list (when hardware-accelerated), * which is necessary when various parent-managed properties of the view change, such as @@ -8221,8 +8287,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (mHardwareLayer == null) { mHardwareLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer( width, height, isOpaque()); + mLocalDirtyRect.setEmpty(); } else if (mHardwareLayer.getWidth() != width || mHardwareLayer.getHeight() != height) { mHardwareLayer.resize(width, height); + mLocalDirtyRect.setEmpty(); } Canvas currentCanvas = mAttachInfo.mHardwareCanvas; @@ -8231,6 +8299,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility try { canvas.setViewport(width, height); canvas.onPreDraw(mLocalDirtyRect); + mLocalDirtyRect.setEmpty(); final int restoreCount = canvas.save(); @@ -8252,7 +8321,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility canvas.onPostDraw(); mHardwareLayer.end(currentCanvas); mAttachInfo.mHardwareCanvas = currentCanvas; - mLocalDirtyRect.setEmpty(); } } diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/com/android/internal/statusbar/StatusBarNotification.java index 2b96bf69fff3..cb791be03164 100644 --- a/core/java/com/android/internal/statusbar/StatusBarNotification.java +++ b/core/java/com/android/internal/statusbar/StatusBarNotification.java @@ -35,12 +35,18 @@ if (truncatedTicker != null && truncatedTicker.length() > maxTickerLen) { */ public class StatusBarNotification implements Parcelable { + public static int PRIORITY_JIFFY_EXPRESS = -100; + public static int PRIORITY_NORMAL = 0; + public static int PRIORITY_ONGOING = 100; + public static int PRIORITY_SYSTEM = 200; + public String pkg; public int id; public String tag; public int uid; public int initialPid; public Notification notification; + public int priority = PRIORITY_NORMAL; public StatusBarNotification() { } @@ -56,6 +62,9 @@ public class StatusBarNotification implements Parcelable { this.uid = uid; this.initialPid = initialPid; this.notification = notification; + + this.priority = ((notification.flags & Notification.FLAG_ONGOING_EVENT) != 0) + ? PRIORITY_ONGOING : PRIORITY_NORMAL; } public StatusBarNotification(Parcel in) { @@ -72,6 +81,7 @@ public class StatusBarNotification implements Parcelable { } this.uid = in.readInt(); this.initialPid = in.readInt(); + this.priority = in.readInt(); this.notification = new Notification(in); } @@ -86,6 +96,7 @@ public class StatusBarNotification implements Parcelable { } out.writeInt(this.uid); out.writeInt(this.initialPid); + out.writeInt(this.priority); this.notification.writeToParcel(out, flags); } @@ -114,7 +125,7 @@ public class StatusBarNotification implements Parcelable { public String toString() { return "StatusBarNotification(package=" + pkg + " id=" + id + " tag=" + tag - + " notification=" + notification + ")"; + + " notification=" + notification + " priority=" + priority + ")"; } public boolean isOngoing() { diff --git a/core/res/res/drawable-hdpi/ic_audio_alarm.png b/core/res/res/drawable-hdpi/ic_audio_alarm.png Binary files differnew file mode 100644 index 000000000000..1b41de4f70cb --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_audio_alarm.png diff --git a/core/res/res/drawable-hdpi/ic_audio_alarm_mute.png b/core/res/res/drawable-hdpi/ic_audio_alarm_mute.png Binary files differnew file mode 100644 index 000000000000..e31fdb857e18 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_audio_alarm_mute.png diff --git a/core/res/res/drawable-hdpi/ic_audio_notification.png b/core/res/res/drawable-hdpi/ic_audio_notification.png Binary files differnew file mode 100644 index 000000000000..00e8f8aec728 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_audio_notification.png diff --git a/core/res/res/drawable-hdpi/ic_audio_notification_mute.png b/core/res/res/drawable-hdpi/ic_audio_notification_mute.png Binary files differnew file mode 100644 index 000000000000..a350e1679fba --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_audio_notification_mute.png diff --git a/core/res/res/drawable-hdpi/ic_audio_vol.png b/core/res/res/drawable-hdpi/ic_audio_vol.png Binary files differnew file mode 100644 index 000000000000..cf3f3f54c514 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_audio_vol.png diff --git a/core/res/res/drawable-hdpi/ic_audio_vol_mute.png b/core/res/res/drawable-hdpi/ic_audio_vol_mute.png Binary files differnew file mode 100644 index 000000000000..c4ac4ef56618 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_audio_vol_mute.png diff --git a/core/res/res/drawable-mdpi/ic_audio_alarm.png b/core/res/res/drawable-mdpi/ic_audio_alarm.png Binary files differnew file mode 100644 index 000000000000..fab95aadf09b --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_audio_alarm.png diff --git a/core/res/res/drawable-mdpi/ic_audio_alarm_mute.png b/core/res/res/drawable-mdpi/ic_audio_alarm_mute.png Binary files differnew file mode 100644 index 000000000000..ca3ed933983e --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_audio_alarm_mute.png diff --git a/core/res/res/drawable-mdpi/ic_audio_notification.png b/core/res/res/drawable-mdpi/ic_audio_notification.png Binary files differnew file mode 100644 index 000000000000..b41ccd09cb15 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_audio_notification.png diff --git a/core/res/res/drawable-mdpi/ic_audio_notification_mute.png b/core/res/res/drawable-mdpi/ic_audio_notification_mute.png Binary files differnew file mode 100644 index 000000000000..f0b6d8ab18db --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_audio_notification_mute.png diff --git a/core/res/res/drawable-mdpi/ic_audio_vol.png b/core/res/res/drawable-mdpi/ic_audio_vol.png Binary files differnew file mode 100644 index 000000000000..049e92ab4126 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_audio_vol.png diff --git a/core/res/res/drawable-mdpi/ic_audio_vol_mute.png b/core/res/res/drawable-mdpi/ic_audio_vol_mute.png Binary files differnew file mode 100644 index 000000000000..be71492984b3 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_audio_vol_mute.png diff --git a/data/sounds/effects/ogg/Effect_Tick.ogg b/data/sounds/effects/ogg/Effect_Tick.ogg Binary files differindex b3790199b8b1..a997fe164ee4 100644 --- a/data/sounds/effects/ogg/Effect_Tick.ogg +++ b/data/sounds/effects/ogg/Effect_Tick.ogg diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd index 376bf6a9c844..b8d1928f9bf9 100644 --- a/docs/html/guide/topics/ui/actionbar.jd +++ b/docs/html/guide/topics/ui/actionbar.jd @@ -12,20 +12,25 @@ parent.link=index.html <li>Provides action items from the Options Menu and modes of navigating around the application</li> <li>Supports custom views, including an embedded search box</li> - <li>Requires API Level HONEYCOMB</li> + <li>Requires API Level 11</li> </ul> <h2>In this document</h2> <ol> - <li><a href="#Adding">Adding the Action Bar</a></li> + <li><a href="#Adding">Adding the Action Bar</a> + <ol> + <li><a href="#Removing">Removing the Action Bar</a></li> + </ol> + </li> <li><a href="#ActionItems">Adding Action Items</a> <ol> - <li><a href="#Home">Using the application icon as an action item</a></li> + <li><a href="#Home">Using the app icon as an action item</a></li> </ol> </li> <li><a href="#ActionView">Adding an Action View</a></li> <li><a href="#Tabs">Adding Tabs</a></li> <li><a href="#Dropdown">Adding Drop-down Navigation</a></li> + <li><a href="#Style">Styling the Action Bar</a></li> </ol> <h2>Key classes</h2> @@ -42,96 +47,129 @@ application</li> </div> <p>The Action Bar is a widget for activities that replaces the traditional title bar at -the top of an activity. By default, the Action Bar includes the application logo on the left side, -followed by the activity title. The Action Bar offers several useful features for -applications—especially those targeted to tablet devices. The Action Bar features include -the ability to:</p> +the top of the screen. By default, the Action Bar includes the application logo on the left side, +followed by the activity title, and any available items from the Options Menu on the right side. The +Action Bar offers several useful features, including the ability to:</p> <ul> <li>Display items from the <a -href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">Options Menu</a> as "action -items"—providing instant access to key user actions. (Menu items not appearing as action -items are placed in the Overflow Menu, revealed by a drop-down in the Action Bar.)</li> +href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">Options Menu</a> directly in the Action +Bar, as "action +items"—providing instant access to key user actions. + <p>Menu items that do not appear as action items are placed in the overflow menu, revealed +by a drop-down list in the Action Bar.</p></li> <li>Provide tabs for navigating between <a href="{@docRoot}guide/topics/fundamentals/fragments.html">fragments</a>.</li> - <li>Provide drop-down navigation items.</li> - <li>Provide interactive "action views" in place of action items.</li> - <li>Use the application logo as a "return home" or "up" navigation action.</li> + <li>Provide a drop-down list for navigation.</li> + <li>Provide interactive "action views" in place of action items (such as a search box).</li> </ul> <img src="{@docRoot}images/ui/actionbar.png" height="36" alt="" /> -<p class="img-caption"><strong>Figure 1.</strong> A screenshot of the Action Bar in the NotePad -sample application, containing action items to save and delete the note.</p> + +<p class="img-caption"><strong>Figure 1.</strong> A screenshot of the Action Bar in the Email +application, containing action items to compose new email and refresh the inbox.</p> <h2 id="Adding">Adding the Action Bar</h2> -<p>To add the Action Bar to your activities, simply target your application for HONEYCOMB or later, -using the <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code -<uses-sdk>}</a> element. That is, by setting either the {@code android:minSdkVersion} or -{@code android:targetSdkVersion} to HONEYCOMB or later, each activity in your application will -include the Action Bar when running on devices with HONEYCOMB or later. For example:</p> +<p>The Action Bar is included by default in all activities that target Android 3.0 or greater. More +specifically, all activities that use the new "holographic" theme include the Action Bar, and any +application that targets Android 3.0 automatically receives this theme. An application is considered +to "target" Android 3.0 when it has set either the {@code android:minSdkVersion} or {@code +android:targetSdkVersion} attribute in the <a +href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a> element to +{@code "11"} or greater. For example:</p> <pre> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.helloworld" android:versionCode="1" android:versionName="1.0"> - <b><uses-sdk android:minSdkVersion="Froyo" /></b> + <uses-sdk android:minSdkVersion="4" + <b>android:targetSdkVersion="11"</b> /> <application ... > ... </application> </manifest> </pre> -<p>This also enables the "Holographic" theme for all your activities, which is the new default -application theme for HONEYCOMB and later.</p> +<p>In this example, the application requires a minimum version of API +Level 4 (Android 1.6), but it also targets API Level 11 (Android 3.0). This way, when +the application is installed on a device running Android 3.0 or greater, the system applies the +holographic theme to each activity, and thus, each activity includes the Action Bar.</p> + +<p>However, if you want to use Action Bar APIs, such as to add tabs or modify Action Bar styles, +you need to set the {@code android:minSdkVersion} to {@code "11"}, so you can access the +{@link android.app.ActionBar} class.</p> -<p class="note"><strong>Note:</strong> In order for the Holographic theme to be applied based on -the target platform version, the <a -href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a> -element must appear <em>before</em> the <a -href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> -element.</p> -<h3>Hide the Action Bar</h3> +<h3 id="Removing">Removing the Action Bar</h3> + +<p>If you want to remove the Action Bar for a particular activity, set the activity theme to +{@link android.R.style#Theme_Holo_NoActionBar Theme.Holo.NoActionBar}. For example:</p> + +<pre> +<activity android:theme="@android:style/Theme.Holo.NoActionBar"> +</pre> + +<p class="note"><strong>Tip:</strong> If you have a custom activity theme in which you'd like to +remove the Action Bar, set the {@link android.R.styleable#Theme_windowActionBar +android:windowActionBar} style property {@code false}. See <a href="#Style">Styling the Action +Bar</a> for more about Action Bar styles.</p> -<p>If you want to hide the Action Bar for a particular activity, set the activity theme to -{@code android:style/Theme.NoTitleBar}. For example:</p> +<p>You can also hide the Action Bar at runtime by calling {@link android.app.ActionBar#hide}, +then show it again by calling {@link android.app.ActionBar#show}. For example:</p> <pre> -<activity android:theme="@android:style/Theme.NoTitleBar"> +ActionBar actionBar = getActionBar(); +actionBar.hide(); </pre> +<p>When the Action Bar hides, the system adjusts your activity content to fill all the +available screen space.</p> + +<p class="note"><strong>Note:</strong> If you remove the Action Bar using a theme, then the +window will not allow the Action Bar at all, so you cannot add it at runtime—calling +{@link android.app.Activity#getActionBar getActionBar()} will return null.</p> + <h2 id="ActionItems">Adding Action Items</h2> -<p>For each action item you want to add to the Action Bar, you must add a menu item to the -activity's <a href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">Options Menu</a> and declare -that the item be shown as an action.</p> +<p>An action item is simply a menu item from the <a +href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">Options Menu</a> which you declare should +appear directly in the Action Bar. An action item can include an icon and/or text. If a menu +item does not appear as an action item, then the system places it in the overflow menu, which +the user can open with the menu icon on the right side of the Action Bar.</p> <div class="figure" style="width:359px"> <img src="{@docRoot}images/ui/actionbar-item-withtext.png" height="57" alt="" /> <p class="img-caption"><strong>Figure 2.</strong> A screenshot from an Action Bar with two -action items.</p> +action items and the overflow menu.</p> </div> +<p>When the activity first starts, the system populates the Action Bar and overflow menu by calling +{@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} for your activity. As +discussed in the guide to <a href="{@docRoot}guide/topics/ui/menus.html">Creating Menus</a>, it's in +this callback method that you define the Options Menu for the activity.</p> + <p>You can specify a menu item to appear as an action item—if there is room -for it—from the <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu +for it—from your <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a> by declaring {@code -android:showAsAction="ifRoom"} for the {@code <item>} element. This way, the item will display -in the Action Bar for quick access only if there is room available for it—if there's not -enough room, the item is placed the Overflow Menu (revealed by the menu icon on the right side -of the Action Bar). From your application code, you can specify the item to appear as an action item -by calling {@link android.view.MenuItem#setShowAsAction setShowAsAction()} on the {@link +android:showAsAction="ifRoom"} for the {@code <item>} element. This way, the menu item appears +in the Action Bar for quick access only if there is room available for it. If there's not +enough room, the item is placed the overflow menu (revealed by the menu icon on the right side +of the Action Bar).</p> + +<p>You can also declare a menu item to appear as an action item from your application code, by +calling {@link android.view.MenuItem#setShowAsAction setShowAsAction()} on the {@link android.view.MenuItem} and passing {@link android.view.MenuItem#SHOW_AS_ACTION_IF_ROOM}.</p> -<p>If your item supplies both a title and an icon, then the action item shows only -the icon by defult. If you want to include the text with the action item, add the <em>with text</em> -flag—in XML, add {@code withText} to the {@code android:showAsAction} attribute or, in +<p>If your menu item supplies both a title and an icon, then the action item shows only +the icon by defult. If you want to include the text with the action item, add the "with +text" flag: in XML, add {@code withText} to the {@code android:showAsAction} attribute or, in your application code, use the {@link android.view.MenuItem#SHOW_AS_ACTION_WITH_TEXT} flag when -calling {@link android.view.MenuItem#setShowAsAction setShowAsAction()}. Figure 2 shows a screenshot -of an Action Bar with two action items that include text.</p> +calling {@link android.view.MenuItem#setShowAsAction setShowAsAction()}. Figure 2 shows an Action +Bar that has two action items with text and the icon for the overflow menu.</p> <p>Here's an example of how you can declare a menu item as an action item in a <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a> file:</p> @@ -149,32 +187,54 @@ href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a> fil item appears as an action item, it includes the title text along with the icon.</p> <p>A menu item placed in the Action Bar triggers the same callback methods as other items in the -Options Menu. When the user selects an item in the Action Bar, your activity receives a call to +Options Menu. When the user selects an action item, your activity receives a call to {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}, passing the -item ID. (If you added the item from a fragment, then the respective {@link -android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} method is called -for that fragment.)</p> +item ID.</p> -<p class="note"><strong>Note:</strong> Menu items that appear in the Overflow Menu (not as action -items) also show an icon, so it's best if you provide an icon for every menu item.</p> +<p class="note"><strong>Note:</strong> If you added the menu item from a fragment, then the +respective {@link +android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} method is called +for that fragment. However the activity gets a chance to handle it first, so the system calls {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} on the activity +before calling the fragment.</p> <p>You can also declare an item to <em>always</em> appear as an action item, but you should avoid -doing so. Most of the time, there will be enough room for several action items and they will appear -in the order you declare them. If you set items to always appear as action -items (instead of <em>if room</em>), then they are added without discrimination and there is a risk -that they will collide with other elements in the Action Bar, such as tabs or custom views.</p> +doing so, because it can create a cluttered UI if there are too many action items and they might +collide with other elements in the Action Bar.</p> <p>For more information about menus, see the <a href="{@docRoot}guide/topics/ui/menus.html#options-menu">Creating Menus</a> developer guide.</p> -<h3 id="Home">Using the application icon as an action item</h3> +<h3 id="Home">Using the app icon as an action item</h3> -<p>By default, the application icon appears in the Action Bar on the left side, but does nothing -when tapped. To use the application icon as an action item when tapped, you simply need to add a -condition to your {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} method -that performs an action when the {@link android.view.MenuItem} ID is {@code android.R.id.home}. -This ID is delivered every time the user taps the application icon.</p> +<p>By default, your application icon appears in the Action Bar on the left side. It also responds +to user interaction (when the user taps it, it visually responds the same way action +items do) and it's your responsibility to do something when the user taps it.</p> + +<img src="{@docRoot}images/ui/actionbar.png" height="36" alt="" /> +<p class="img-caption"><strong>Figure 3.</strong> Email's Action Bar, with the +application icon on the left.</p> + +<p>The normal behavior should be for your application to return to the "home" activity or the +initial state (such as when the activity hasn't changed, but fragments have changed) when the user +taps the icon. If the user is already at home or the initial state, then you don't need to do +anything.</p> + +<p>When the user taps the icon, the system calls your activity's {@link +android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} method with the {@code +android.R.id.home} ID. So, you need to add a condition to your {@link +android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} method to listen for {@code +android.R.id.home} and perform the appropriate action, such as start the home activity or pop recent +fragment transactions off the stack.</p> + +<p>If you respond to the application icon by returning to the home activity, you should include +the {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} flag in the {@link +android.content.Intent}. With this flag, if the activity you're starting already exists in the +current task, then all activities on top of it are destroyed and it is brought to the front. +You should favor this approach, because going "home" is an action that's equivalent to "going +back" and you should usually not create a new instance of the home activity. Otherwise, you +might end up with a long stack of activities in the current task.</p> <p>For example, here's an implementation of {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} that returns to the application's "home" activity:</p> @@ -186,97 +246,126 @@ public boolean onOptionsItemSelected(MenuItem item) { case android.R.id.home: // app icon in Action Bar clicked; go home Intent intent = new Intent(this, HomeActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); - break; + return true; + default: + return super.onOptionsItemSelected(item); } - return super.onOptionsItemSelected(item); } </pre> + + + +<h4>Using the app icon to navigate "up"</h4> + <div class="figure" style="width:144px"> <img src="{@docRoot}images/ui/actionbar-logo.png" height="140" alt="" /> - <p class="img-caption"><strong>Figure 3.</strong> The standard icon for the Email application -(top) and the "up navigation" icon (bottom).</p> + <p class="img-caption"><strong>Figure 4.</strong> The standard icon for the Email application +(top) and the "up" icon (bottom).</p> </div> -<p>You can also use the application icon to provide "up" navigation. The way you handle the event -when a user taps the icon is the same, but if the user experience for the event is to <em>navigate -up to the parent activity</em>, then you should indicate this behavior by setting the -Action Bar to "show home as up." You can do so by calling {@link -android.app.ActionBar#setDisplayOptions setDisplayOptions()} on your activity's {@link -android.app.ActionBar}, and passing the {@link -android.app.ActionBar#DISPLAY_HOME_AS_UP} display option.</p> +<p>You can also use the application icon to provide "up" navigation for the user. This is especially +useful when your application is composed of activities that generally appear in a certain order and +you want to facilitate the ability for the user to navigate up the activity hierarchy +(regardless of how they entered the current activity).</p> -<p>To get the {@link android.app.ActionBar}, call {@link android.app.Activity#getActionBar} from -your {@link android.app.Activity} during {@link android.app.Activity#onCreate onCreate()} (but be -sure you do so <em>after</em> you've called {@link android.app.Activity#setContentView -setContentView()}).</p> +<p>The way you respond to this event is the same as when navigating home (as +discussed above, except you start a different activity, based on the current activity). All you +need to do to indicate to the user that the behavior is different is set the Action Bar to "show +home as up." You can do so by calling {@link android.app.ActionBar#setDisplayHomeAsUpEnabled +setDisplayHomeAsUpEnabled(true)} on your activity's {@link android.app.ActionBar}. When you do, the +system draws your application icon with an arrow indicating the up behavior, as shown in figure +4.</p> -<p>For example, here's how you can change the Action Bar display mode to show the application -icon as an "up" action:</p> +<p>For example, here's how you can show the application icon as an "up" action:</p> <pre> @Override protected void onStart() { - super.onStart(); - ActionBar actionBar = this.getActionBar(); - actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP); + super.onStart(); + ActionBar actionBar = this.getActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); } </pre> -<p class="caution"><strong>Caution:</strong> If your activity does not have an Action Bar (if you -did not set the theme of your activity or application to the holographic or Action Bar theme), then -{@link android.app.Activity#getActionBar} returns null.</p> +<p>Then, your activity should respond to the user tapping the icon, from the {@link +android.app.Activity#onOptionsItemSelected +onOptionsItemSelected()}, by listening for the {@code android.R.id.home} ID (as shown above). In +this case, when navigating up, it's even more important that you use the {@link +android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} flag in the {@link android.content.Intent}, so that +you don't create a new instance of the parent activity if one already exists.</p> + <h2 id="ActionView">Adding an Action View</h2> -<div class="figure" style="width:281px"> +<div class="figure" style="width:429px"> <img src="{@docRoot}images/ui/actionbar-actionview.png" alt="" /> - <p class="img-caption"><strong>Figure 4.</strong> An action view with a search widget.</p> + <p class="img-caption"><strong>Figure 5.</strong> An action view with a {@link +android.widget.SearchView} widget.</p> </div> -<p>An action view is a customized view you can specify for an item in your Options Menu, to -display in the Action Bar when the item is included as an action item. For example, you can -include a menu item for "Search", which appears and behaves as a normal menu item in the Overflow -Menu, but, when set as an action item, it provides an action view that is a {@link -android.widget.SearchView}, so the user can initiate a search directly from the Action Bar. -Figure 4 shows an example of this, in which a menu item for search provides an action view -using the {@link android.widget.SearchView} widget.</p> +<p>An action view is a widget that appears in the Action Bar as a substitute for an action +item. For example, if you have an item in the Options Menu for "Search", you can add an action view +for the item that provides a {@link android.widget.SearchView} widget in the Action Bar whenever +the item is enabled as an action item.</p> + +<p>When adding an action view for a menu item, it's important that you still allow the item to +behave as a normal menu item when it does not appear in the Action Bar. For example, a menu item to +perform a search should, by default, bring up the <a +href="{@docRoot}guide/topics/search/search-dialog.html">search dialog</a>, but if the item is +placed in the Action Bar, the action view appears with a {@link android.widget.SearchView} +widget. Figure 4 shows an example of the {@link android.widget.SearchView} widget in an action +view.</p> <p>The best way to declare an action view for an item is in your <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>, using the {@code -android:actionLayout} or {@code android:actionViewClass} attribute.</p> +android:actionLayout} or {@code android:actionViewClass} attribute:</p> <ul> <li>The value for {@code android:actionLayout} must be a resource pointer to a layout file. For example: <pre> -<item android:id="@+id/menu_search" - android:title="Search" - android:icon="@drawable/ic_menu_search" - android:showAsAction="ifRoom" - <b>android:actionLayout="@layout/searchview"</b> /> -</pre></li> +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/menu_search" + android:title="Search" + android:icon="@drawable/ic_menu_search" + android:showAsAction="ifRoom" + <b>android:actionLayout="@layout/searchview"</b> /> +</menu> +</pre> +</li> + <li>The value for {@code android:actionViewClass} must be a fully-qualified class name for the {@link android.view.View} you want to use. For example: <pre> -<item android:id="@+id/menu_search" - android:title="Search" - android:icon="@drawable/ic_menu_search" - android:showAsAction="ifRoom" - <b>android:actionViewClass="android.widget.SearchView"</b> /> +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/menu_search" + android:title="Search" + android:icon="@drawable/ic_menu_search" + android:showAsAction="ifRoom" + <b>android:actionViewClass="android.widget.SearchView"</b> /> +</menu> </pre></li> </ul> +<p class="note">You must include {@code android:showAsAction="ifRoom"} in order for the item to +appear as an action view when room is available. If necessary, however, you can force the item to +always appear as an action view by setting {@code android:showAsAction} to {@code "always"}.</p> + <p>Now, when the menu item is displayed as an action item, it's action view appears instead of -the item's traditional icon and/or text. Yet, if for some reason the item does not appear in the -Action Bar, then it behaves like a normal menu item in the Overflow Menu and you must respond -accordingly when the user taps it, from the {@link android.app.Activity#onOptionsItemSelected -onOptionsItemSelected()} callback.</p> +the icon and/or title text. However, if there's not enough room in the Action Bar, the item appears +in the overflow menu as a normal menu item and you must respond to it from the {@link +android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} callback method. (For a +guide to providing search functionality, see the <a +href="{@docRoot}gudie/topics/search/index.html">Search</a> documentation.)</p> -<p>When the activity first starts, the system populates the Action Bar and Overflow Menu by calling +<p>When the activity first starts, the system populates the Action Bar and overflow menu by calling {@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()}. After you've inflated your menu in this method, you can acquire elements in an action view (perhaps in order to attach listeners) by calling {@link android.view.Menu#findItem @@ -300,18 +389,20 @@ href="{@docRoot}guide/topics/search/index.html">Search</a> developer guide.</p> + <h2 id="Tabs">Adding Tabs</h2> -<p>The Action Bar can display tabs that allow the user navigate between different fragments in the -activity. Each tab can include a title and/or an icon.</p> -<!-- -<div class="figure" style="width:300px"> +<div class="figure" style="width:504px"> <img src="{@docRoot}images/ui/actionbar-tabs.png" alt="" /> - <p class="img-caption"><strong>Figure 5.</strong> Screenshot of tabs in the -Action Bar.</p> + <p class="img-caption"><strong>Figure 6.</strong> Screenshot of tabs in the +Action Bar, from the <a +href="{@docRoot}resources/samples/Honeycomb-Gallery/index.html">Honeycomb Gallery</a> sample +application.</p> </div> ---> + +<p>The Action Bar can display tabs that allow the user navigate between different fragments in the +activity. Each tab can include a title and/or an icon.</p> <p>To begin, your layout must include a {@link android.view.View} in which each {@link android.app.Fragment} associated with a tab is displayed. Be sure the view has an ID that you @@ -381,7 +472,7 @@ setTabListener()}. </li> <li>Add each {@link android.app.ActionBar.Tab} to the Action Bar by calling {@link android.app.ActionBar#addTab addTab()} on the {@link android.app.ActionBar} and passing the -{@link android.app.ActionBar.Tab}.<> +{@link android.app.ActionBar.Tab}.</li> </ol> <p>For example, the following code combines steps 2 - 5 to create two tabs and add them to the Action Bar:</p> @@ -395,7 +486,7 @@ protected void onCreate(Bundle savedInstanceState) { final ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); // remove the activity title to make space for tabs - actionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE); + actionBar.setDisplayShowTitleEnabled(false); // instantiate fragment for the tab Fragment artistsFragment = new ArtistsFragment(); @@ -418,9 +509,10 @@ android.app.FragmentTransaction}. Likewise, when a tab is deselected (because an selected), you should remove that fragment from the layout, using {@link android.app.FragmentTransaction#remove remove()}.</p> -<p class="note"><strong>Note:</strong> You <strong>do not</strong> need -to call {@link android.app.FragmentTransaction#commit} for these transactions. You also -<strong>cannot</strong> add these fragment transactions to the back stack.</p> +<p class="caution"><strong>Caution:</strong> You <strong>must not</strong> call {@link +android.app.FragmentTransaction#commit} for these transactions—the system calls it for you +and it may throw an exception if you call it yourself. You also <strong>cannot</strong> add these +fragment transactions to the back stack.</p> <p>If your activity is stopped, you should retain the currently selected tab with the saved state so that when the user returns to your application, you can open the tab. When it's time to save the @@ -436,6 +528,7 @@ href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> develope + <h2 id="Dropdown">Adding Drop-down Navigation</h2> <p>As another mode of navigation within your activity, you can provide a drop-down list in the @@ -450,23 +543,25 @@ Action Bar.</p> </div> --> -<p>Here's a quick list of what you must do to enable drop-down navigation:</p> +<p>Here's a quick list of steps to enable drop-down navigation:</p> <ol> <li>Create a {@link android.widget.SpinnerAdapter} that provides the -list of selectable items for the list and the layout to use when drawing each item in the list.</li> +list of selectable items for the drop-down and the layout to use when drawing each item in the +list.</li> <li>Implement {@link android.app.ActionBar.OnNavigationListener} to define the behavior when the user selects an item from the list.</li> - <li>Turn on navigation mode for the Action Bar with {@link + <li>Enable navigation mode for the Action Bar with {@link android.app.ActionBar#setNavigationMode setNavigationMode()}. For example: <pre> ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); </pre> - <p>You should perform this during your activity's {@link android.app.Activity#onCreate + <p class="note"><strong>Note:</strong> You should perform this during your activity's {@link +android.app.Activity#onCreate onCreate()} method.</p> </li> - <li>Following that, set the callback for your drop-down list with {@link + <li>Then, set the callback for the drop-down list with {@link android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()}. For example: <pre> actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback); @@ -476,37 +571,45 @@ android.app.ActionBar.OnNavigationListener}. More about these next.</p> </li> </ol> -<p>That's the basic setup. The {@link android.widget.SpinnerAdapter} and {@link -android.app.ActionBar.OnNavigationListener} is where most of the work is done. There are many ways -you can implement these to define the functionality for your drop-down navigation. Implementing -various types of {@link android.widget.SpinnerAdapter} is beyond the scope of this -document—you should refer to the class refrence for more information about implementing it or -extending an existing implementation. However, below is a simple example for a {@link -android.widget.SpinnerAdapter} and {@link android.app.ActionBar.OnNavigationListener} to get you -started.</p> +<p>That's the basic setup. However, implementing the {@link android.widget.SpinnerAdapter} and +{@link android.app.ActionBar.OnNavigationListener} is where most of the work is done. There are many +ways you can implement these to define the functionality for your drop-down navigation and +implementing various types of {@link android.widget.SpinnerAdapter} is beyond the scope of this +document (you should refer to the {@link android.widget.SpinnerAdapter} class reference for more +information). However, below is a simple example for a {@link android.widget.SpinnerAdapter} and +{@link android.app.ActionBar.OnNavigationListener} to get you started (click the title to +reveal the sample).</p> + + + +<div class="toggle-content closed"> + <h3 id="Spinner"><a href="#" onclick="return toggleContent(this)"> + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt="" /> + Example SpinnerAdapter and OnNavigationListener + </a></h3> -<h3 id="Spinner">Example: simple SpinnerAdapter</h3> + <div class="toggle-content-toggleme"> -<p>{@link android.widget.SpinnerAdapter} is an interface that you can implement to provide -content for the list and is where your implementation for the drop-down list can be heavily -customized. Android includes some useful implementations that you can extend, such as {@link -android.widget.ArrayAdapter} and {@link +<p>{@link android.widget.SpinnerAdapter} is an adapter that provides data for a spinner widget, +such as the drop-down list in the Action Bar. {@link android.widget.SpinnerAdapter} is an interface +that you can implement, but Android includes some useful implementations that you can extend, such +as {@link android.widget.ArrayAdapter} and {@link android.widget.SimpleCursorAdapter}. For example, here's an easy way to create a {@link -android.widget.SpinnerAdapter} with {@link android.widget.ArrayAdapter}, using a string array -from resources:</p> +android.widget.SpinnerAdapter} by using {@link android.widget.ArrayAdapter} implementation, which +uses a string array as the data source:</p> <pre> SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this, R.array.action_list, android.R.layout.simple_spinner_dropdown_item); </pre> -<p>This is now ready to be given to {@link -android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()}, in step 4 from -above.</p> +<p>The {@link android.widget.ArrayAdapter#createFromResource createFromResource()} method takes +three parameters: the application {@link android.content.Context}, the resource ID for the string +array, and the layout to use for each list item.</p> <p>A <a href="{@docRoot}guide/topics/resources/string-resource.html#StringArray">string array</a> -defined as a resource looks like this:</p> +defined in a resource looks like this:</p> <pre> <?xml version="1.0" encoding="utf-8"?> @@ -519,21 +622,26 @@ defined as a resource looks like this:</p> </pre> </pre> +<p>The {@link android.widget.ArrayAdapter} returned by {@link +android.widget.ArrayAdapter#createFromResource createFromResource()} is complete and ready for you +to pass it to {@link android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()} +(in step 4 from above). Before you do, though, you need to create the {@link +android.app.ActionBar.OnNavigationListener OnNavigationListener}.</p> -<h3 id="OnNavigationListener">Example: simple OnNavigationListener</h3> <p>Your implementation of {@link android.app.ActionBar.OnNavigationListener} is where you handle fragment changes or other modifications to your activity when the user selects an item from the -drop-down list. There's only one callback method to implement: {@link +drop-down list. There's only one callback method to implement in the listener: {@link android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()}.</p> <p>The {@link android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()} -method receives the position of the item in the list and an item ID provided by the {@link +method receives the position of the item in the list and a unique item ID provided by the {@link android.widget.SpinnerAdapter}.</p> <p>Here's an example that instantiates an anonymous implementation of {@link -android.app.ActionBar.OnNavigationListener}, which inserts a {@link android.app.Fragment} into the +android.app.ActionBar.OnNavigationListener OnNavigationListener}, which inserts a {@link +android.app.Fragment} into the layout container identified by {@code R.id.fragment_container}:</p> <pre> @@ -556,13 +664,18 @@ mOnNavigationListener = new OnNavigationListener() { }; </pre> -<p>This instance of {@link android.app.ActionBar.OnNavigationListener} can be given to {@link -android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()}, in step 4 from -above.</p> +<p>This instance of {@link android.app.ActionBar.OnNavigationListener OnNavigationListener} is +complete and you can now call {@link android.app.ActionBar#setListNavigationCallbacks +setListNavigationCallbacks()} (in step 4), passing the {@link android.widget.ArrayAdapter} and this +{@link android.app.ActionBar.OnNavigationListener OnNavigationListener}.</p> -<p>In this example, the fragment added is given a tag that can uniquely identify the fragment. -For this example, the {@code ListContentFragment} class used uses this tag as -the text for a {@link android.widget.TextView} in the fragment's layout. Here's how it's done:</p> +<p>In this example, when the user selects an item from the drop-down list, a fragment is added to +the layout (replacing the current fragment in the {@code R.id.fragment_container} view). The +fragment added is given a tag that uniquely identifies it, which is the same string used to +identify the fragment in the drop-down list.</p> + +<p>Here's a look at the {@code ListContentFragment} class that defines each fragment in this +example:</p> <pre> public class ListContentFragment extends Fragment { @@ -588,6 +701,150 @@ public class ListContentFragment extends Fragment { } </pre> + </div><!-- end toggle-content-toggleme --> + +</div><!-- end toggle-content --> + + + + + +<h2 id="Style">Styling the Action Bar</h2> + +<p>The Action Bar is the heading for your application and a primary interaction point for users, +so you might want to modify some of its design in order to make it feel more integrated with your +application design. There are several ways you can do this if you wish.</p> + +<p>For simple modifications to the {@link android.app.ActionBar}, you can use the following +methods:</p> + +<dl> + <dt>{@link android.app.ActionBar#setBackgroundDrawable setBackgroundDrawable()}</dt> + <dd>Sets a drawable to use as the Action Bar's background. The drawable should be a <a +href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">Nine-patch</a> image, a <a +href="{@docRoot}guide/topics/resources/drawable-resource.html#Shape">shape</a>, or a <a +href="{@docRoot}guide/topics/resources/more-resources.html#Color">solid color</a>, so the system can +resize the drawable based on the size of the Action Bar (you should <em>not</em> use a fixed-size +bitmap image).</dd> + + <dt>{@link android.app.ActionBar#setDisplayUseLogoEnabled setDisplayUseLogoEnabled()}</dt> + <dd>Enables the use of an alternative image (a "logo") in the Action Bar, instead of the default +application icon. A logo is often a wider, more detailed image that represents the application. +When this is enabled, the system uses the logo image defined for the application (or the +individual activity) in the manifest file, with the <a +href="{@docRoot}guide/topics/manifest/application-element.html#logo">{@code android:logo}</a> +attribute. The logo will be resized as necessary to fit the height of the Action Bar. (Best +practice is to design the logo at the same size as your application icon.)</dd> +</dl> + + +<p>For more complex customizations, you can use Android's <a +href="{@docRoot}guide/topics/ui/themes.html">style and theme</a> framework to restyle your Action +Bar in several ways.</p> + +<p>The Action Bar has two standard themes, "dark" and "light". The dark theme is applied with +the default holographic theme, as specified by the {@link android.R.style#Theme_Holo Theme.Holo} +theme. If you want a white background with dark text, instead, you can apply the {@link +android.R.style#Theme_Holo_Light Theme.Holo.Light} theme to the activity in the manifest file. For +example:</p> + +<pre> +<activity android:name=".ExampleActivity" + android:theme="@android:style/Theme.Holo.Light" /> +</pre> + +<p>For more control, you can override either the {@link android.R.style#Theme_Holo +Theme.Holo} or {@link android.R.style#Theme_Holo_Light Theme.Holo.Light} theme and apply custom +styles to certain aspects of the Action Bar. Some of the Action Bar properties you can customize +include the following:</p> + +<dl> + <dt>{@link android.R.styleable#Theme_actionBarTabStyle + android:actionBarTabStyle}</dt> + <dd>Style for tabs in the Action Bar.</dd> + + <dt>{@link android.R.styleable#Theme_actionBarTabBarStyle + android:actionBarTabBarStyle}</dt> + <dd>Style for the bar that appears below tabs in the Action Bar.</dd> + + <dt>{@link android.R.styleable#Theme_actionBarTabTextStyle + android:actionBarTabTextStyle}</dt> + <dd>Style for the text in the tabs.</dd> + + <dt>{@link android.R.styleable#Theme_actionDropDownStyle + android:actionDropDownStyle}</dt> + <dd>Style for the drop-down list used for the overflow menu and drop-down navigation.</dd> + + <dt>{@link android.R.styleable#Theme_actionButtonStyle + android:actionButtonStyle}</dt> + <dd>Style for the background image used for buttons in the Action Bar.</dd> + +</dl> + +<p>For example, here's a resource file that defines a custom theme for the Action Bar, based on +the standard {@link android.R.style#Theme_Holo Theme.Holo} theme:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- the theme applied to the application or activity --> + <style name="CustomActionBar" parent="android:style/Theme.Holo.Light"> + <item name="android:actionBarTabTextStyle">@style/customActionBarTabTextStyle</item> + <item name="android:actionBarTabStyle">@style/customActionBarTabStyle</item> + <item name="android:actionBarTabBarStyle">@style/customActionBarTabBarStyle</item> + </style> + + <!-- style for the tab text --> + <style name="customActionBarTabTextStyle"> + <item name="android:textColor">#2966c2</item> + <item name="android:textSize">20sp</item> + <item name="android:typeface">sans</item> + </style> + + <!-- style for the tabs --> + <style name="customActionBarTabStyle"> + <item name="android:background">@drawable/actionbar_tab_bg</item> + <item name="android:paddingLeft">20dp</item> + <item name="android:paddingRight">20dp</item> + </style> + + <!-- style for the tab bar --> + <style name="customActionBarTabBarStyle"> + <item name="android:background">@drawable/actionbar_tab_bar</item> + </style> +</resources> +</pre> + +<p class="note"><strong>Note:</strong> In order for the tab background image to change, +depending on the current tab state (selected, pressed, unselected), the drawable resource used +must be a <a href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">state +list drawable</a>. Also be certain that your theme declares a parent theme, from which it +inherits all styles not explicitly declared in your theme.</p> + +<p>You can apply your custom theme to the entire application or to individual activities in your +manifest file, like this:</p> + +<pre> +<application android:theme="@style/CustomActionBar" + ... /> +</pre> + +<p>Additionally, if you want to create a custom theme for your activity that removes the Action +Bar completely, use the following style attributes:</p> + +<dl> + <dt>{@link android.R.styleable#Theme_windowActionBar + android:windowActionBar}</dt> + <dd>Set this style property {@code false} to remove the Action Bar.</dd> + + <dt>{@link android.R.styleable#Theme_windowNoTitle + android:windowNoTitle}</dt> + <dd>Set this style property {@code true} to also remove the traditional title bar.</dd> +</dl> + +<p>For more information about using themes in your application, read <a +href="{@docRoot}guide/topics/ui/themes.html">Applying Styles and Themes</a>.</p> + diff --git a/docs/html/images/ui/actionbar-actionview.png b/docs/html/images/ui/actionbar-actionview.png Binary files differindex 30edca08c1a9..cc18f9b764e8 100644 --- a/docs/html/images/ui/actionbar-actionview.png +++ b/docs/html/images/ui/actionbar-actionview.png diff --git a/docs/html/images/ui/actionbar-item-withtext.png b/docs/html/images/ui/actionbar-item-withtext.png Binary files differindex 98b5f8490f55..61742d3f62a8 100644 --- a/docs/html/images/ui/actionbar-item-withtext.png +++ b/docs/html/images/ui/actionbar-item-withtext.png diff --git a/docs/html/images/ui/actionbar-logo.png b/docs/html/images/ui/actionbar-logo.png Binary files differindex 481ed2c11c34..df914bcf3d6e 100644 --- a/docs/html/images/ui/actionbar-logo.png +++ b/docs/html/images/ui/actionbar-logo.png diff --git a/docs/html/images/ui/actionbar-tabs.png b/docs/html/images/ui/actionbar-tabs.png Binary files differnew file mode 100644 index 000000000000..9d36db69fc65 --- /dev/null +++ b/docs/html/images/ui/actionbar-tabs.png diff --git a/docs/html/images/ui/actionbar.png b/docs/html/images/ui/actionbar.png Binary files differindex 4fc871c670d7..dcd8449f18fe 100644 --- a/docs/html/images/ui/actionbar.png +++ b/docs/html/images/ui/actionbar.png diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index bffab958378f..ebf7aa0a3d04 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -20,6 +20,7 @@ #include "Caches.h" #include "Properties.h" +#include "LayerRenderer.h" namespace android { @@ -116,12 +117,7 @@ void Caches::clearGarbage() { size_t count = mLayerGarbage.size(); for (size_t i = 0; i < count; i++) { Layer* layer = mLayerGarbage.itemAt(i); - if (layer) { - if (layer->fbo) glDeleteFramebuffers(1, &layer->fbo); - if (layer->texture) glDeleteTextures(1, &layer->texture); - - delete layer; - } + LayerRenderer::destroyLayer(layer); } mLayerGarbage.clear(); } diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 7667af5fe018..a9710ad6adbf 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -128,6 +128,31 @@ Layer* LayerCache::get(const uint32_t width, const uint32_t height) { return layer; } +bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t height) { + // TODO: We should be smarter and see if we have a texture of the appropriate + // size already in the cache, and reuse it instead of creating a new one + + LayerEntry entry(width, height); + if (entry.mWidth <= layer->width && entry.mHeight <= layer->height) { + return true; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, layer->texture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, entry.mWidth, entry.mHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + if (glGetError() != GL_NO_ERROR) { + return false; + } + + layer->width = entry.mWidth; + layer->height = entry.mHeight; + + return true; +} + bool LayerCache::put(Layer* layer) { const uint32_t size = layer->width * layer->height * 4; // Don't even try to cache a layer that's bigger than the cache diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index 1333a73feea7..d2d5f3960568 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -76,6 +76,17 @@ public: * Clears the cache. This causes all layers to be deleted. */ void clear(); + /** + * Resize the specified layer if needed. + * + * @param layer The layer to resize + * @param width The new width of the layer + * @param height The new height of the layer + * + * @return True if the layer was resized or nothing happened, false if + * a failure occurred during the resizing operation + */ + bool resize(Layer* layer, const uint32_t width, const uint32_t height); /** * Sets the maximum size of the cache in bytes. diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 7379b639eed1..24f9739715ee 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -18,6 +18,7 @@ #include <ui/Rect.h> +#include "LayerCache.h" #include "LayerRenderer.h" #include "Properties.h" #include "Rect.h" @@ -32,25 +33,26 @@ namespace uirenderer { void LayerRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, mLayer->fbo); + + const float width = mLayer->layer.getWidth(); + const float height = mLayer->layer.getHeight(); + #if RENDER_LAYERS_AS_REGIONS Rect dirty(left, top, right, bottom); if (dirty.isEmpty() || (dirty.left <= 0 && dirty.top <= 0 && - dirty.right >= mLayer->width && dirty.bottom >= mLayer->height)) { + dirty.right >= width && dirty.bottom >= height)) { mLayer->region.clear(); - dirty.set(0.0f, 0.0f, mLayer->width, mLayer->height); + dirty.set(0.0f, 0.0f, width, height); } else { - dirty.intersect(0.0f, 0.0f, mLayer->width, mLayer->height); + dirty.intersect(0.0f, 0.0f, width, height); android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom); mLayer->region.subtractSelf(r); } -#endif - - glBindFramebuffer(GL_FRAMEBUFFER, mLayer->fbo); -#if RENDER_LAYERS_AS_REGIONS OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque); #else - OpenGLRenderer::prepareDirty(0.0f, 0.0f, mLayer->width, mLayer->height, opaque); + OpenGLRenderer::prepareDirty(0.0f, 0.0f, width, height, opaque); #endif } @@ -164,64 +166,56 @@ void LayerRenderer::generateMesh() { Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) { LAYER_RENDERER_LOGD("Creating new layer %dx%d", width, height); - Layer* layer = new Layer(width, height); - - GLuint previousFbo; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - - glGenFramebuffers(1, &layer->fbo); - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); - - if (glGetError() != GL_NO_ERROR) { - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - glDeleteBuffers(1, &layer->fbo); - return 0; + GLuint fbo = Caches::getInstance().fboCache.get(); + if (!fbo) { + LOGW("Could not obtain an FBO"); + return NULL; } glActiveTexture(GL_TEXTURE0); - glGenTextures(1, &layer->texture); - glBindTexture(GL_TEXTURE_2D, layer->texture); + Layer* layer = Caches::getInstance().layerCache.get(width, height); + if (!layer) { + LOGW("Could not obtain a layer"); + return NULL; + } - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + layer->fbo = fbo; + layer->layer.set(0.0f, 0.0f, width, height); + layer->texCoords.set(0.0f, height / float(layer->height), + width / float(layer->width), 0.0f); + layer->alpha = 255; + layer->mode = SkXfermode::kSrcOver_Mode; + layer->blend = !isOpaque; + layer->colorFilter = NULL; + layer->region.clear(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLuint previousFbo; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); + glBindTexture(GL_TEXTURE_2D, layer->texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + // Initialize the texture if needed + if (layer->empty) { + layer->empty = false; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); - if (glGetError() != GL_NO_ERROR) { - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - glDeleteBuffers(1, &layer->fbo); - glDeleteTextures(1, &layer->texture); - delete layer; - return 0; + if (glGetError() != GL_NO_ERROR) { + LOGD("Could not allocate texture"); + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + glDeleteTextures(1, &layer->texture); + Caches::getInstance().fboCache.put(fbo); + delete layer; + return NULL; + } } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, layer->texture, 0); - if (glGetError() != GL_NO_ERROR) { - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - glDeleteBuffers(1, &layer->fbo); - glDeleteTextures(1, &layer->texture); - delete layer; - return 0; - } - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - layer->layer.set(0.0f, 0.0f, width, height); - layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f); - layer->alpha = 255; - layer->mode = SkXfermode::kSrcOver_Mode; - layer->blend = !isOpaque; - layer->empty = false; - layer->colorFilter = NULL; - return layer; } @@ -229,27 +223,17 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { if (layer) { LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->fbo, width, height); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, layer->texture); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - if (glGetError() != GL_NO_ERROR) { - glDeleteBuffers(1, &layer->fbo); - glDeleteTextures(1, &layer->texture); - - layer->width = 0; - layer->height = 0; - layer->fbo = 0; - layer->texture = 0; - + if (Caches::getInstance().layerCache.resize(layer, width, height)) { + layer->layer.set(0.0f, 0.0f, width, height); + layer->texCoords.set(0.0f, height / float(layer->height), + width / float(layer->width), 0.0f); + } else { + if (layer->texture) glDeleteTextures(1, &layer->texture); + delete layer; return false; } - - layer->width = width; - layer->height = height; } + return true; } @@ -257,10 +241,16 @@ void LayerRenderer::destroyLayer(Layer* layer) { if (layer) { LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo); - if (layer->fbo) glDeleteFramebuffers(1, &layer->fbo); - if (layer->texture) glDeleteTextures(1, &layer->texture); + if (layer->fbo) { + Caches::getInstance().fboCache.put(layer->fbo); + } - delete layer; + if (!Caches::getInstance().layerCache.put(layer)) { + if (layer->texture) glDeleteTextures(1, &layer->texture); + delete layer; + } else { + layer->region.clear(); + } } } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 2f230b566c82..2d8b6f3950f6 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -64,14 +64,14 @@ enum DebugLevel { // Converts a number of mega-bytes into bytes #define MB(s) s * 1024 * 1024 -#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f -#define DEFAULT_LAYER_CACHE_SIZE 8.0f +#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f +#define DEFAULT_LAYER_CACHE_SIZE 24.0f #define DEFAULT_PATH_CACHE_SIZE 4.0f #define DEFAULT_SHAPE_CACHE_SIZE 1.0f #define DEFAULT_PATCH_CACHE_SIZE 512 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f -#define DEFAULT_FBO_CACHE_SIZE 12 +#define DEFAULT_FBO_CACHE_SIZE 16 #define DEFAULT_TEXT_GAMMA 1.4f #define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64 diff --git a/media/java/android/media/videoeditor/VideoEditorImpl.java b/media/java/android/media/videoeditor/VideoEditorImpl.java index 27ab799416bd..2a9a5395415e 100755 --- a/media/java/android/media/videoeditor/VideoEditorImpl.java +++ b/media/java/android/media/videoeditor/VideoEditorImpl.java @@ -33,6 +33,8 @@ import org.xmlpull.v1.XmlSerializer; import android.graphics.Bitmap; import android.graphics.Rect; +import android.media.videoeditor.MediaImageItem; +import android.media.videoeditor.MediaItem; import android.util.Log; import android.util.Xml; import android.view.Surface; @@ -772,8 +774,7 @@ public class VideoEditorImpl implements VideoEditor { return mediaItem; } - private synchronized MediaItem removeMediaItem(String mediaItemId, - boolean flag) { + private synchronized MediaItem removeMediaItem(String mediaItemId, boolean flag) { final String firstItemString = mMediaItems.get(0).getId(); final MediaItem mediaItem = getMediaItem(mediaItemId); @@ -879,14 +880,15 @@ public class VideoEditorImpl implements VideoEditor { /** * the project form XML */ - private void load() throws FileNotFoundException, XmlPullParserException, - IOException { + private void load() throws FileNotFoundException, XmlPullParserException, IOException { final File file = new File(mProjectPath, PROJECT_FILENAME); /** * Load the metadata */ final FileInputStream fis = new FileInputStream(file); try { + final List<String> ignoredMediaItems = new ArrayList<String>(); + final XmlPullParser parser = Xml.newPullParser(); parser.setInput(fis, "UTF-8"); int eventType = parser.getEventType(); @@ -898,74 +900,43 @@ public class VideoEditorImpl implements VideoEditor { case XmlPullParser.START_TAG: { name = parser.getName(); if (TAG_PROJECT.equals(name)) { - mAspectRatio = - Integer.parseInt(parser.getAttributeValue("", + mAspectRatio = Integer.parseInt(parser.getAttributeValue("", ATTR_ASPECT_RATIO)); final boolean mRegenPCM = - Boolean.parseBoolean(parser.getAttributeValue("", + Boolean.parseBoolean(parser.getAttributeValue("", ATTR_REGENERATE_PCM)); mMANativeHelper.setAudioflag(mRegenPCM); - } else if (TAG_MEDIA_ITEM.equals(name)) { - final String mediaItemId = - parser.getAttributeValue("", ATTR_ID); - final String type = - parser.getAttributeValue("", ATTR_TYPE); - final String filename = - parser.getAttributeValue("", ATTR_FILENAME); - final int renderingMode = - Integer.parseInt(parser.getAttributeValue("", - ATTR_RENDERING_MODE)); - - if (MediaImageItem.class.getSimpleName().equals(type)) { - final long durationMs = Long.parseLong(parser.getAttributeValue("", - ATTR_DURATION)); - currentMediaItem = new MediaImageItem(this, mediaItemId, filename, - durationMs, renderingMode); - } else if (MediaVideoItem.class.getSimpleName().equals(type)) { - final long beginMs = Long.parseLong(parser.getAttributeValue("", - ATTR_BEGIN_TIME)); - final long endMs = Long.parseLong(parser.getAttributeValue("", - ATTR_END_TIME)); - final int volume = Integer.parseInt(parser.getAttributeValue("", - ATTR_VOLUME)); - final boolean muted = Boolean.parseBoolean(parser.getAttributeValue("", - ATTR_MUTED)); - final String audioWaveformFilename = parser.getAttributeValue("", - ATTR_AUDIO_WAVEFORM_FILENAME); - currentMediaItem = new MediaVideoItem(this, mediaItemId, filename, - renderingMode, beginMs, endMs, volume, muted, - audioWaveformFilename); - - final long beginTimeMs = Long.parseLong(parser.getAttributeValue("", - ATTR_BEGIN_TIME)); - final long endTimeMs = Long.parseLong(parser.getAttributeValue("", - ATTR_END_TIME)); - ((MediaVideoItem)currentMediaItem).setExtractBoundaries(beginTimeMs, - endTimeMs); - - final int volumePercent = Integer.parseInt(parser.getAttributeValue("", - ATTR_VOLUME)); - ((MediaVideoItem)currentMediaItem).setVolume(volumePercent); - } else { - Log.e(TAG, "Unknown media item type: " + type); - currentMediaItem = null; - } - - if (currentMediaItem != null) { + final String mediaItemId = parser.getAttributeValue("", ATTR_ID); + try { + currentMediaItem = parseMediaItem(parser); mMediaItems.add(currentMediaItem); + } catch (Exception ex) { + Log.w(TAG, "Cannot load media item: " + mediaItemId, ex); + currentMediaItem = null; + // Ignore the media item + ignoredMediaItems.add(mediaItemId); } } else if (TAG_TRANSITION.equals(name)) { - final Transition transition = parseTransition(parser); - if (transition != null) { - mTransitions.add(transition); + try { + final Transition transition = parseTransition(parser, + ignoredMediaItems); + // The transition will be null if the bounding + // media items are ignored + if (transition != null) { + mTransitions.add(transition); + } + } catch (Exception ex) { + Log.w(TAG, "Cannot load transition", ex); } } else if (TAG_OVERLAY.equals(name)) { if (currentMediaItem != null) { - currentOverlay = parseOverlay(parser, currentMediaItem); - if (currentOverlay != null) { + try { + currentOverlay = parseOverlay(parser, currentMediaItem); currentMediaItem.addOverlay(currentOverlay); + } catch (Exception ex) { + Log.w(TAG, "Cannot load overlay", ex); } } } else if (TAG_OVERLAY_USER_ATTRIBUTES.equals(name)) { @@ -978,40 +949,45 @@ public class VideoEditorImpl implements VideoEditor { } } else if (TAG_EFFECT.equals(name)) { if (currentMediaItem != null) { - final Effect effect = parseEffect(parser, currentMediaItem); - if (effect != null) { + try { + final Effect effect = parseEffect(parser, currentMediaItem); currentMediaItem.addEffect(effect); - } - if (effect instanceof EffectKenBurns) { - final boolean isImageClipGenerated = - Boolean.parseBoolean(parser.getAttributeValue("", - ATTR_IS_IMAGE_CLIP_GENERATED)); - if(isImageClipGenerated) { - String filename = parser.getAttributeValue("", - ATTR_GENERATED_IMAGE_CLIP); - if (new File(filename).exists() == true) { + + if (effect instanceof EffectKenBurns) { + final boolean isImageClipGenerated = + Boolean.parseBoolean(parser.getAttributeValue("", + ATTR_IS_IMAGE_CLIP_GENERATED)); + if(isImageClipGenerated) { + final String filename = parser.getAttributeValue("", + ATTR_GENERATED_IMAGE_CLIP); + if (new File(filename).exists() == true) { + ((MediaImageItem)currentMediaItem). + setGeneratedImageClip(filename); + ((MediaImageItem)currentMediaItem). + setRegenerateClip(false); + } else { + ((MediaImageItem)currentMediaItem). + setGeneratedImageClip(null); + ((MediaImageItem)currentMediaItem). + setRegenerateClip(true); + } + } else { ((MediaImageItem)currentMediaItem). - setGeneratedImageClip(filename); + setGeneratedImageClip(null); ((MediaImageItem)currentMediaItem). - setRegenerateClip(false); - } else { - ((MediaImageItem)currentMediaItem). - setGeneratedImageClip(null); - ((MediaImageItem)currentMediaItem). - setRegenerateClip(true); - } - } else { - ((MediaImageItem)currentMediaItem). - setGeneratedImageClip(null); - ((MediaImageItem)currentMediaItem). - setRegenerateClip(true); + setRegenerateClip(true); + } } + } catch (Exception ex) { + Log.w(TAG, "Cannot load effect", ex); } } } else if (TAG_AUDIO_TRACK.equals(name)) { - final AudioTrack audioTrack = parseAudioTrack(parser); - if (audioTrack != null) { + try { + final AudioTrack audioTrack = parseAudioTrack(parser); addAudioTrack(audioTrack); + } catch (Exception ex) { + Log.w(TAG, "Cannot load audio track", ex); } } break; @@ -1042,34 +1018,81 @@ public class VideoEditorImpl implements VideoEditor { } /** + * Parse the media item + * + * @param parser The parser + * @return The media item + */ + private MediaItem parseMediaItem(XmlPullParser parser) throws IOException { + final String mediaItemId = parser.getAttributeValue("", ATTR_ID); + final String type = parser.getAttributeValue("", ATTR_TYPE); + final String filename = parser.getAttributeValue("", ATTR_FILENAME); + final int renderingMode = Integer.parseInt(parser.getAttributeValue("", + ATTR_RENDERING_MODE)); + + final MediaItem currentMediaItem; + if (MediaImageItem.class.getSimpleName().equals(type)) { + final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION)); + currentMediaItem = new MediaImageItem(this, mediaItemId, filename, + durationMs, renderingMode); + } else if (MediaVideoItem.class.getSimpleName().equals(type)) { + final long beginMs = Long.parseLong(parser.getAttributeValue("", ATTR_BEGIN_TIME)); + final long endMs = Long.parseLong(parser.getAttributeValue("", ATTR_END_TIME)); + final int volume = Integer.parseInt(parser.getAttributeValue("", ATTR_VOLUME)); + final boolean muted = Boolean.parseBoolean(parser.getAttributeValue("", ATTR_MUTED)); + final String audioWaveformFilename = parser.getAttributeValue("", + ATTR_AUDIO_WAVEFORM_FILENAME); + currentMediaItem = new MediaVideoItem(this, mediaItemId, filename, + renderingMode, beginMs, endMs, volume, muted, audioWaveformFilename); + + final long beginTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_BEGIN_TIME)); + final long endTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_END_TIME)); + ((MediaVideoItem)currentMediaItem).setExtractBoundaries(beginTimeMs, endTimeMs); + + final int volumePercent = Integer.parseInt(parser.getAttributeValue("", ATTR_VOLUME)); + ((MediaVideoItem)currentMediaItem).setVolume(volumePercent); + } else { + throw new IllegalArgumentException("Unknown media item type: " + type); + } + + return currentMediaItem; + } + + /** * Parse the transition * * @param parser The parser + * @param ignoredMediaItems The list of ignored media items + * * @return The transition */ - private Transition parseTransition(XmlPullParser parser) { + private Transition parseTransition(XmlPullParser parser, List<String> ignoredMediaItems) { final String transitionId = parser.getAttributeValue("", ATTR_ID); final String type = parser.getAttributeValue("", ATTR_TYPE); - final long durationMs = Long.parseLong(parser.getAttributeValue("", - ATTR_DURATION)); - final int behavior = Integer.parseInt(parser.getAttributeValue("", - ATTR_BEHAVIOR)); - final boolean isTransitionGenerated; + final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION)); + final int behavior = Integer.parseInt(parser.getAttributeValue("", ATTR_BEHAVIOR)); - - final String beforeMediaItemId = parser.getAttributeValue("", - ATTR_BEFORE_MEDIA_ITEM_ID); + final String beforeMediaItemId = parser.getAttributeValue("", ATTR_BEFORE_MEDIA_ITEM_ID); final MediaItem beforeMediaItem; if (beforeMediaItemId != null) { + if (ignoredMediaItems.contains(beforeMediaItemId)) { + // This transition is ignored + return null; + } + beforeMediaItem = getMediaItem(beforeMediaItemId); } else { beforeMediaItem = null; } - final String afterMediaItemId = parser.getAttributeValue("", - ATTR_AFTER_MEDIA_ITEM_ID); + final String afterMediaItemId = parser.getAttributeValue("", ATTR_AFTER_MEDIA_ITEM_ID); final MediaItem afterMediaItem; if (afterMediaItemId != null) { + if (ignoredMediaItems.contains(afterMediaItemId)) { + // This transition is ignored + return null; + } + afterMediaItem = getMediaItem(afterMediaItemId); } else { afterMediaItem = null; @@ -1093,18 +1116,10 @@ public class VideoEditorImpl implements VideoEditor { transition = new TransitionFadeBlack(transitionId, afterMediaItem, beforeMediaItem, durationMs, behavior); } else { - transition = null; + throw new IllegalArgumentException("Invalid transition type: " + type); } - if (beforeMediaItem != null) { - beforeMediaItem.setBeginTransition(transition); - } - - if (afterMediaItem != null) { - afterMediaItem.setEndTransition(transition); - } - - isTransitionGenerated = Boolean.parseBoolean(parser.getAttributeValue("", + final boolean isTransitionGenerated = Boolean.parseBoolean(parser.getAttributeValue("", ATTR_IS_TRANSITION_GENERATED)); if (isTransitionGenerated == true) { final String transitionFile = parser.getAttributeValue("", @@ -1116,10 +1131,19 @@ public class VideoEditorImpl implements VideoEditor { transition.setFilename(null); } } + + // Use the transition + if (beforeMediaItem != null) { + beforeMediaItem.setBeginTransition(transition); + } + + if (afterMediaItem != null) { + afterMediaItem.setEndTransition(transition); + } + return transition; } - /** * Parse the overlay * @@ -1131,40 +1155,32 @@ public class VideoEditorImpl implements VideoEditor { private Overlay parseOverlay(XmlPullParser parser, MediaItem mediaItem) { final String overlayId = parser.getAttributeValue("", ATTR_ID); final String type = parser.getAttributeValue("", ATTR_TYPE); - final long durationMs = Long.parseLong(parser.getAttributeValue("", - ATTR_DURATION)); - final long startTimeMs = Long.parseLong(parser.getAttributeValue("", - ATTR_BEGIN_TIME)); + final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION)); + final long startTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_BEGIN_TIME)); final Overlay overlay; if (OverlayFrame.class.getSimpleName().equals(type)) { final String filename = parser.getAttributeValue("", ATTR_FILENAME); - overlay = new OverlayFrame(mediaItem, overlayId, filename, - startTimeMs, durationMs); + overlay = new OverlayFrame(mediaItem, overlayId, filename, startTimeMs, durationMs); } else { - overlay = null; + throw new IllegalArgumentException("Invalid overlay type: " + type); } - final String overlayRgbFileName = parser.getAttributeValue("", - ATTR_OVERLAY_RGB_FILENAME); + final String overlayRgbFileName = parser.getAttributeValue("", ATTR_OVERLAY_RGB_FILENAME); if (overlayRgbFileName != null) { ((OverlayFrame)overlay).setFilename(overlayRgbFileName); - final int overlayFrameWidth = - Integer.parseInt(parser.getAttributeValue("", + final int overlayFrameWidth = Integer.parseInt(parser.getAttributeValue("", ATTR_OVERLAY_FRAME_WIDTH)); - final int overlayFrameHeight = - Integer.parseInt(parser.getAttributeValue("", + final int overlayFrameHeight = Integer.parseInt(parser.getAttributeValue("", ATTR_OVERLAY_FRAME_HEIGHT)); ((OverlayFrame)overlay).setOverlayFrameWidth(overlayFrameWidth); ((OverlayFrame)overlay).setOverlayFrameHeight(overlayFrameHeight); - final int resizedRGBFrameWidth = - Integer.parseInt(parser.getAttributeValue("", + final int resizedRGBFrameWidth = Integer.parseInt(parser.getAttributeValue("", ATTR_OVERLAY_RESIZED_RGB_FRAME_WIDTH)); - final int resizedRGBFrameHeight = - Integer.parseInt(parser.getAttributeValue("", + final int resizedRGBFrameHeight = Integer.parseInt(parser.getAttributeValue("", ATTR_OVERLAY_RESIZED_RGB_FRAME_HEIGHT)); ((OverlayFrame)overlay).setResizedRGBSize(resizedRGBFrameWidth, resizedRGBFrameHeight); @@ -1184,21 +1200,17 @@ public class VideoEditorImpl implements VideoEditor { private Effect parseEffect(XmlPullParser parser, MediaItem mediaItem) { final String effectId = parser.getAttributeValue("", ATTR_ID); final String type = parser.getAttributeValue("", ATTR_TYPE); - final long durationMs = Long.parseLong(parser.getAttributeValue("", - ATTR_DURATION)); - final long startTimeMs = Long.parseLong(parser.getAttributeValue("", - ATTR_BEGIN_TIME)); + final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION)); + final long startTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_BEGIN_TIME)); final Effect effect; if (EffectColor.class.getSimpleName().equals(type)) { - final int colorEffectType = - Integer.parseInt(parser.getAttributeValue("", + final int colorEffectType = Integer.parseInt(parser.getAttributeValue("", ATTR_COLOR_EFFECT_TYPE)); final int color; if (colorEffectType == EffectColor.TYPE_COLOR || colorEffectType == EffectColor.TYPE_GRADIENT) { - color = Integer.parseInt(parser.getAttributeValue("", - ATTR_COLOR_EFFECT_VALUE)); + color = Integer.parseInt(parser.getAttributeValue("", ATTR_COLOR_EFFECT_VALUE)); } else { color = 0; } @@ -1206,27 +1218,19 @@ public class VideoEditorImpl implements VideoEditor { durationMs, colorEffectType, color); } else if (EffectKenBurns.class.getSimpleName().equals(type)) { final Rect startRect = new Rect( - Integer.parseInt(parser.getAttributeValue("", - ATTR_START_RECT_LEFT)), - Integer.parseInt(parser.getAttributeValue("", - ATTR_START_RECT_TOP)), - Integer.parseInt(parser.getAttributeValue("", - ATTR_START_RECT_RIGHT)), - Integer.parseInt(parser.getAttributeValue("", - ATTR_START_RECT_BOTTOM))); + Integer.parseInt(parser.getAttributeValue("", ATTR_START_RECT_LEFT)), + Integer.parseInt(parser.getAttributeValue("", ATTR_START_RECT_TOP)), + Integer.parseInt(parser.getAttributeValue("", ATTR_START_RECT_RIGHT)), + Integer.parseInt(parser.getAttributeValue("", ATTR_START_RECT_BOTTOM))); final Rect endRect = new Rect( - Integer.parseInt(parser.getAttributeValue("", - ATTR_END_RECT_LEFT)), - Integer.parseInt(parser.getAttributeValue("", - ATTR_END_RECT_TOP)), - Integer.parseInt(parser.getAttributeValue("", - ATTR_END_RECT_RIGHT)), - Integer.parseInt(parser.getAttributeValue("", - ATTR_END_RECT_BOTTOM))); + Integer.parseInt(parser.getAttributeValue("", ATTR_END_RECT_LEFT)), + Integer.parseInt(parser.getAttributeValue("", ATTR_END_RECT_TOP)), + Integer.parseInt(parser.getAttributeValue("", ATTR_END_RECT_RIGHT)), + Integer.parseInt(parser.getAttributeValue("", ATTR_END_RECT_BOTTOM))); effect = new EffectKenBurns(mediaItem, effectId, startRect, endRect, startTimeMs, durationMs); } else { - effect = null; + throw new IllegalArgumentException("Invalid effect type: " + type); } return effect; @@ -1239,48 +1243,34 @@ public class VideoEditorImpl implements VideoEditor { * * @return The audio track */ - private AudioTrack parseAudioTrack(XmlPullParser parser) { + private AudioTrack parseAudioTrack(XmlPullParser parser) throws IOException { final String audioTrackId = parser.getAttributeValue("", ATTR_ID); final String filename = parser.getAttributeValue("", ATTR_FILENAME); - final long startTimeMs = Long.parseLong(parser.getAttributeValue("", - ATTR_START_TIME)); - final long beginMs = Long.parseLong(parser.getAttributeValue("", - ATTR_BEGIN_TIME)); - final long endMs = Long.parseLong(parser.getAttributeValue("", - ATTR_END_TIME)); - final int volume = Integer.parseInt(parser.getAttributeValue("", - ATTR_VOLUME)); - final boolean muted = Boolean.parseBoolean(parser.getAttributeValue("", - ATTR_MUTED)); - final boolean loop = Boolean.parseBoolean(parser.getAttributeValue("", - ATTR_LOOP)); - final boolean duckingEnabled = - Boolean.parseBoolean(parser.getAttributeValue("", - ATTR_DUCK_ENABLED)); - final int duckThreshold = Integer.parseInt(parser.getAttributeValue("", - ATTR_DUCK_THRESHOLD)); - final int duckedTrackVolume = - Integer.parseInt(parser.getAttributeValue("", + final long startTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_START_TIME)); + final long beginMs = Long.parseLong(parser.getAttributeValue("", ATTR_BEGIN_TIME)); + final long endMs = Long.parseLong(parser.getAttributeValue("", ATTR_END_TIME)); + final int volume = Integer.parseInt(parser.getAttributeValue("", ATTR_VOLUME)); + final boolean muted = Boolean.parseBoolean(parser.getAttributeValue("", ATTR_MUTED)); + final boolean loop = Boolean.parseBoolean(parser.getAttributeValue("", ATTR_LOOP)); + final boolean duckingEnabled = Boolean.parseBoolean( + parser.getAttributeValue("", ATTR_DUCK_ENABLED)); + final int duckThreshold = Integer.parseInt( + parser.getAttributeValue("", ATTR_DUCK_THRESHOLD)); + final int duckedTrackVolume = Integer.parseInt(parser.getAttributeValue("", ATTR_DUCKED_TRACK_VOLUME)); - final String waveformFilename = parser.getAttributeValue("", - ATTR_AUDIO_WAVEFORM_FILENAME); - try { - final AudioTrack audioTrack = new AudioTrack(this, audioTrackId, - filename, startTimeMs, - beginMs, endMs, loop, - volume, muted, - duckingEnabled, - duckThreshold, - duckedTrackVolume, - waveformFilename); - - return audioTrack; - } catch (IOException ex) { - return null; - } - } + final String waveformFilename = parser.getAttributeValue("", ATTR_AUDIO_WAVEFORM_FILENAME); + final AudioTrack audioTrack = new AudioTrack(this, audioTrackId, + filename, startTimeMs, + beginMs, endMs, loop, + volume, muted, + duckingEnabled, + duckThreshold, + duckedTrackVolume, + waveformFilename); + return audioTrack; + } /* * {@inheritDoc} @@ -1424,7 +1414,8 @@ public class VideoEditorImpl implements VideoEditor { Integer.toString(endRect.bottom)); final MediaItem mItem = effect.getMediaItem(); if(((MediaImageItem)mItem).getGeneratedImageClip() != null) { - serializer.attribute("", ATTR_IS_IMAGE_CLIP_GENERATED,Boolean.toString(true)); + serializer.attribute("", ATTR_IS_IMAGE_CLIP_GENERATED, + Boolean.toString(true)); serializer.attribute("", ATTR_GENERATED_IMAGE_CLIP, ((MediaImageItem)mItem).getGeneratedImageClip()); } else { @@ -1447,28 +1438,22 @@ public class VideoEditorImpl implements VideoEditor { for (Transition transition : mTransitions) { serializer.startTag("", TAG_TRANSITION); serializer.attribute("", ATTR_ID, transition.getId()); - serializer.attribute("", ATTR_TYPE, - transition.getClass().getSimpleName()); - serializer.attribute("", ATTR_DURATION, - Long.toString(transition.getDuration())); - serializer.attribute("", ATTR_BEHAVIOR, - Integer.toString(transition.getBehavior())); + serializer.attribute("", ATTR_TYPE, transition.getClass().getSimpleName()); + serializer.attribute("", ATTR_DURATION, Long.toString(transition.getDuration())); + serializer.attribute("", ATTR_BEHAVIOR, Integer.toString(transition.getBehavior())); serializer.attribute("", ATTR_IS_TRANSITION_GENERATED, Boolean.toString(transition.isGenerated())); if (transition.isGenerated() == true) { - serializer.attribute("", ATTR_GENERATED_TRANSITION_CLIP, - transition.mFilename); + serializer.attribute("", ATTR_GENERATED_TRANSITION_CLIP, transition.mFilename); } final MediaItem afterMediaItem = transition.getAfterMediaItem(); if (afterMediaItem != null) { - serializer.attribute("", ATTR_AFTER_MEDIA_ITEM_ID, - afterMediaItem.getId()); + serializer.attribute("", ATTR_AFTER_MEDIA_ITEM_ID, afterMediaItem.getId()); } final MediaItem beforeMediaItem = transition.getBeforeMediaItem(); if (beforeMediaItem != null) { - serializer.attribute("", ATTR_BEFORE_MEDIA_ITEM_ID, - beforeMediaItem.getId()); + serializer.attribute("", ATTR_BEFORE_MEDIA_ITEM_ID, beforeMediaItem.getId()); } if (transition instanceof TransitionSliding) { @@ -1492,14 +1477,10 @@ public class VideoEditorImpl implements VideoEditor { serializer.startTag("", TAG_AUDIO_TRACK); serializer.attribute("", ATTR_ID, at.getId()); serializer.attribute("", ATTR_FILENAME, at.getFilename()); - serializer.attribute("", ATTR_START_TIME, - Long.toString(at.getStartTime())); - serializer.attribute("", ATTR_BEGIN_TIME, - Long.toString(at.getBoundaryBeginTime())); - serializer.attribute("", ATTR_END_TIME, - Long.toString(at.getBoundaryEndTime())); - serializer.attribute("", ATTR_VOLUME, - Integer.toString(at.getVolume())); + serializer.attribute("", ATTR_START_TIME, Long.toString(at.getStartTime())); + serializer.attribute("", ATTR_BEGIN_TIME, Long.toString(at.getBoundaryBeginTime())); + serializer.attribute("", ATTR_END_TIME, Long.toString(at.getBoundaryEndTime())); + serializer.attribute("", ATTR_VOLUME, Integer.toString(at.getVolume())); serializer.attribute("", ATTR_DUCK_ENABLED, Boolean.toString(at.isDuckingEnabled())); serializer.attribute("", ATTR_DUCKED_TRACK_VOLUME, diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_default.png Binary files differindex 9812339f04c8..ca8656cd70ea 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_default.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_default.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_default.png Binary files differindex 4f61511af1ae..a4f4e872b5d6 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_default.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_default.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_default.png Binary files differindex 60050751fa95..06c99218f234 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_default.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_default.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_default.png Binary files differindex 2591521f2cc3..cb938fedcc3e 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_default.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_default.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_default.png Binary files differindex 77546575f3a2..b2c4d45d172c 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_default.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_default.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png Binary files differindex 08f1993feaf7..fe9be2ca1179 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png Binary files differindex 4a40b14f1ef4..f9b39666ec55 100644 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_in.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_in.png Binary files differnew file mode 100644 index 000000000000..883808abc328 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_in.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_out.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_out.png Binary files differnew file mode 100644 index 000000000000..8ea42c79c44e --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_out.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_in.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_in.png Binary files differnew file mode 100644 index 000000000000..71429ae49123 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_in.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_inout.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_inout.png Binary files differnew file mode 100644 index 000000000000..5c34554dddbd --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_inout.png diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_out.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_out.png Binary files differnew file mode 100644 index 000000000000..44e7072b7dd1 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_wifi_out.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png Binary files differindex 4bcd2be5cb86..91fd0e85f7ad 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png Binary files differindex 92ffde9988e0..e4d5a328bdb3 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png Binary files differindex cfeba3e5e7f0..accdcfd24ef7 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png Binary files differindex 2490cdf8dd98..0d58c3ad9cec 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png Binary files differindex 1d97e0581c59..88f0a44141a3 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth.png Binary files differindex 5ca2415a6670..45a97fd233ac 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png Binary files differindex b727c2d81310..306afd09804f 100644 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_in.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_in.png Binary files differnew file mode 100644 index 000000000000..e429ea180546 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_in.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_inout.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_inout.png Binary files differnew file mode 100644 index 000000000000..e3bcaf955fb5 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_inout.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_out.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_out.png Binary files differnew file mode 100644 index 000000000000..26db91e1241b --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_out.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_in.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_in.png Binary files differnew file mode 100644 index 000000000000..957c5baa9832 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_in.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_inout.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_inout.png Binary files differnew file mode 100644 index 000000000000..aec60502a575 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_inout.png diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_out.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_out.png Binary files differnew file mode 100644 index 000000000000..bb8eea0d7fc7 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_out.png diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml b/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml index 4e41e538fef4..a892cd91c1b4 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml @@ -84,7 +84,7 @@ android:layout_height="wrap_content" android:singleLine="true" android:textSize="40sp" - android:textColor="#8cccdaff" /> + android:textColor="#ff525e79" /> </com.android.systemui.statusbar.tablet.HoloClock> <TextView @@ -104,10 +104,16 @@ android:id="@+id/signal_battery_cluster" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_marginRight="8dp" + android:layout_marginRight="16dp" android:orientation="horizontal" android:gravity="center" > + <ImageView + android:id="@+id/bluetooth" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:visibility="gone" + /> <FrameLayout android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -123,6 +129,11 @@ android:layout_height="wrap_content" android:layout_width="wrap_content" /> + <ImageView + android:id="@+id/network_direction" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + /> </FrameLayout> <ImageView android:id="@+id/battery" diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel_title.xml b/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel_title.xml index 15b2b708d46e..c87960950d8c 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel_title.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel_title.xml @@ -23,67 +23,89 @@ android:orientation="vertical" android:background="@drawable/notify_panel_clock_bg" > - <ImageView - android:id="@+id/network_signal" - android:layout_height="32dp" - android:layout_width="32dp" - android:scaleType="centerInside" + <LinearLayout + android:id="@+id/icons" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentBottom="true" - android:baseline="22dp" + android:baselineAlignedChildIndex="0" android:layout_marginLeft="32dp" android:layout_marginTop="16dp" android:layout_marginBottom="16dp" - /> - - <ImageView - android:id="@+id/network_type" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:layout_alignLeft="@id/network_signal" - android:layout_alignBottom="@id/network_signal" - android:layout_marginRight="8dp" - /> + > + <ImageView + android:id="@+id/bluetooth" + android:layout_height="32dp" + android:layout_width="32dp" + android:scaleType="centerInside" + android:baseline="22dp" + android:visibility="gone" + /> - <TextView - android:id="@+id/network_text" - style="@style/StatusBarNotificationText" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/network_signal" - android:layout_marginRight="8dp" - android:layout_alignBaseline="@id/network_signal" - android:singleLine="true" - android:text="@string/status_bar_settings_settings_button" - /> + <FrameLayout + android:id="@+id/netwerk" + android:layout_height="32dp" + android:layout_width="32dp" + android:layout_marginRight="4dp" + > + <ImageView + android:id="@+id/network_signal" + android:layout_height="match_parent" + android:layout_width="match_parent" + /> + <ImageView + android:id="@+id/network_type" + android:layout_height="match_parent" + android:layout_width="match_parent" + /> + <ImageView + android:id="@+id/network_direction" + android:layout_height="match_parent" + android:layout_width="match_parent" + /> + </FrameLayout> + + <TextView + android:id="@+id/network_text" + style="@style/StatusBarNotificationText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/netwerk" + android:layout_marginRight="8dp" + android:layout_alignBaseline="@id/network_signal" + android:singleLine="true" + android:text="@string/status_bar_settings_settings_button" + /> - <ImageView - android:id="@+id/battery" - android:layout_height="32dp" - android:layout_width="32dp" - android:scaleType="centerInside" - android:layout_toRightOf="@id/network_text" - android:layout_alignBaseline="@id/network_signal" - android:baseline="22dp" - /> + <ImageView + android:id="@+id/battery" + android:layout_height="32dp" + android:layout_width="32dp" + android:scaleType="centerInside" + android:layout_toRightOf="@id/network_text" + android:layout_alignBaseline="@id/network_signal" + android:baseline="22dp" + /> - <TextView - android:id="@+id/battery_text" - style="@style/StatusBarNotificationText" - android:layout_width="56dp" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/battery" - android:layout_alignBaseline="@id/battery" - android:layout_marginRight="8dp" - android:singleLine="true" - android:text="@string/status_bar_settings_settings_button" - /> + <TextView + android:id="@+id/battery_text" + style="@style/StatusBarNotificationText" + android:layout_width="56dp" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/battery" + android:layout_alignBaseline="@id/battery" + android:layout_marginRight="8dp" + android:singleLine="true" + android:text="@string/status_bar_settings_settings_button" + /> + </LinearLayout> <ImageView android:id="@+id/settings_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignBaseline="@id/battery" + android:layout_alignBaseline="@id/icons" android:layout_alignParentRight="true" android:paddingRight="16dp" android:src="@drawable/ic_sysbar_quicksettings" diff --git a/packages/SystemUI/res/values-xlarge/colors.xml b/packages/SystemUI/res/values-xlarge/colors.xml index 7f1e3588079a..1fd396d754b7 100644 --- a/packages/SystemUI/res/values-xlarge/colors.xml +++ b/packages/SystemUI/res/values-xlarge/colors.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <resources> <drawable name="status_bar_background">#000000</drawable> - <drawable name="notification_icon_area_smoke">#D0000000</drawable> + <drawable name="notification_icon_area_smoke">#CC000000</drawable> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 004174ee611a..3d904ee52bda 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -48,7 +48,12 @@ public class NotificationData { private final ArrayList<Entry> mEntries = new ArrayList<Entry>(); private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() { public int compare(Entry a, Entry b) { - return (int)(a.notification.notification.when - b.notification.notification.when); + final StatusBarNotification na = a.notification; + final StatusBarNotification nb = b.notification; + int priDiff = na.priority - nb.priority; + return (priDiff != 0) + ? priDiff + : (int)(na.notification.when - nb.notification.when); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java new file mode 100644 index 000000000000..0525054b4aaa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java @@ -0,0 +1,79 @@ +/* + * 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.policy; + +import java.util.ArrayList; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.util.Slog; +import android.view.View; +import android.widget.ImageView; + +import com.android.systemui.R; + +public class BluetoothController extends BroadcastReceiver { + private static final String TAG = "StatusBar.BluetoothController"; + + private Context mContext; + private ArrayList<ImageView> mIconViews = new ArrayList<ImageView>(); + + private int mIconId = R.drawable.stat_sys_data_bluetooth; + private boolean mEnabled; + + public BluetoothController(Context context) { + mContext = context; + + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); + context.registerReceiver(this, filter); + } + + public void addIconView(ImageView v) { + mIconViews.add(v); + } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); + mEnabled = state == BluetoothAdapter.STATE_ON; + } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) { + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, + BluetoothAdapter.STATE_DISCONNECTED); + if (state == BluetoothAdapter.STATE_CONNECTED) { + mIconId = R.drawable.stat_sys_data_bluetooth_connected; + } else { + mIconId = R.drawable.stat_sys_data_bluetooth; + } + } + + + int N = mIconViews.size(); + for (int i=0; i<N; i++) { + ImageView v = mIconViews.get(i); + v.setImageResource(mIconId); + v.setVisibility(mEnabled ? View.VISIBLE : View.GONE); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index c94f9ee8808c..42868db00788 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -75,8 +75,8 @@ public class NetworkController extends BroadcastReceiver { String mNetworkNameSeparator; int mPhoneSignalIconId; int mDataDirectionIconId; + int mDataDirectionOverlayIconId; int mDataSignalIconId; - int mDataActiveSignalIconId; int mDataTypeIconId; boolean mDataActive; @@ -101,12 +101,14 @@ public class NetworkController extends BroadcastReceiver { Context mContext; ArrayList<ImageView> mPhoneSignalIconViews = new ArrayList<ImageView>(); ArrayList<ImageView> mDataDirectionIconViews = new ArrayList<ImageView>(); + ArrayList<ImageView> mDataDirectionOverlayIconViews = new ArrayList<ImageView>(); ArrayList<ImageView> mWifiIconViews = new ArrayList<ImageView>(); ArrayList<ImageView> mCombinedSignalIconViews = new ArrayList<ImageView>(); ArrayList<ImageView> mDataTypeIconViews = new ArrayList<ImageView>(); ArrayList<TextView> mLabelViews = new ArrayList<TextView>(); int mLastPhoneSignalIconId = -1; int mLastDataDirectionIconId = -1; + int mLastDataDirectionOverlayIconId = -1; int mLastWifiIconId = -1; int mLastCombinedSignalIconId = -1; int mLastDataTypeIconId = -1; @@ -163,6 +165,10 @@ public class NetworkController extends BroadcastReceiver { mDataDirectionIconViews.add(v); } + public void addDataDirectionOverlayIconView(ImageView v) { + mDataDirectionOverlayIconViews.add(v); + } + public void addWifiIconView(ImageView v) { mWifiIconViews.add(v); } @@ -367,17 +373,15 @@ public class NetworkController extends BroadcastReceiver { if (Settings.System.getInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 1) { mPhoneSignalIconId = R.drawable.stat_sys_signal_flightmode; - mDataActiveSignalIconId = mDataSignalIconId = R.drawable.stat_sys_signal_flightmode; + mDataSignalIconId = R.drawable.stat_sys_signal_flightmode; } else { mPhoneSignalIconId = R.drawable.stat_sys_signal_null; - // note we use 0 instead of null - mDataActiveSignalIconId = mDataSignalIconId = R.drawable.stat_sys_signal_0; + mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null } } else { if (mSignalStrength == null) { mPhoneSignalIconId = R.drawable.stat_sys_signal_null; - // note we use 0 instead of null - mDataActiveSignalIconId = mDataSignalIconId = R.drawable.stat_sys_signal_0; + mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null } else if (isCdma()) { // If 3G(EV) and 1x network are available than 3G should be // displayed, displayed RSSI should be from the EV side. @@ -396,8 +400,6 @@ public class NetworkController extends BroadcastReceiver { } mPhoneSignalIconId = iconList[iconLevel]; mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel]; - mDataActiveSignalIconId - = TelephonyIcons.DATA_SIGNAL_STRENGTH_ACTIVE[mInetCondition][iconLevel]; } else { int asu = mSignalStrength.getGsmSignalStrength(); @@ -421,8 +423,6 @@ public class NetworkController extends BroadcastReceiver { } mPhoneSignalIconId = iconList[iconLevel]; mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel]; - mDataActiveSignalIconId - = TelephonyIcons.DATA_SIGNAL_STRENGTH_ACTIVE[mInetCondition][iconLevel]; } } } @@ -685,6 +685,7 @@ public class NetworkController extends BroadcastReceiver { Context context = mContext; int combinedSignalIconId; + int dataDirectionOverlayIconId = 0; int dataTypeIconId; String label; int N; @@ -699,16 +700,22 @@ public class NetworkController extends BroadcastReceiver { dataTypeIconId = 0; } else if (mDataConnected) { label = mNetworkName; + combinedSignalIconId = mDataSignalIconId; switch (mDataActivity) { case TelephonyManager.DATA_ACTIVITY_IN: + dataDirectionOverlayIconId = R.drawable.stat_sys_signal_in; + break; case TelephonyManager.DATA_ACTIVITY_OUT: + dataDirectionOverlayIconId = R.drawable.stat_sys_signal_out; + break; case TelephonyManager.DATA_ACTIVITY_INOUT: - combinedSignalIconId = mDataActiveSignalIconId; + dataDirectionOverlayIconId = R.drawable.stat_sys_signal_inout; break; default: - combinedSignalIconId = mDataSignalIconId; + dataDirectionOverlayIconId = 0; break; } + combinedSignalIconId = mDataSignalIconId; dataTypeIconId = mDataTypeIconId; } else if (mBluetoothTethered) { label = mContext.getString(R.string.bluetooth_tethered); @@ -724,11 +731,11 @@ public class NetworkController extends BroadcastReceiver { Slog.d(TAG, "refreshViews combinedSignalIconId=0x" + Integer.toHexString(combinedSignalIconId) + "/" + getResourceName(combinedSignalIconId) + + " dataDirectionOverlayIconId=0x" + Integer.toHexString(dataDirectionOverlayIconId) + " mDataActivity=" + mDataActivity + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId) + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId) + " mDataSignalIconId=0x" + Integer.toHexString(mDataSignalIconId) - + " mDataActiveSignalIconId=0x" + Integer.toHexString(mDataActiveSignalIconId) + " mDataTypeIconId=0x" + Integer.toHexString(mDataTypeIconId) + " mWifiIconId=0x" + Integer.toHexString(mWifiIconId) + " mBluetoothTetherIconId=0x" + Integer.toHexString(mBluetoothTetherIconId)); @@ -789,6 +796,22 @@ public class NetworkController extends BroadcastReceiver { } } + // the data direction overlay + if (mLastDataDirectionOverlayIconId != dataDirectionOverlayIconId) { + Slog.d(TAG, "changing data overlay icon id to " + dataDirectionOverlayIconId); + mLastDataDirectionOverlayIconId = dataDirectionOverlayIconId; + N = mDataDirectionOverlayIconViews.size(); + for (int i=0; i<N; i++) { + final ImageView v = mDataDirectionOverlayIconViews.get(i); + if (dataDirectionOverlayIconId == 0) { + v.setVisibility(View.INVISIBLE); + } else { + v.setVisibility(View.VISIBLE); + v.setImageResource(dataDirectionOverlayIconId); + } + } + } + // the label in the notification panel if (!mLastLabel.equals(label)) { mLastLabel = label; @@ -834,10 +857,6 @@ public class NetworkController extends BroadcastReceiver { pw.print(Integer.toHexString(mDataSignalIconId)); pw.print("/"); pw.println(getResourceName(mDataSignalIconId)); - pw.print(" mDataActiveSignalIconId="); - pw.print(Integer.toHexString(mDataActiveSignalIconId)); - pw.print("/"); - pw.println(getResourceName(mDataActiveSignalIconId)); pw.print(" mDataTypeIconId="); pw.print(Integer.toHexString(mDataTypeIconId)); pw.print("/"); @@ -872,6 +891,10 @@ public class NetworkController extends BroadcastReceiver { pw.print(Integer.toHexString(mLastDataDirectionIconId)); pw.print("/"); pw.println(getResourceName(mLastDataDirectionIconId)); + pw.print(" mLastDataDirectionOverlayIconId=0x"); + pw.print(Integer.toHexString(mLastDataDirectionOverlayIconId)); + pw.print("/"); + pw.println(getResourceName(mLastDataDirectionOverlayIconId)); pw.print(" mLastWifiIconId=0x"); pw.print(Integer.toHexString(mLastWifiIconId)); pw.print("/"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index 4373dba7a62f..4bac07fb44ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -64,6 +64,7 @@ import com.android.internal.statusbar.StatusBarNotification; import com.android.systemui.R; import com.android.systemui.statusbar.*; import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.recent.RecentApplicationsActivity; @@ -133,6 +134,7 @@ public class TabletStatusBar extends StatusBar implements HeightReceiver mHeightReceiver; BatteryController mBatteryController; + BluetoothController mBluetoothController; NetworkController mNetworkController; View mBarContents; @@ -170,10 +172,14 @@ public class TabletStatusBar extends StatusBar implements mBatteryController.addIconView((ImageView)mNotificationPanel.findViewById(R.id.battery)); mBatteryController.addLabelView( (TextView)mNotificationPanel.findViewById(R.id.battery_text)); + mBluetoothController.addIconView( + (ImageView)mNotificationPanel.findViewById(R.id.bluetooth)); mNetworkController.addCombinedSignalIconView( (ImageView)mNotificationPanel.findViewById(R.id.network_signal)); mNetworkController.addDataTypeIconView( (ImageView)mNotificationPanel.findViewById(R.id.network_type)); + mNetworkController.addDataDirectionOverlayIconView( + (ImageView)mNotificationPanel.findViewById(R.id.network_direction)); mNetworkController.addLabelView( (TextView)mNotificationPanel.findViewById(R.id.network_text)); mNetworkController.addLabelView( @@ -355,11 +361,15 @@ public class TabletStatusBar extends StatusBar implements // The icons mBatteryController = new BatteryController(mContext); mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery)); + mBluetoothController = new BluetoothController(mContext); + mBluetoothController.addIconView((ImageView)sb.findViewById(R.id.bluetooth)); mNetworkController = new NetworkController(mContext); mNetworkController.addCombinedSignalIconView( (ImageView)sb.findViewById(R.id.network_signal)); mNetworkController.addDataTypeIconView( (ImageView)sb.findViewById(R.id.network_type)); + mNetworkController.addDataDirectionOverlayIconView( + (ImageView)sb.findViewById(R.id.network_direction)); // The navigation buttons mBackButton = (ImageView)sb.findViewById(R.id.back); @@ -660,7 +670,8 @@ public class TabletStatusBar extends StatusBar implements && oldContentView.getLayoutId() == contentView.getLayoutId(); ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent(); boolean orderUnchanged = notification.notification.when==oldNotification.notification.when - && notification.isOngoing() == oldNotification.isOngoing(); + && notification.priority == oldNotification.priority; + // priority now encompasses isOngoing() boolean isLastAnyway = rowParent.indexOfChild(oldEntry.row) == rowParent.getChildCount()-1; if (contentsUnchanged && (orderUnchanged || isLastAnyway)) { if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key); @@ -1177,7 +1188,10 @@ public class TabletStatusBar extends StatusBar implements } // Add the icon. - mNotns.add(entry); + int pos = mNotns.add(entry); + if (DEBUG) { + Slog.d(TAG, "addNotificationViews: added at " + pos); + } updateNotificationIcons(); return iconView; @@ -1264,7 +1278,7 @@ public class TabletStatusBar extends StatusBar implements for (int i=0; i<toShow.size(); i++) { View v = toShow.get(i); if (v.getParent() == null) { - mPile.addView(toShow.get(i)); + mPile.addView(v, N-1-i); // the notification panel has newest at the bottom } } diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/ScreenRotationAnimation.java index 90318f19044e..ef00b08ff1a4 100644 --- a/services/java/com/android/server/ScreenRotationAnimation.java +++ b/services/java/com/android/server/ScreenRotationAnimation.java @@ -39,10 +39,48 @@ class ScreenRotationAnimation { static final String TAG = "ScreenRotationAnimation"; static final boolean DEBUG = false; + static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200; + + class BlackSurface { + final int left; + final int top; + final Surface surface; + + BlackSurface(SurfaceSession session, int layer, int l, int t, int w, int h) + throws Surface.OutOfResourcesException { + left = l; + top = t; + surface = new Surface(session, 0, "BlackSurface", + -1, w, h, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); + surface.setAlpha(1.0f); + surface.setLayer(FREEZE_LAYER); + } + + void setMatrix(Matrix matrix) { + mTmpMatrix.setTranslate(left, top); + mTmpMatrix.postConcat(matrix); + mTmpMatrix.getValues(mTmpFloats); + surface.setPosition((int)mTmpFloats[Matrix.MTRANS_X], + (int)mTmpFloats[Matrix.MTRANS_Y]); + surface.setMatrix( + mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y], + mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]); + if (false) { + Slog.i(TAG, "Black Surface @ (" + left + "," + top + "): (" + + mTmpFloats[Matrix.MTRANS_X] + "," + + mTmpFloats[Matrix.MTRANS_Y] + ") matrix=[" + + mTmpFloats[Matrix.MSCALE_X] + "," + + mTmpFloats[Matrix.MSCALE_Y] + "][" + + mTmpFloats[Matrix.MSKEW_X] + "," + + mTmpFloats[Matrix.MSKEW_Y] + "]"); + } + } + } + final Context mContext; final Display mDisplay; Surface mSurface; - Surface mBlackSurface; + BlackSurface[] mBlackSurfaces; int mWidth, mHeight; int mSnapshotRotation; @@ -60,6 +98,7 @@ class ScreenRotationAnimation { final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); final Matrix mSnapshotInitialMatrix = new Matrix(); final Matrix mSnapshotFinalMatrix = new Matrix(); + final Matrix mTmpMatrix = new Matrix(); final float[] mTmpFloats = new float[9]; public ScreenRotationAnimation(Context context, Display display, SurfaceSession session, @@ -97,23 +136,11 @@ class ScreenRotationAnimation { try { mSurface = new Surface(session, 0, "FreezeSurface", -1, mWidth, mHeight, PixelFormat.OPAQUE, 0); - mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 200); + mSurface.setLayer(FREEZE_LAYER + 1); } catch (Surface.OutOfResourcesException e) { Slog.w(TAG, "Unable to allocate freeze surface", e); } - if (true) { - try { - int size = mOriginalWidth > mOriginalHeight ? mOriginalWidth : mOriginalHeight; - mBlackSurface = new Surface(session, 0, "BlackSurface", - -1, size, size, PixelFormat.OPAQUE, Surface.FX_SURFACE_DIM); - mBlackSurface.setAlpha(1.0f); - mBlackSurface.setLayer(0); - } catch (Surface.OutOfResourcesException e) { - Slog.w(TAG, "Unable to allocate black surface", e); - } - } - setRotation(display.getRotation()); if (mSurface != null) { @@ -138,7 +165,7 @@ class ScreenRotationAnimation { paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); c.drawBitmap(screenshot, 0, 0, paint); } else { - c.drawColor(Color.GREEN, PorterDuff.Mode.SRC); + c.drawColor(Color.BLACK, PorterDuff.Mode.SRC); } mSurface.unlockCanvasAndPost(c); @@ -221,7 +248,8 @@ class ScreenRotationAnimation { /** * Returns true if animating. */ - public boolean dismiss(long maxAnimationDuration, float animationScale) { + public boolean dismiss(SurfaceSession session, long maxAnimationDuration, + float animationScale) { // Figure out how the screen has moved from the original rotation. int delta = deltaRotation(mCurRotation, mOriginalRotation); if (false && delta == 0) { @@ -277,6 +305,26 @@ class ScreenRotationAnimation { mEnterAnimation.restrictDuration(maxAnimationDuration); mEnterAnimation.scaleCurrentDuration(animationScale); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, + ">>> OPEN TRANSACTION ScreenRotationAnimation.dismiss"); + Surface.openTransaction(); + + mBlackSurfaces = new BlackSurface[4]; + try { + final int w = mDisplayMetrics.widthPixels; + final int h = mDisplayMetrics.heightPixels; + mBlackSurfaces[0] = new BlackSurface(session, FREEZE_LAYER, -w, -h, w, h*2); + mBlackSurfaces[1] = new BlackSurface(session, FREEZE_LAYER, 0, -h, w*2, h); + mBlackSurfaces[2] = new BlackSurface(session, FREEZE_LAYER, w, 0, w, h*2); + mBlackSurfaces[3] = new BlackSurface(session, FREEZE_LAYER, -w, h, w*2, h); + } catch (Surface.OutOfResourcesException e) { + Slog.w(TAG, "Unable to allocate black surface", e); + } finally { + Surface.closeTransaction(); + if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, + "<<< CLOSE TRANSACTION ScreenRotationAnimation.dismiss"); + } + return true; } @@ -285,9 +333,13 @@ class ScreenRotationAnimation { mSurface.destroy(); mSurface = null; } - if (mBlackSurface != null) { - mBlackSurface.destroy(); - mBlackSurface = null; + if (mBlackSurfaces != null) { + for (int i=0; i<mBlackSurfaces.length; i++) { + if (mBlackSurfaces[i] != null) { + mBlackSurfaces[i].surface.destroy(); + } + } + mBlackSurfaces = null; } if (mExitAnimation != null) { mExitAnimation.cancel(); @@ -325,12 +377,7 @@ class ScreenRotationAnimation { mExitAnimation = null; mExitTransformation.clear(); if (mSurface != null) { - mSurface.destroy(); - mSurface = null; - } - if (mBlackSurface != null) { - mBlackSurface.destroy(); - mBlackSurface = null; + mSurface.hide(); } } } @@ -343,6 +390,21 @@ class ScreenRotationAnimation { mEnterAnimation.cancel(); mEnterAnimation = null; mEnterTransformation.clear(); + if (mBlackSurfaces != null) { + for (int i=0; i<mBlackSurfaces.length; i++) { + if (mBlackSurfaces[i] != null) { + mBlackSurfaces[i].surface.hide(); + } + } + } + } else { + if (mBlackSurfaces != null) { + for (int i=0; i<mBlackSurfaces.length; i++) { + if (mBlackSurfaces[i] != null) { + mBlackSurfaces[i].setMatrix(mEnterTransformation.getMatrix()); + } + } + } } } diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index e1c03d4fe667..a98c3e36d48a 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -7842,7 +7842,7 @@ public class WindowManagerService extends IWindowManager.Stub if (selfTransformation) { tmpMatrix.postConcat(mTransformation.getMatrix()); } - tmpMatrix.postTranslate(frame.left, frame.top); + tmpMatrix.postTranslate(frame.left + mXOffset, frame.top + mYOffset); if (attachedTransformation != null) { tmpMatrix.postConcat(attachedTransformation.getMatrix()); } @@ -7866,8 +7866,8 @@ public class WindowManagerService extends IWindowManager.Stub mDtDx = tmpFloats[Matrix.MSKEW_Y]; mDsDy = tmpFloats[Matrix.MSKEW_X]; mDtDy = tmpFloats[Matrix.MSCALE_Y]; - int x = (int)tmpFloats[Matrix.MTRANS_X] + mXOffset; - int y = (int)tmpFloats[Matrix.MTRANS_Y] + mYOffset; + int x = (int)tmpFloats[Matrix.MTRANS_X]; + int y = (int)tmpFloats[Matrix.MTRANS_Y]; int w = frame.width(); int h = frame.height(); mShownFrame.set(x, y, x+w, y+h); @@ -9783,6 +9783,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean animating = false; boolean createWatermark = false; boolean updateRotation = false; + boolean screenRotationFinished = false; if (mFxSession == null) { mFxSession = new SurfaceSession(); @@ -9879,7 +9880,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mScreenRotationAnimation.stepAnimation(currentTime)) { animating = true; } else { - mScreenRotationAnimation = null; + screenRotationFinished = true; updateRotation = true; } } @@ -11043,6 +11044,11 @@ public class WindowManagerService extends IWindowManager.Stub mTurnOnScreen = false; } + if (screenRotationFinished && mScreenRotationAnimation != null) { + mScreenRotationAnimation.kill(); + mScreenRotationAnimation = null; + } + if (updateRotation) { if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation"); boolean changed = setRotationUncheckedLocked( @@ -11382,7 +11388,7 @@ public class WindowManagerService extends IWindowManager.Stub if (CUSTOM_SCREEN_ROTATION) { if (mScreenRotationAnimation != null) { - if (mScreenRotationAnimation.dismiss(MAX_ANIMATION_DURATION, + if (mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION, mTransitionAnimationScale)) { requestAnimationLocked(0); } else { diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp index 11f8feb1ff69..f79166d8acca 100644 --- a/services/surfaceflinger/LayerDim.cpp +++ b/services/surfaceflinger/LayerDim.cpp @@ -30,29 +30,12 @@ namespace android { // --------------------------------------------------------------------------- -bool LayerDim::sUseTexture; -GLuint LayerDim::sTexId; -EGLImageKHR LayerDim::sImage; -int32_t LayerDim::sWidth; -int32_t LayerDim::sHeight; - -// --------------------------------------------------------------------------- - LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client) : LayerBaseClient(flinger, display, client) { } -void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h) -{ - sTexId = -1; - sImage = EGL_NO_IMAGE_KHR; - sWidth = w; - sHeight = h; - sUseTexture = false; -} - LayerDim::~LayerDim() { } @@ -84,15 +67,7 @@ void LayerDim::onDraw(const Region& clip) const #endif glDisable(GL_TEXTURE_2D); - GLshort w = sWidth; - GLshort h = sHeight; - const GLshort vertices[4][2] = { - { 0, 0 }, - { 0, h }, - { w, h }, - { w, 0 } - }; - glVertexPointer(2, GL_SHORT, 0, vertices); + glVertexPointer(2, GL_FLOAT, 0, mVertices); while (it != end) { const Rect& r = *it++; diff --git a/services/surfaceflinger/LayerDim.h b/services/surfaceflinger/LayerDim.h index f0323149ac25..5631c0a417b5 100644 --- a/services/surfaceflinger/LayerDim.h +++ b/services/surfaceflinger/LayerDim.h @@ -31,11 +31,6 @@ namespace android { class LayerDim : public LayerBaseClient { - static bool sUseTexture; - static GLuint sTexId; - static EGLImageKHR sImage; - static int32_t sWidth; - static int32_t sHeight; public: LayerDim(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client); @@ -45,8 +40,6 @@ public: virtual bool needsBlending() const { return true; } virtual bool isSecure() const { return false; } virtual const char* getTypeId() const { return "LayerDim"; } - - static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h); }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 87b66eafa985..fd3f0c263072 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -259,8 +259,6 @@ status_t SurfaceFlinger::readyToRun() glLoadIdentity(); glOrthof(0, w, h, 0, 0, 1); - LayerDim::initDimmer(this, w, h); - mReadyToRunBarrier.open(); /* diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index fc50334da73b..353580910c4d 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -96,7 +96,16 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> - + + <activity + android:name="ViewLayersActivity6" + android:label="_ViewLayers6"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity android:name="AlphaLayersActivity" android:label="_αLayers"> diff --git a/tests/HwAccelerationTest/res/layout/view_layers_6.xml b/tests/HwAccelerationTest/res/layout/view_layers_6.xml new file mode 100644 index 000000000000..36cf8c9b80e3 --- /dev/null +++ b/tests/HwAccelerationTest/res/layout/view_layers_6.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1"> + + <Button + android:onClick="enableLayer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Enable layer" /> + + <Button + android:onClick="disableLayer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Disable layer" /> + + <Button + android:onClick="shrinkLayer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Shrink layer" /> + + <Button + android:onClick="growLayer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Grow layer" /> + + </LinearLayout> + + <ListView + android:id="@+id/list1" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1" /> + +</LinearLayout> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity6.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity6.java new file mode 100644 index 000000000000..2edfec70e1f7 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity6.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2011 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.test.hwui; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.os.Bundle; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; + +@SuppressWarnings({"UnusedDeclaration"}) +public class ViewLayersActivity6 extends Activity { + private final Paint mPaint = new Paint(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.view_layers_6); + + mPaint.setColorFilter(new PorterDuffColorFilter(0xff00ff00, PorterDuff.Mode.MULTIPLY)); + + setupList(R.id.list1); + } + + public void enableLayer(View v) { + findViewById(R.id.list1).setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); + } + + public void disableLayer(View v) { + findViewById(R.id.list1).setLayerType(View.LAYER_TYPE_NONE, null); + } + + public void growLayer(View v) { + findViewById(R.id.list1).getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; + findViewById(R.id.list1).requestLayout(); + } + + public void shrinkLayer(View v) { + findViewById(R.id.list1).getLayoutParams().height = 300; + findViewById(R.id.list1).requestLayout(); + } + + private void setupList(int listId) { + final ListView list = (ListView) findViewById(listId); + list.setAdapter(new SimpleListAdapter(this)); + } + + private static class SimpleListAdapter extends ArrayAdapter<String> { + public SimpleListAdapter(Context context) { + super(context, android.R.layout.simple_list_item_1, DATA_LIST); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView v = (TextView) super.getView(position, convertView, parent); + final Resources r = getContext().getResources(); + final DisplayMetrics metrics = r.getDisplayMetrics(); + v.setCompoundDrawablePadding((int) (6 * metrics.density + 0.5f)); + v.setCompoundDrawablesWithIntrinsicBounds(r.getDrawable(R.drawable.icon), + null, null, null); + return v; + } + } + + private static final String[] DATA_LIST = { + "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", + "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina", + "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan", + "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", + "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", + "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", + "British Indian Ocean Territory", "British Virgin Islands", "Brunei", "Bulgaria", + "Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde", + "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", + "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", + "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic", + "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic", + "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea", + "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland", + "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia", + "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar", + "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau", + "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary", + "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica", + "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos", + "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg", + "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", + "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova", + "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", + "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", + "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas", + "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru", + "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar", + "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena", + "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon", + "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal", + "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands", + "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea", + "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden", + "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas", + "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", + "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda", + "Ukraine", "United Arab Emirates", "United Kingdom", + "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", + "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara", + "Yemen", "Yugoslavia", "Zambia", "Zimbabwe" + }; +} |