diff options
104 files changed, 2812 insertions, 1364 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 8711ad7..2287859 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -135,6 +135,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew $(call add-clean-step, rm -f $(PRODUCT_OUT)/system/media/video/Disco*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/ImageProcessing_intermediates) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/ImageProcessing2_intermediates) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ @@ -27448,6 +27448,7 @@ package android.webkit { method public void setInitialScale(int); method public deprecated void setMapTrackballToArrowKeys(boolean); method public void setNetworkAvailable(boolean); + method public deprecated void setPictureListener(android.webkit.WebView.PictureListener); method public void setVerticalScrollbarOverlay(boolean); method public void setWebChromeClient(android.webkit.WebChromeClient); method public void setWebViewClient(android.webkit.WebViewClient); diff --git a/api/current.txt b/api/current.txt index 7cadf9b..7b2f161 100644 --- a/api/current.txt +++ b/api/current.txt @@ -27448,6 +27448,7 @@ package android.webkit { method public void setInitialScale(int); method public deprecated void setMapTrackballToArrowKeys(boolean); method public void setNetworkAvailable(boolean); + method public deprecated void setPictureListener(android.webkit.WebView.PictureListener); method public void setVerticalScrollbarOverlay(boolean); method public void setWebChromeClient(android.webkit.WebChromeClient); method public void setWebViewClient(android.webkit.WebViewClient); diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index c455b7d..463a18c 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -990,7 +990,10 @@ public final class Pm { } name = arg; try { - if (mUm.createUser(name, 0) == null) { + final UserInfo info = mUm.createUser(name, 0); + if (info != null) { + System.out.println("Success: created user id " + info.id); + } else { System.err.println("Error: couldn't create User."); } } catch (RemoteException e) { diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 888955c..2af65b9 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -448,6 +448,10 @@ public class AppWidgetManager { * and outside of the handler. * This method will only work when called from the uid that owns the AppWidget provider. * + * <p> + * This method will be ignored if a widget has not received a full update via + * {@link #updateAppWidget(int[], RemoteViews)}. + * * @param appWidgetIds The AppWidget instances for which to set the RemoteViews. * @param views The RemoteViews object containing the incremental update / command. */ @@ -476,6 +480,10 @@ public class AppWidgetManager { * and outside of the handler. * This method will only work when called from the uid that owns the AppWidget provider. * + * <p> + * This method will be ignored if a widget has not received a full update via + * {@link #updateAppWidget(int[], RemoteViews)}. + * * @param appWidgetId The AppWidget instance for which to set the RemoteViews. * @param views The RemoteViews object containing the incremental update / command. */ diff --git a/core/java/android/service/dreams/Sandman.java b/core/java/android/service/dreams/Sandman.java new file mode 100644 index 0000000..70142ce --- /dev/null +++ b/core/java/android/service/dreams/Sandman.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2012 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 android.service.dreams; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Slog; + +/** + * Internal helper for launching dreams to ensure consistency between the + * <code>UiModeManagerService</code> system service and the <code>Somnambulator</code> activity. + * + * @hide + */ +public final class Sandman { + private static final String TAG = "Sandman"; + + private static final int DEFAULT_SCREENSAVER_ENABLED = 1; + private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1; + + // The component name of a special dock app that merely launches a dream. + // We don't want to launch this app when docked because it causes an unnecessary + // activity transition. We just want to start the dream. + private static final ComponentName SOMNAMBULATOR_COMPONENT = + new ComponentName("com.android.systemui", "com.android.systemui.Somnambulator"); + + + // The sandman is eternal. No one instantiates him. + private Sandman() { + } + + /** + * Returns true if the specified dock app intent should be started. + * False if we should dream instead, if appropriate. + */ + public static boolean shouldStartDockApp(Context context, Intent intent) { + ComponentName name = intent.resolveActivity(context.getPackageManager()); + return name != null && !name.equals(SOMNAMBULATOR_COMPONENT); + } + + /** + * Starts a dream manually. + */ + public static void startDreamByUserRequest(Context context) { + startDream(context, false); + } + + /** + * Starts a dream when docked if the system has been configured to do so, + * otherwise does nothing. + */ + public static void startDreamWhenDockedIfAppropriate(Context context) { + if (!isScreenSaverEnabled(context) + || !isScreenSaverActivatedOnDock(context)) { + Slog.i(TAG, "Dreams currently disabled for docks."); + return; + } + + startDream(context, true); + } + + private static void startDream(Context context, boolean docked) { + try { + IDreamManager dreamManagerService = IDreamManager.Stub.asInterface( + ServiceManager.getService(DreamService.DREAM_SERVICE)); + if (dreamManagerService != null && !dreamManagerService.isDreaming()) { + if (docked) { + Slog.i(TAG, "Activating dream while docked."); + + // Wake up. + // The power manager will wake up the system automatically when it starts + // receiving power from a dock but there is a race between that happening + // and the UI mode manager starting a dream. We want the system to already + // be awake by the time this happens. Otherwise the dream may not start. + PowerManager powerManager = + (PowerManager)context.getSystemService(Context.POWER_SERVICE); + powerManager.wakeUp(SystemClock.uptimeMillis()); + } else { + Slog.i(TAG, "Activating dream by user request."); + } + + // Dream. + dreamManagerService.dream(); + } + } catch (RemoteException ex) { + Slog.e(TAG, "Could not start dream when docked.", ex); + } + } + + private static boolean isScreenSaverEnabled(Context context) { + return Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED, + UserHandle.USER_CURRENT) != 0; + } + + private static boolean isScreenSaverActivatedOnDock(Context context) { + return Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, + DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK, UserHandle.USER_CURRENT) != 0; + } +} diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 6436059..0661992 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4817,6 +4817,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); + final int layoutDirection = getLayoutDirection(); + lp.resolveLayoutDirection(layoutDirection); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java index d6576e1..357a16e 100644 --- a/core/java/android/webkit/AccessibilityInjector.java +++ b/core/java/android/webkit/AccessibilityInjector.java @@ -96,11 +96,26 @@ class AccessibilityInjector { // Template for JavaScript that performs AndroidVox actions. private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE = - "cvox.AndroidVox.performAction('%1s')"; + "(function() {" + + " if ((typeof(cvox) != 'undefined')"+ + " && (typeof(cvox.ChromeVox) != 'undefined')" + + " && (typeof(cvox.AndroidVox) != 'undefined')" + + " && cvox.ChromeVox.isActive) {" + + " return cvox.AndroidVox.performAction('%1s');" + + " } else {" + + " return false;" + + " }" + + "})()"; // JS code used to shut down an active AndroidVox instance. private static final String TOGGLE_CVOX_TEMPLATE = - "javascript:(function() { cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b); })();"; + "javascript:(function() {" + + " if ((typeof(cvox) != 'undefined')"+ + " && (typeof(cvox.ChromeVox) != 'undefined')" + + " && (typeof(cvox.ChromeVox.host) != 'undefined')) {" + + " cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b);" + + " }" + + "})();"; /** * Creates an instance of the AccessibilityInjector based on @@ -776,20 +791,26 @@ class AccessibilityInjector { while (true) { try { if (mResultId == resultId) { + if (DEBUG) + Log.w(TAG, "Received CVOX result"); return true; } if (mResultId > resultId) { + if (DEBUG) + Log.w(TAG, "Obsolete CVOX result"); return false; } final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; waitTimeMillis = RESULT_TIMEOUT - elapsedTimeMillis; if (waitTimeMillis <= 0) { + if (DEBUG) + Log.w(TAG, "Timed out while waiting for CVOX result"); return false; } mResultLock.wait(waitTimeMillis); } catch (InterruptedException ie) { if (DEBUG) - Log.w(TAG, "Timed out while waiting for CVOX result"); + Log.w(TAG, "Interrupted while waiting for CVOX result"); /* ignore */ } } @@ -805,6 +826,8 @@ class AccessibilityInjector { @JavascriptInterface @SuppressWarnings("unused") public void onResult(String id, String result) { + if (DEBUG) + Log.w(TAG, "Saw CVOX result of '" + result + "'"); final long resultId; try { diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 4202a7f..6df7820 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1480,7 +1480,6 @@ public class WebView extends AbsoluteLayout * * @param listener an implementation of WebView.PictureListener * @deprecated This method is now obsolete. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} */ @Deprecated public void setPictureListener(PictureListener listener) { diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 00cd604..45f30df 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -304,11 +304,16 @@ public class FrameLayout extends ViewGroup { int maxWidth = 0; int childState = 0; + final int layoutDirection = getLayoutDirection(); + for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() != GONE) { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); + // measureChildWithMargins() has triggered layout params resolution, so no need + // to do it now final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); maxHeight = Math.max(maxHeight, diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 4ca405b..ace26f3 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -414,12 +414,15 @@ public class RelativeLayout extends ViewGroup { final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY; final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY; + final int layoutDirection = getLayoutDirection(); + View[] views = mSortedHorizontalChildren; int count = views.length; for (int i = 0; i < count; i++) { View child = views[i]; if (child.getVisibility() != GONE) { LayoutParams params = (LayoutParams) child.getLayoutParams(); + params.resolveLayoutDirection(layoutDirection); applyHorizontalSizeRules(params, myWidth); measureChildHorizontal(child, params, myWidth, myHeight); @@ -483,8 +486,6 @@ public class RelativeLayout extends ViewGroup { } } - final int layoutDirection = getLayoutDirection(); - if (isWrapContentWidth) { // Width already has left padding in it since it was calculated by looking at // the right of each child view diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index bc41931..1def89f 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -329,11 +329,13 @@ public class ScrollView extends FrameLayout { return; } + final int layoutDirection = getLayoutDirection(); if (getChildCount() > 0) { final View child = getChildAt(0); int height = getMeasuredHeight(); if (child.getMeasuredHeight() < height) { final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); + lp.resolveLayoutDirection(layoutDirection); int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); diff --git a/core/java/android/widget/SimpleExpandableListAdapter.java b/core/java/android/widget/SimpleExpandableListAdapter.java index f514374..015c169 100644 --- a/core/java/android/widget/SimpleExpandableListAdapter.java +++ b/core/java/android/widget/SimpleExpandableListAdapter.java @@ -38,8 +38,6 @@ import java.util.Map; */ public class SimpleExpandableListAdapter extends BaseExpandableListAdapter { private List<? extends Map<String, ?>> mGroupData; - // Keeps track of if a group is currently expanded or not - private boolean[] mIsGroupExpanded; private int mExpandedGroupLayout; private int mCollapsedGroupLayout; private String[] mGroupFrom; @@ -198,8 +196,6 @@ public class SimpleExpandableListAdapter extends BaseExpandableListAdapter { int childLayout, int lastChildLayout, String[] childFrom, int[] childTo) { mGroupData = groupData; - // Initially all groups are not expanded - mIsGroupExpanded = new boolean[groupData.size()]; mExpandedGroupLayout = expandedGroupLayout; mCollapsedGroupLayout = collapsedGroupLayout; mGroupFrom = groupFrom; @@ -302,52 +298,4 @@ public class SimpleExpandableListAdapter extends BaseExpandableListAdapter { return true; } - /** - * {@inheritDoc} - * @return 1 for the last child in a group, 0 for the other children. - */ - @Override - public int getChildType(int groupPosition, int childPosition) { - final int childrenInGroup = getChildrenCount(groupPosition); - return childPosition == childrenInGroup - 1 ? 1 : 0; - } - - /** - * {@inheritDoc} - * @return 2, one type for the last child in a group, one for the other children. - */ - @Override - public int getChildTypeCount() { - return 2; - } - - /** - * {@inheritDoc} - * @return 1 for an expanded group view, 0 for a collapsed one. - */ - @Override - public int getGroupType(int groupPosition) { - return mIsGroupExpanded[groupPosition] ? 1 : 0; - } - - /** - * {@inheritDoc} - * @return 2, one for a collapsed group view, one for an expanded one. - */ - @Override - public int getGroupTypeCount() { - return 2; - } - - /** {@inheritDoc} */ - @Override - public void onGroupCollapsed(int groupPosition) { - mIsGroupExpanded[groupPosition] = false; - } - - /** {@inheritDoc} */ - @Override - public void onGroupExpanded(int groupPosition) { - mIsGroupExpanded[groupPosition] = true; - } } diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java index db3853f..3f8f9dae 100644 --- a/core/java/android/widget/TableRow.java +++ b/core/java/android/widget/TableRow.java @@ -192,7 +192,9 @@ public class TableRow extends LinearLayout { int widthMeasureSpec, int totalWidth, int heightMeasureSpec, int totalHeight) { if (mConstrainedColumnWidths != null) { + final int layoutDirection = getLayoutDirection(); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + lp.resolveLayoutDirection(layoutDirection); int measureMode = MeasureSpec.EXACTLY; int columnWidth = 0; @@ -226,7 +228,6 @@ public class TableRow extends LinearLayout { final int childWidth = child.getMeasuredWidth(); lp.mOffset[LayoutParams.LOCATION_NEXT] = columnWidth - childWidth; - final int layoutDirection = getLayoutDirection(); final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.LEFT: @@ -292,11 +293,13 @@ public class TableRow extends LinearLayout { } final int[] columnWidths = mColumnWidths; + final int layoutDirection = getLayoutDirection(); for (int i = 0; i < numColumns; i++) { final View child = getVirtualChildAt(i); if (child != null && child.getVisibility() != GONE) { final LayoutParams layoutParams = (LayoutParams) child.getLayoutParams(); + layoutParams.resolveLayoutDirection(layoutDirection); if (layoutParams.span == 1) { int spec; switch (layoutParams.width) { diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index 8bc1081..43c63b6 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -343,9 +343,11 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi final int height = maxHeight - verticalPadding; final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); + final int layoutDirection = getLayoutDirection(); if (mClose != null) { availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0); MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams(); + lp.resolveLayoutDirection(layoutDirection); availableWidth -= lp.leftMargin + lp.rightMargin; } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 5a7f10c..d8b3d2f 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -946,6 +946,9 @@ public class ActionBarView extends AbsActionBarView { final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? (ActionBar.LayoutParams) lp : null; + final int layoutDirection = getLayoutDirection(); + lp.resolveLayoutDirection(layoutDirection); + int horizontalMargin = 0; int verticalMargin = 0; if (ablp != null) { @@ -1096,9 +1099,9 @@ public class ActionBarView extends AbsActionBarView { customView = mCustomNavView; } if (customView != null) { - final int resolvedLayoutDirection = getLayoutDirection(); ViewGroup.LayoutParams lp = customView.getLayoutParams(); - lp.resolveLayoutDirection(resolvedLayoutDirection); + final int layoutDirection = getLayoutDirection(); + lp.resolveLayoutDirection(layoutDirection); final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? (ActionBar.LayoutParams) lp : null; final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY; @@ -1139,7 +1142,7 @@ public class ActionBarView extends AbsActionBarView { } int xpos = 0; - switch (Gravity.getAbsoluteGravity(hgravity, resolvedLayoutDirection)) { + switch (Gravity.getAbsoluteGravity(hgravity, layoutDirection)) { case Gravity.CENTER_HORIZONTAL: xpos = ((mRight - mLeft) - navWidth) / 2; break; @@ -1336,11 +1339,15 @@ public class ActionBarView extends AbsActionBarView { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0); + // measureChildWithMargins() has triggered layout params resolution, so no need + // to do it now final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); mUpWidth = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin; int width = mUpView.getVisibility() == GONE ? 0 : mUpWidth; int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin; measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0); + // measureChildWithMargins() has triggered layout params resolution, so no need + // to do it now final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin; height = Math.max(height, diff --git a/core/jni/android_os_UEventObserver.cpp b/core/jni/android_os_UEventObserver.cpp index 7033ff3..3f7c7d2 100644 --- a/core/jni/android_os_UEventObserver.cpp +++ b/core/jni/android_os_UEventObserver.cpp @@ -49,7 +49,7 @@ static bool isMatch(const char* buffer, size_t length) { // Consider all zero-delimited fields of the buffer. const char* field = buffer; - const char* end = buffer + length; + const char* end = buffer + length + 1; do { if (strstr(field, match.string())) { ALOGV("Matched uevent message with pattern: %s", match.string()); diff --git a/core/res/res/layout/keyguard_face_unlock_view.xml b/core/res/res/layout/keyguard_face_unlock_view.xml index 60f0492..e25d035 100644 --- a/core/res/res/layout/keyguard_face_unlock_view.xml +++ b/core/res/res/layout/keyguard_face_unlock_view.xml @@ -46,13 +46,14 @@ android:background="@color/facelock_spotlight_mask" /> - <ImageView - android:id="@+id/cancel_button" + <ImageButton + android:id="@+id/face_unlock_cancel_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5dip" android:layout_alignParentTop="true" android:layout_alignParentEnd="true" + android:background="#00000000" android:src="@drawable/ic_facial_backup" /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c48de1f..b79348a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1233,6 +1233,7 @@ <java-symbol type="id" name="eight" /> <java-symbol type="id" name="emergencyCallButton" /> <java-symbol type="id" name="face_unlock_area_view" /> + <java-symbol type="id" name="face_unlock_cancel_button" /> <java-symbol type="id" name="five" /> <java-symbol type="id" name="forgotPatternButton" /> <java-symbol type="id" name="four" /> diff --git a/data/sounds/notifications/ogg/Alya.ogg b/data/sounds/notifications/ogg/Alya.ogg Binary files differnew file mode 100644 index 0000000..f07a0d1 --- /dev/null +++ b/data/sounds/notifications/ogg/Alya.ogg diff --git a/data/sounds/notifications/ogg/Syrma.ogg b/data/sounds/notifications/ogg/Syrma.ogg Binary files differnew file mode 100644 index 0000000..a615441 --- /dev/null +++ b/data/sounds/notifications/ogg/Syrma.ogg diff --git a/data/sounds/notifications/ogg/Talitha.ogg b/data/sounds/notifications/ogg/Talitha.ogg Binary files differnew file mode 100644 index 0000000..75f2fbd --- /dev/null +++ b/data/sounds/notifications/ogg/Talitha.ogg diff --git a/data/sounds/ringtones/ogg/Atria.ogg b/data/sounds/ringtones/ogg/Atria.ogg Binary files differnew file mode 100644 index 0000000..5cbb889 --- /dev/null +++ b/data/sounds/ringtones/ogg/Atria.ogg diff --git a/data/sounds/ringtones/ogg/Kuma.ogg b/data/sounds/ringtones/ogg/Kuma.ogg Binary files differnew file mode 100644 index 0000000..780d5d4 --- /dev/null +++ b/data/sounds/ringtones/ogg/Kuma.ogg diff --git a/data/sounds/ringtones/ogg/Rasalas.ogg b/data/sounds/ringtones/ogg/Rasalas.ogg Binary files differnew file mode 100644 index 0000000..7e62f17 --- /dev/null +++ b/data/sounds/ringtones/ogg/Rasalas.ogg diff --git a/docs/html/guide/topics/ui/notifiers/notifications.jd b/docs/html/guide/topics/ui/notifiers/notifications.jd index 273b5f7..fbff532 100644 --- a/docs/html/guide/topics/ui/notifiers/notifications.jd +++ b/docs/html/guide/topics/ui/notifiers/notifications.jd @@ -1,646 +1,902 @@ -page.title=Status Notifications -parent.title=Notifications -parent.link=index.html +page.title=Notifications @jd:body <div id="qv-wrapper"> - <div id="qv"> - <h2>Quickview</h2> - <ul> - <li>A status notification allows your application to notify the user of an event -without interupting their current activity</li> - <li>You can attach an intent to your notification that the system will initiate when the -user clicks it</li> - </ul> - - <h2>In this document</h2> +<div id="qv"> +<h2>In this document</h2> <ol> - <li><a href="#Basics">The Basics</a></li> - <li><a href="#HandlingNotifications">Responding to Notifications</a></li> - <li><a href="#ManageYourNotifications">Managing your Notifications</a></li> - <li><a href="#CreateANotification">Creating a Notification</a> - <ol> - <li><a href="#Updating">Updating the notification</a></li> - <li><a href="#Sound">Adding a sound</a></li> - <li><a href="#Vibration">Adding vibration</a></li> - <li><a href="#Lights">Adding flashing lights</a></li> - <li><a href="#More">More features</a></li> - </ol> - </li> - <li><a href="#CustomExpandedView">Creating a Custom Notification Layout</a></li> + <li> + <a href="#NotificationUI">Notification Display Elements</a> + </li> + <li> + <a href="#CreateNotification">Creating a Notification</a> + </li> + <li> + <a href="#Managing">Managing Notifications</a> + </li> + <li> + <a href="#NotificationResponse">Preserving Navigation when Starting an Activity</a> + </li> + <li> + <a href="#Progress">Displaying Progress in a Notification</a> + </li> + <li> + <a href="#CustomNotification">Custom Notification Layouts</a> + </li> </ol> <h2>Key classes</h2> <ol> - <li>{@link android.app.Notification}</li> - <li>{@link android.app.NotificationManager}</li> + <li>{@link android.app.NotificationManager}</li> + <li>{@link android.support.v4.app.NotificationCompat}</li> </ol> - - <h2>See also</h2> + <h2>Videos</h2> <ol> - <li><a href="{@docRoot}design/patterns/notifications.html">Android -Design: Notifications</a></li> + <li> + <a href="http://www.youtube.com/watch?v=Yc8YrVc47TI&feature=player_detailpage#t=1672s"> + Notifications in 4.1</a> + </li> </ol> - </div> +<h2>See also</h2> +<ol> + <li> + <a href="{@docRoot}design/patterns/notifications.html">Android Design: Notifications</a> + </li> +</ol> </div> - -<p>A status notification adds an icon to the system's status bar -(with an optional ticker-text message) and a notification message in the notifications window. -When the user selects the notification, Android fires an -{@link android.content.Intent} that is defined by the {@link android.app.Notification} (usually to -launch an {@link android.app.Activity}). -You can also configure the notification to alert the user with a sound, a vibration, and flashing -lights on the device.</p> - -<p>A status notification should be used for any case in -which a background service needs to alert the user about an event that requires a response. A -background service -<strong>should never</strong> launch an activity on its own in order to receive user interaction. -The service should instead create a status notification that will launch the activity -when selected by the user.</p> - -<p>Figure 1 shows the status bar with a notification icon on the left side.</p> -<img src="{@docRoot}images/status_bar.png" alt="" /> -<p class="img-caption"><strong>Figure 1.</strong> Status bar with a notification.</p> - -<p>Figure 2 shows the notification's message in the notifications window.</p> - -<img src="{@docRoot}images/notifications_window.png" alt="" /> -<p class="img-caption"><strong>Figure 2.</strong> The notifications window.</p> - - +</div> +<p> + A notification is a message you can display to the user outside of your application's + normal UI. When you tell the system to issue a notification, it first appears as an icon in the + <strong>notification area</strong>. To see the details of the notification, the user opens the + <strong>notification drawer</strong>. Both the notification area and the notification drawer + are system-controlled areas that the user can view at any time. +</p> +<img + id="figure1" + src="{@docRoot}images/ui/notifications/iconic_notification.png" + height="32" + alt="" /> +<p class="img-caption"> + <strong>Figure 1.</strong> Notifications in the notification area. +</p> +<img id="figure2" src="{@docRoot}images/ui/notifications/normal_notification.png" + height="240" alt="" /> +<p class="img-caption"> + <strong>Figure 2.</strong> Notifications in the notification drawer. +</p> <div class="note design"> -<p><strong>Notification Design</strong></p> - <p>For design guidelines, read Android Design's <a -href="{@docRoot}design/patterns/notifications.html">Notifications</a> guide.</p> + <p> + <strong>Notification Design</strong> + </p> + <p> + Notifications, as an important part of the Android UI, have their own design guidelines. To + learn how to design notifications and their interactions, read the Android Design Guide + <a href="{@docRoot}design/patterns/notifications.html">Notifications</a> topic. + </p> </div> - - - -<h2 id="Basics">The Basics</h2> - -<p>An {@link android.app.Activity} or {@link android.app.Service} can initiate a status -notification. Because an activity can perform actions only while it is -running in the foreground and its window has focus, you will usually create status notifications -from a -service. This way, the notification can be created from the background, -while the user is using another application or -while the device is asleep. To create a notification, you must use two -classes: {@link android.app.Notification} and {@link android.app.NotificationManager}.</p> - -<p>Use an instance of the {@link android.app.Notification} class to define the properties of your -status notification, such as the status icon, the notification message, and extra settings -such as a sound to play. The {@link android.app.NotificationManager} is an Android system service -that executes and manages all status notifications. You do not instantiate the -{@link android.app.NotificationManager} directly. In order -to give it your {@link android.app.Notification}, you must retrieve a reference to the -{@link android.app.NotificationManager} with -{@link android.app.Activity#getSystemService(String) getSystemService()} and -then, when you want to notify the user, pass it your {@link android.app.Notification} with -{@link android.app.NotificationManager#notify(int,Notification) notify()}. </p> - -<p>To create a status notification:</p> -<ol> - <li>Get a reference to the {@link android.app.NotificationManager}: -<pre> -String ns = Context.NOTIFICATION_SERVICE; -NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns); -</pre> - </li> - <!-- use Notification.Builder in 3.0 --> - <li>Instantiate the {@link android.app.Notification}: -<pre> -int icon = R.drawable.notification_icon; -CharSequence tickerText = "Hello"; -long when = System.currentTimeMillis(); - -Notification notification = new Notification(icon, tickerText, when); -</pre> - </li> - <li>Define the notification's message and {@link android.app.PendingIntent}: -<pre> -Context context = getApplicationContext(); -CharSequence contentTitle = "My notification"; -CharSequence contentText = "Hello World!"; -Intent notificationIntent = new Intent(this, MyClass.class); -PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); - -notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent); -</pre> - </li> - <li>Pass the {@link android.app.Notification} to the {@link android.app.NotificationManager}: -<pre> -private static final int HELLO_ID = 1; - -mNotificationManager.notify(HELLO_ID, notification); -</pre> - <p>That's it. Your user has now been notified.</p> - </li> -</ol> - - -<h2 id="HandlingNotifications">Responding to Notifications</h2> - -<p>A central part of the user's experience with a notification revolves around -how it interacts with the application's UI flow. You must implement -this correctly to provide a consistent user experience within your app.</p> - -<p>Two typical examples of notifications are provided by Calendar, which can send out -notifications of upcoming events, and Email, which can send out notifications -when new messages arrive. These represent the two recommended patterns for handling -notifications: either launching into an activity that is separate from the -main application, or launching an entirely new instance of the application -showing the appropriate point for the notification.</p> - -<p>The following scenario shows how the activity stack should work -in these two typical notification flows, first handling a Calendar notification: +<p class="note"> + <strong>Note:</strong> This guide refers to the + {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} class + in the version 4 <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>. + The class {@link android.app.Notification.Builder Notification.Builder} was added in API + level 11. +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<!-- ------------------------------------------------------------------------------------------ --> +<h2 id="NotificationUI">Notification Display Elements</h2> +<p> + Notifications in the notification drawer appear in two main visual styles, normal view and + big view. +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="NormalNotify">Normal view</h3> +<p> + A notification in normal view appears in an area that's up to 64 dp tall. Even if you create a + notification with a big view style, it will appear in normal view until it's expanded. This + is an example of a normal view: +</p> +<img + src="{@docRoot}images/ui/notifications/normal_notification_callouts.png" + alt="" + height="204" + id="figure3" /> +<p class="img-caption"> + <strong>Figure 3.</strong> Notification in normal view. +</p> +<p> + The callouts in the illustration refer to the following: </p> - -<ol> - <li>User is creating a new event in Calendar. They realize they - need to copy part of an email message into this event. - </li> - <li> - The user chooses Home > Email. - </li> - <li> - While in Email, they receive a notification from Calendar for an upcoming - meeting. - </li> - <li> - So they choose that notification, which takes them to a - dedicated Calendar activity that displays brief details of the - upcoming meeting. - </li> - <li> - The user has seen enough to know they have a meeting coming up, - so they press the <em>Back</em> button. They are now returned to Email, which - is where they were when they took the notification. - </li> -</ol> - -<p>Handling an Email notification:</p> - <ol> - <li> - The user is currently in Email composing a message, and needs to - check a date in their calendar. - </li> - <li> - The user chooses Home > Calendar. - </li> - <li> - While in Calendar, they receive a notification from Email about a new - message. - </li> - <li> - They select the notification, which brings them to Email with the message - details displayed. This has replaced what they were previously doing - (writing an e-mail), but that message is still saved in their drafts. - </li> - <li> - The user presses <em>Back</em> once to go to the message list (the typical flow in the - Email app), and press <em>Back</em> again to return to Calendar as they left it. - </li> + <li>Content title</li> + <li>Large icon</li> + <li>Content text</li> + <li>Content info</li> + <li>Small icon</li> + <li> + Time that the notification was issued. You can set an explicit value with + {@link android.support.v4.app.NotificationCompat.Builder#setWhen setWhen()}; if you don't + it defaults to the time that the system received the notification. + </li> </ol> - -<p>In an Email style of notification, the UI launched by the notification -shows the main application in a state representing that notification. -For example, when the Email application comes to the foreground from its -notification, it displays either the conversion list or a specific -conversation depending on whether there are multiple or only one new -email. To achieve this, we want to completely replace whatever current -state the application is in with a new activity stack representing the -new notification state.</p> - -<p>The following code illustrates how to show this kind of notification. Of -most interest is the <code>makeMessageIntentStack()</code> method, which constructs -an array of intents representing the app's new activity stack for this state. -(If you are using fragments, you may need to initialize your fragment and -app state so that pressing <em>Back</em> will switch the UI back to its parent state.) -The core of this is the {@link android.content.Intent#makeRestartActivityTask -Intent.makeRestartActivityTask()} method, which constructs the root activity -of the stack with the appropriate flags, such as -{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK Intent.FLAG_ACTIVITY_CLEAR_TASK}.</p> - -{@sample development/samples/ApiDemos/src/com/example/android/apis/app/IncomingMessage.java - app_notification} - -<p>In a Calendar style of notification, the UI launched by the notification -is a dedicated activity that is not part of the normal application flow. -For example, when the user receives a Calendar notification, choosing that -notification starts a special activity that displays a list -of upcoming calendar events — this view is available only -from the notification, not through the Calendar's normal user -interface.</p> - -<p>The code for posting this type of notification is very straight-forward; it -is like the above, but the {@link android.app.PendingIntent} is for just a single -activity, our dedicated notification activity.</p> - -{@sample development/samples/ApiDemos/src/com/example/android/apis/app/IncomingMessage.java - interstitial_notification} - -<p>This is not enough, however. Normally Android considers all activities within -an application to be part of that application's UI flow, so simply launching the -activity like this can cause it to be mixed with your normal application back stack -in undesired ways. To make it behave correctly, in the manifest declaration -for the activity the attributes -<code>android:launchMode="singleTask"</code>, -<code>android:taskAffinity=""</code> and -<code>android:excludeFromRecents="true"</code> -must be set. The full activity declaration for this sample is:</p> - -{@sample development/samples/ApiDemos/AndroidManifest.xml interstitial_affinity} - -<p>You must be careful when launching other activities from this initial activity, -because this is not a top-level part of the application, does not appear in -recents, and needs to be relaunched at any point from the notification with new data -to show. This best approach is to make sure any activity launched from it is -launched in its own task. When doing this care must be taken to make sure this -new task interacts well with the current state of your exiting application's -task. This is essentially -the same as switching to the main application as described for the Email style -notification shown before. Given the <code>makeMessageIntentStack()</code> -method previously shown, handling a click then would look something like this:</p> - -{@sample development/samples/ApiDemos/src/com/example/android/apis/app/IncomingMessageInterstitial.java - app_launch} - -<h2 id="ManageYourNotifications">Managing your Notifications</h2> - -<p>The {@link android.app.NotificationManager} is a system service that manages all -notifications. You must retrieve a reference to it with the -{@link android.app.Activity#getSystemService(String) getSystemService()} method. -For example:</p> -<pre> -String ns = Context.NOTIFICATION_SERVICE; -NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns); -</pre> - -<p>When you want to deliver your status notification, pass the {@link android.app.Notification} -to the {@link android.app.NotificationManager} with {@link -android.app.NotificationManager#notify(int,Notification)}. -The first parameter is the unique ID for the notification and the second is the {@link -android.app.Notification} object. -The ID uniquely identifies the notification from within your -application. The ID is necessary if you need to update the notification or (if -your application manages different kinds of notifications) select the appropriate action -when the user returns to your application via the intent defined in the notification.</p> - -<p>To clear the status notification when the user selects it from the notifications -window, add the "FLAG_AUTO_CANCEL" flag to your {@link android.app.Notification}. You can -also clear it manually with {@link android.app.NotificationManager#cancel(int)}, passing it the -notification ID, or clear all your notifications with {@link -android.app.NotificationManager#cancelAll()}.</p> - - -<h2 id="CreateANotification">Creating a Notification</h2> - -<p>A {@link android.app.Notification} object defines the details of the notification -message that is displayed in the status bar and notifications window, and any other -alert settings, such as sounds and blinking lights.</p> - -<p>A status notification <em>requires</em> all of the following:</p> -<ul> - <li>An icon for the status bar</li> - <li>A title and message, unless you define a - <a href="#CustomExpandedView">custom notification layout</a></li> - <li>A {@link android.app.PendingIntent}, to be fired when the notification is selected</li> -</ul> -<p>Optional settings for the status notification include:</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="BigNotify">Big view</h3> +<p> + A notification's big view appears only when the notification is expanded, which happens when the + notification is at the top of the notification drawer, or when the user expands the + notification with a gesture. +</p> +<p> + The following screenshot shows an inbox-style notification: +</p> +<img src="{@docRoot}images/ui/notifications/bigpicture_notification_callouts.png" + alt="" + height="240" + id="figure4" /> +<p class="img-caption"> + <strong>Figure 4.</strong> Big view notification. +</p> +<p> + Notice that the big view shares most of its visual elements with the normal view. The + only difference is callout number 7, the details area. Each big view style sets this area in + a different way. The available styles are: +</p> +<dl> + <dt> + Big picture style + </dt> + <dd> + The details area contains a bitmap up to 256 dp tall in its detail section. + </dd> + <dt> + Big text style + </dt> + <dd> + Displays a large text block in the details section. + </dd> + <dt> + Inbox style + </dt> + <dd> + Displays lines of text in the details section. + </dd> +</dl> +<p> + All of the big view styles also have the following content options that aren't + available in normal view: +</p> +<dl> + <dt> + Big content title + </dt> + <dd> + Allows you to override the normal view's content title with a title that appears only in + the expanded view. + </dd> + <dt> + Summary text + </dt> + <dd> + Allows you to add a line of text below the details area. + </dd> +</dl> +<p> + Applying a big view style to a notification is described in the section + <a href="#ApplyStyle">Applying a big view style to a notification</a>. +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<!-- ------------------------------------------------------------------------------------------ --> +<h2 id="CreateNotification">Creating a Notification</h2> +<p> + You specify the UI information and actions for a notification in a + {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} object. + To create the notification itself, you call + {@link android.support.v4.app.NotificationCompat.Builder#build + NotificationCompat.Builder.build()}, which returns a {@link android.app.Notification} object + containing your specifications. + To issue the notification, you pass the {@link android.app.Notification} object to the system + by calling {@link android.app.NotificationManager#notify NotificationManager.notify()}. +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="Required">Required notification contents</h3> +<p> + A {@link android.app.Notification} object <em>must</em> contain the following: +</p> <ul> - <li>A ticker-text message for the status bar</li> - <li>An alert sound</li> - <li>A vibrate setting</li> - <li>A flashing LED setting</li> + <li> + A small icon, set by + {@link android.support.v4.app.NotificationCompat.Builder#setSmallIcon setSmallIcon()} + </li> + <li> + A title, set by + {@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()} + </li> + <li> + Detail text, set by + {@link android.support.v4.app.NotificationCompat.Builder#setContentText setContentText()} + </li> </ul> - -<p>The starter-kit for a new notification includes the -{@link android.app.Notification#Notification(int,CharSequence,long)} constructor and the -{@link android.app.Notification#setLatestEventInfo(Context,CharSequence,CharSequence,PendingIntent)} -method. These define all the required settings for a notification. -The following snippet demonstrates a basic notification setup:</p> -<pre> -int icon = R.drawable.notification_icon; // icon from resources -CharSequence tickerText = "Hello"; // ticker-text -long when = System.currentTimeMillis(); // notification time -Context context = getApplicationContext(); // application Context -CharSequence contentTitle = "My notification"; // message title -CharSequence contentText = "Hello World!"; // message text - -Intent notificationIntent = new Intent(this, MyClass.class); -PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); - -// the next two lines initialize the Notification, using the configurations above -Notification notification = new Notification(icon, tickerText, when); -notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent); -</pre> - - -<h3 id="Updating">Updating the notification</h3> - -<p>You can update the information in your status notification as events -continue to occur in your application. For example, when a new SMS text message arrives -before previous messages have been read, the Messaging application updates the existing -notification to display the total number of new messages received. -This practice of updating an existing notification is much better than adding new -notifications, because it avoids clutter in the notifications window.</p> - -<p>Because each notification is uniquely identified -by the {@link android.app.NotificationManager} with an integer ID, you can revise the notification -by calling {@link -android.app.Notification#setLatestEventInfo(Context,CharSequence,CharSequence,PendingIntent) -setLatestEventInfo()} with new values, change some field values of the notification, and then call -{@link android.app.NotificationManager#notify(int,Notification) notify()} again.</p> - -<p>You can revise each property with the object member fields -(except for the {@link android.content.Context} and the notification title and text). You -should always revise the text message when you update the notification by calling -{@link android.app.Notification#setLatestEventInfo(Context,CharSequence,CharSequence,PendingIntent) -setLatestEventInfo()} with new values for <var>contentTitle</var> and <var>contentText</var>. -Then call {@link android.app.NotificationManager#notify(int,Notification) notify()} to update the -notification. (Of course, if you've created a <a href="#CustomExpandedView">custom notification -layout</a>, then updating these title and text values has no effect.)</p> - - -<h3 id="Sound">Adding a sound</h3> - -<p>You can alert the user with the default notification sound -(which is defined by the user) or with a sound specified by your application.</p> - -<p>To use the user's default sound, add "DEFAULT_SOUND" to the <var>defaults</var> field:</p> -<pre> -notification.defaults |= Notification.DEFAULT_SOUND; -</pre> - -<p>To use a different sound with your notifications, pass a Uri reference to the -<var>sound</var> field. -The following example uses a known audio file saved to the device SD card:</p> -<pre> -notification.sound = Uri.parse("file:///sdcard/notification/ringer.mp3"); -</pre> - -<p>In the next example, the audio file is chosen from the internal -{@link android.provider.MediaStore.Audio.Media MediaStore}'s {@link android.content.ContentProvider}:</p> +<h3 id="Optional">Optional notification contents and settings</h3> +<p> + All other notification settings and contents are optional. To learn more about them, + see the reference documentation for {@link android.support.v4.app.NotificationCompat.Builder}. +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="Actions">Notification actions</h3> +<p> + Although they're optional, you should add at least one action to your notification. + An action allows users to go directly from the notification to an + {@link android.app.Activity} in your application, where they can look at one or more events + or do further work. +</p> +<p> + A notification can provide multiple actions. You should always define the action that's + triggered when the user touches the notification; usually this action opens an + {@link android.app.Activity} in your application. You can also add buttons to the notification + that perform additional actions such as snoozing an alarm or responding immediately to a text + message. +</p> +<p> + Inside a {@link android.app.Notification}, the action itself is defined by a + {@link android.app.PendingIntent} containing an {@link android.content.Intent} that starts + an {@link android.app.Activity} in your application. To associate the + {@link android.app.PendingIntent} with a gesture, call the appropriate method of + {@link android.support.v4.app.NotificationCompat.Builder}. For example, if you want to start + {@link android.app.Activity} when the user touches the notification text in + the notification drawer, you add the {@link android.app.PendingIntent} by calling + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent setContentIntent()}. +</p> +<p> + Starting an {@link android.app.Activity} when the user touches the notification is the most + common action scenario. You can also start an {@link android.app.Activity} when the user + dismisses an {@link android.app.Activity}, and you can start an {@link android.app.Activity} + from an action button. To learn more, read the reference guide for + {@link android.support.v4.app.NotificationCompat.Builder}. +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="SimpleNotification">Creating a simple notification</h3> +<p> + The following snippet illustrates a simple notification that specifies an activity to open when + the user touches the notification. Notice that the code creates a + {@link android.support.v4.app.TaskStackBuilder} object and uses it to create the + {@link android.app.PendingIntent} for the action. This pattern is explained in more detail + in the section <a href="#NotificationResponse"> + Preserving Navigation when Starting an Activity</a>: +</p> <pre> -notification.sound = Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6"); +NotificationCompat.Builder mBuilder = + new NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.notification_icon) + .setContentTitle("My notification") + .setContentText("Hello World!"); +// Creates an explicit intent for an Activity in your app +Intent resultIntent = new Intent(this, ResultActivity.class); + +// The stack builder object will contain an artificial back stack for the +// started Activity. +// This ensures that navigating backward from the Activity leads out of +// your application to the Home screen. +TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); +// Adds the back stack for the Intent (but not the Intent itself) +stackBuilder.addParentStack(ResultActivity.class); +// Adds the Intent that starts the Activity to the top of the stack +stackBuilder.addNextIntent(resultIntent); +PendingIntent resultPendingIntent = + stackBuilder.getPendingIntent( + 0, + PendingIntent.FLAG_UPDATE_CURRENT + ); +mBuilder.setContentIntent(resultPendingIntent); +NotificationManager mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); +// mId allows you to update the notification later on. +mNotificationManager.notify(mId, mBuilder.build()); </pre> - -<p>In this case, the exact ID of the media file ("6") is known and appended to the content -{@link android.net.Uri}. If you don't know the exact ID, you must query all the -media available in the {@link android.provider.MediaStore} with a {@link -android.content.ContentResolver}. -See the <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> -documentation for more information on using a ContentResolver.</p> - -<p>If you want the sound to continuously repeat until the user responds to the notification -or the notification is cancelled, add {@link android.app.Notification#FLAG_INSISTENT} to the -<var>flags</var> field.</p> - -<p class="note"><strong>Note:</strong> If the <var>defaults</var> field includes -{@link android.app.Notification#DEFAULT_SOUND}, then the default sound overrides any sound defined -by the <var>sound</var> field.</p> - - -<h3 id="Vibration">Adding vibration</h3> - -<p>You can alert the user with the the default -vibration pattern or with a vibration pattern defined by your application.</p> - -<p>To use the default pattern, add {@link android.app.Notification#DEFAULT_VIBRATE} to the -<var>defaults</var> field:</p> +<p>That's it. Your user has now been notified.</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="ApplyStyle">Applying a big view style to a notification</h3> +<p> + To have a notification appear in a big view when it's expanded, first create a + {@link android.support.v4.app.NotificationCompat.Builder} object with the normal view options + you want. Next, call {@link android.support.v4.app.NotificationCompat.Builder#setStyle + Builder.setStyle()} with a big view style object as its argument. +</p> +<p> + For example, the following code snippet demonstrates how to alter the notification created + in the previous snippet to use the Inbox big view style: +</p> <pre> -notification.defaults |= Notification.DEFAULT_VIBRATE; +NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.notification_icon) + .setContentTitle("Event tracker") + .setContentText("Events received") +NotificationCompat.InboxStyle inboxStyle = + new NotificationCompat.InboxStyle(); +String[] events = new String[6]; +// Sets a title for the Inbox style big view +inboxStyle.SetBigContentTitle("Event tracker details:"); +... +// Moves events into the big view +for (int i=0; i < events.length; i++) { + + inboxStyle.addLine(events[i]); +} +// Moves the big view style object into the notification object. +mBuilder.setStyle(inBoxStyle); +... +// Issue the notification here. </pre> - -<p>To define your own vibration pattern, pass an array of <em>long</em> values to the -<var>vibrate</var> field:</p> +<!-- ------------------------------------------------------------------------------------------ --> +<!-- ------------------------------------------------------------------------------------------ --> +<h2 id="Managing">Managing Notifications</h2> +<p> + When you need to issue a notification multiple times for the same type of event, you + should avoid making a completely new notification. Instead, you should consider updating a + previous notification, either by changing some of its values or by adding to it, or both. +</p> +<p> + For example, Gmail notifies the user that new emails have arrived by increasing its count of + unread messages and by adding a summary of each email to the notification. This is called + "stacking" the notification; it's described in more detail in the + <a href="{@docRoot}design/patterns/notifications.html">Notifications</a> Design guide. +</p> +<p> + The following section describes how to update notifications and also how to remove them. +</p> +<h3 id="Updating">Updating notifications</h3> +<p> + To set up a notification so it can be updated, issue it with a notification ID by + calling {@link android.app.NotificationManager#notify(int, Notification) + NotificationManager.notify(ID, notification)}. To update this notification once you've issued + it, update or create a {@link android.support.v4.app.NotificationCompat.Builder} object, + build a {@link android.app.Notification} object from it, and issue the + {@link android.app.Notification} with the same ID you used previously. If + the previous notification is still visible, the system updates it from the contents of + the {@link android.app.Notification} object. If the previous notification has been dismissed, a + new notification is created instead. +</p> +<p> + The following snippet demonstrates a notification that is updated to reflect the + number of events that have occurred. It stacks the notification, showing a summary: +</p> <pre> -long[] vibrate = {0,100,200,300}; -notification.vibrate = vibrate; +mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); +// Sets an ID for the notification, so it can be updated +int notifyID = 1; +mNotifyBuilder = new NotificationCompat.Builder(this) + .setContentTitle("New Message") + .setContentText("You've received new messages.") + .setSmallIcon(R.drawable.ic_notify_status) +numMessages = 0; +// Start of a loop that processes data and then notifies the user +... + mNotifyBuilder.setContentText(currentText) + .setNumber(++numMessages); + // Because the ID remains unchanged, the existing notification is + // updated. + mNotificationManager.notify( + notifyID, + mNotifyBuilder.build()); +... </pre> - -<p>The long array defines the alternating pattern for the length of vibration off and on -(in milliseconds). The first value is how long to wait (off) before beginning, the second -value is the length of the first vibration, the third is the next length off, and so on. -The pattern can be as long as you like, but it can't be set to repeat. +<p> + This produces a notification that looks like this: </p> - -<p class="note"><strong>Note:</strong> If the <var>defaults</var> field includes -{@link android.app.Notification#DEFAULT_VIBRATE}, then the default vibration overrides any vibration -defined by the -<var>vibrate</var> field.</p> - - -<h3 id="Lights">Adding flashing lights</h3> - -<p>To alert the user by flashing LED lights, you can implement the default -light pattern (if available), or define your own color and pattern for the lights.</p> - -<p>To use the default light setting, add {@link android.app.Notification#DEFAULT_LIGHTS} to the -<var>defaults</var> field:</p> +<img + id="figure5" + src="{@docRoot}images/ui/notifications/updated_notification.png" + alt="" + height="118"/> +<p class="img-caption"> + <strong>Figure 5.</strong> Updated notification displayed in the notification drawer. +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="Removing">Removing notifications</h3> +<p> + Notifications remain visible until one of the following happens: +</p> +<ul> + <li> + The user dismisses the notification either individually or by using "Clear All" (if + the notification can be cleared). + </li> + <li> + The user touches the notification, and you called + {@link android.support.v4.app.NotificationCompat.Builder#setAutoCancel setAutoCancel()} when + you created the notification. + </li> + <li> + You call {@link android.app.NotificationManager#cancel(int) cancel()} for a specific + notification ID. This method also deletes ongoing notifications. + </li> + <li> + You call {@link android.app.NotificationManager#cancelAll() cancelAll()}, which removes + all of the notifications you previously issued. + </li> +</ul> +<!-- ------------------------------------------------------------------------------------------ --> +<!-- ------------------------------------------------------------------------------------------ --> +<h2 id="NotificationResponse">Preserving Navigation when Starting an Activity</h2> +<p> + When you start an {@link android.app.Activity} from a notification, you must preserve the + user's expected navigation experience. Clicking <i>Back</i> should take the user back through + the application's normal work flow to the Home screen, and clicking <i>Recents</i> should show + the {@link android.app.Activity} as a separate task. To preserve the navigation experience, you + should start the {@link android.app.Activity} in a fresh task. How you set up the + {@link android.app.PendingIntent} to give you a fresh task depends on the nature of the + {@link android.app.Activity} you're starting. There are two general situations: +</p> +<dl> + <dt> + Regular activity + </dt> + <dd> + You're starting an {@link android.app.Activity} that's part of the application's normal + workflow. In this situation, set up the {@link android.app.PendingIntent} to + start a fresh task, and provide the {@link android.app.PendingIntent} with a back stack + that reproduces the application's normal <i>Back</i> behavior. + <p> + Notifications from the Gmail app demonstrate this. When you touch a notification for + a single email message, you see the message itself. Touching <b>Back</b> takes you + backwards through Gmail to the Home screen, just as if you had entered Gmail from the + Home screen rather than entering it from a notification. + </p> + <p> + This happens regardless of the application you were in when you touched the + notification. For example, if you're in Gmail composing a message, and you click a + notification for a single email, you go immediately to that email. Touching <i>Back</i> + takes you to the inbox and then the Home screen, rather than taking you to the + message you were composing. + </p> + </dd> + <dt> + Special activity + </dt> + <dd> + The user only sees this {@link android.app.Activity} if it's started from a notification. + In a sense, the {@link android.app.Activity} extends the notification by providing + information that would be hard to display in the notification itself. For this situation, + set up the {@link android.app.PendingIntent} to start in a fresh task. There's no need to + create a back stack, though, because the started {@link android.app.Activity} isn't part of + the application's activity flow. Clicking <i>Back</i> will still take the user to the + Home screen. + </dd> +</dl> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="DirectEntry">Setting up a regular activity PendingIntent</h3> +<p> + To set up a {@link android.app.PendingIntent} that starts a direct entry + {@link android.app.Activity}, follow these steps: +</p> +<ol> + <li> + Define your application's {@link android.app.Activity} hierarchy in the manifest. + <ol style="list-style-type: lower-alpha;"> + <li> + Add support for API versions 15 and earlier. To do this, specify the parent of the + {@link android.app.Activity} you're starting by adding a +<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data></a></code> + element as the child of the +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code>. + <p> + For this element, set +<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#nm">android:name</a>="android.support.PARENT_ACTIVITY"</code>. + Set +<code><a href="{@docRoot}/guide/topics/manifest/meta-data-element.html#val">android:value</a>="<parent_activity_name>"</code> + where <code><parent_activity_name></code> is the value of +<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#nm">android:name</a></code> + for the parent +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + element. See the following XML for an example. + </p> + </li> + <li> + Also add support for API versions 16 and later. To do this, add the +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#parent">android:parentActivityName</a></code> + attribute to the +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + element of the {@link android.app.Activity} you're starting. + </li> + </ol> + <p> + The final XML should look like this: + </p> <pre> -notification.defaults |= Notification.DEFAULT_LIGHTS; +<activity + android:name=".MainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> +<activity + android:name=".ResultActivity" + android:parentActivityName=".MainActivity"> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".MainActivity"/> +</activity> </pre> - -<p>To define your own color and pattern, define a value for the <var>ledARGB</var> field -(for the color), the <var>ledOffMS</var> field (length of time, in milliseconds, to -keep the light off), the <var>ledOnMS</var> (length of time, in milliseconds, to keep the light on), -and also add {@link android.app.Notification#FLAG_SHOW_LIGHTS} to the <var>flags</var> field:</p> + </li> + <li> + Create a back stack based on the {@link android.content.Intent} that starts the + {@link android.app.Activity}: + <ol style="list-style-type: lower-alpha;"> + <li> + Create the {@link android.content.Intent} to start the {@link android.app.Activity}. + </li> + <li> + Create a stack builder by calling {@link android.app.TaskStackBuilder#create + TaskStackBuilder.create()}. + </li> + <li> + Add the back stack to the stack builder by calling + {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()}. + For each {@link android.app.Activity} in the hierarchy you've defined in the + manifest, the back stack contains an {@link android.content.Intent} object that + starts the {@link android.app.Activity}. This method also adds flags that start the + stack in a fresh task. + <p class="note"> + <strong>Note:</strong> Although the argument to + {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} + is a reference to the started {@link android.app.Activity}, the method call + doesn't add the {@link android.content.Intent} that starts the + {@link android.app.Activity}. Instead, that's taken care of in the next step. + </p> + </li> + <li> + Add the {@link android.content.Intent} that starts the {@link android.app.Activity} + from the notification, by calling + {@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()}. + Pass the {@link android.content.Intent} you created in the first step as the + argument to + {@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()}. + </li> + <li> + If you need to, add arguments to {@link android.content.Intent} objects on the + stack by calling {@link android.support.v4.app.TaskStackBuilder#editIntentAt + TaskStackBuilder.editIntentAt()}. This is sometimes necessary to ensure that the + target {@link android.app.Activity} displays meaningful data when the user navigates + to it using <i>Back</i>. + </li> + <li> + Get a {@link android.app.PendingIntent} for this back stack by calling + {@link android.support.v4.app.TaskStackBuilder#getPendingIntent getPendingIntent()}. + You can then use this {@link android.app.PendingIntent} as the argument to + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()}. + </li> + </ol> + </li> +</ol> +<p> + The following code snippet demonstrates the process: +</p> <pre> -notification.ledARGB = 0xff00ff00; -notification.ledOnMS = 300; -notification.ledOffMS = 1000; -notification.flags |= Notification.FLAG_SHOW_LIGHTS; +... +Intent resultIntent = new Intent(this, ResultActivity.class); +TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); +// Adds the back stack +stackBuilder.addParentStack(ResultActivity.class); +// Adds the Intent to the top of the stack +stackBuilder.addNextIntent(resultIntent); +// Gets a PendingIntent containing the entire back stack +PendingIntent resultPendingIntent = + stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); +... +NotificationCompat.Builder builder = new NotificationCompat.Builder(this); +builder.setContentIntent(resultPendingIntent); +NotificationManager mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); +mNotificationManager.notify(id, builder.build()); </pre> - -<p>In this example, the green light repeatedly flashes on for 300 milliseconds and -turns off for one second. Not every color in the spectrum is supported by the -device LEDs, and not every device supports the same colors, so the hardware -estimates to the best of its ability. Green is the most common notification color.</p> - - -<h3 id="More">More features</h3> - -<p>You can add several more features to your notifications -using {@link android.app.Notification} fields and flags. Some useful features include the -following:</p> - -<dl> - <dt>{@link android.app.Notification#FLAG_AUTO_CANCEL} flag</dt> - <dd>Add this to the <var>flags</var> field to automatically cancel the notification - after it is selected from the notifications window.</dd> - <dt>{@link android.app.Notification#FLAG_INSISTENT} flag</dt> - <dd>Add this to the <var>flags</var> field to repeat the audio until the - user responds.</dd> - <dt>{@link android.app.Notification#FLAG_ONGOING_EVENT} flag</dt> - <dd>Add this to the <var>flags</var> field to group the notification under the "Ongoing" - title in the notifications window. This indicates that the application is on-going — - its processes are still running in the background, even when the application is not - visible (such as with music or a phone call).</dd> - <dt>{@link android.app.Notification#FLAG_NO_CLEAR} flag</dt> - <dd>Add this to the <var>flags</var> field to indicate that the notification should - <em>not</em> be cleared by the "Clear notifications" button. This is particularly useful if - your notification is on-going.</dd> - <dt>{@link android.app.Notification#number} field</dt> - <dd>This value indicates the current number of events represented by the notification. - The appropriate number is overlaid on top of the status icon. - If you intend to use this field, then you must start with "1" when the Notification is first - created. (If you change the value from zero to anything greater during an update, the number - is not shown.)</dd> - <dt>{@link android.app.Notification#iconLevel} field</dt> - <dd>This value indicates the current level of a - {@link android.graphics.drawable.LevelListDrawable} that is used for the notification icon. - You can animate the icon in the status bar by changing this value to correlate with the - drawable's defined in a LevelListDrawable. See the {@link android.graphics.drawable.LevelListDrawable} - reference for more information.</dd> -</dl> - -<p>See the {@link android.app.Notification} class reference for more information about additional -features that you can customize for your application.</p> - - -<h2 id="CustomExpandedView">Creating a Custom Notification Layout</h2> - -<div class="figure" style="width:200px;margin-top:0"> -<img src="{@docRoot}images/custom_message.png" alt="" /> -<p class="img-caption"><strong>Figure 3.</strong> Notification with a custom layout.</p> -</div> - -<p>By default, the notification that appears in the notifications window includes a title -and the message text. -These are defined by the <var>contentTitle</var> and <var>contentText</var> -parameters of the {@link android.app.Notification#setLatestEventInfo(Context,CharSequence,CharSequence,PendingIntent) -setLatestEventInfo()} method. However, you can also define a custom layout for the -notification using -{@link android.widget.RemoteViews}. Figure 3 shows an example of a -custom notification layout. It looks similar to the default notification, but is -actually created with a custom XML layout.</p> - -<p>To define your own layout for the notification, -instantiate a {@link android.widget.RemoteViews} object that inflates a custom layout file, then -pass the {@link android.widget.RemoteViews} to the <var>contentView</var> field of your -Notification.</p> - -<p>Creating a custom notification layout is best understood with an example:</p> - +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="ExtendedNotification">Setting up a special activity PendingIntent</h3> +<p> + The following section describes how to set up a special activity + {@link android.app.PendingIntent}. +</p> +<p> + A special {@link android.app.Activity} doesn't need a back stack, so you don't have to + define its {@link android.app.Activity} hierarchy in the manifest, and you don't have + to call + {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} to build a + back stack. Instead, use the manifest to set up the {@link android.app.Activity} task options, + and create the {@link android.app.PendingIntent} by calling + {@link android.app.PendingIntent#getActivity getActivity()}: +</p> <ol> - <li>Create the XML layout for the notification. - For example, the following layout is called <code>custom_notification.xml</code>: + <li> + In your manifest, add the following attributes to the +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + element for the {@link android.app.Activity} + <dl> + <dt> +<code><a href="guide/topics/manifest/activity-element.html#nm">android:name</a>="<i>activityclass</i>"</code> + </dt> + <dd> + The activity's fully-qualified class name. + </dd> + <dt> +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">android:taskAffinity</a>=""</code> + </dt> + <dd> + Combined with the + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} flag + that you set in code, this ensures that this {@link android.app.Activity} doesn't + go into the application's default task. Any existing tasks that have the + application's default affinity are not affected. + </dd> + <dt> +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#exclude">android:excludeFromRecents</a>="true"</code> + </dt> + <dd> + Excludes the new task from <i>Recents</i>, so that the user can't accidentally + navigate back to it. + </dd> + </dl> + <p> + This snippet shows the element: + </p> <pre> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/layout" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:padding="10dp" > - <ImageView android:id="@+id/image" - android:layout_width="wrap_content" - android:layout_height="fill_parent" - android:layout_alignParentLeft="true" - android:layout_marginRight="10dp" /> - <TextView android:id="@+id/title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/image" - style="@style/NotificationTitle" /> - <TextView android:id="@+id/text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/image" - android:layout_below="@id/title" - style="@style/NotificationText" /> -</RelativeLayout> +<activity + android:name=".ResultActivity" +... + android:launchMode="singleTask" + android:taskAffinity="" + android:excludeFromRecents="true"> +</activity> +... </pre> - - <p>Notice that the two {@link android.widget.TextView} elements include the {@code style} -attribute. It's important that you use style resources for the text in your custom -notifications, because the background color of the notification can vary across different -devices and platform versions. Beginning with Android 2.3 (API level 9), the system defines a -style for the text it uses for the default notification layouts. Thus, you should apply -that style when running on Android 2.3 or higher to ensure that your text is visible against -the background.</p> - - <p>For example, to use the standard text colors on versions of Android lower than 2.3, you -should use the following styles for {@code res/values/styles.xml}:</p> + </li> + <li> + Build and issue the notification: + <ol style="list-style-type: lower-alpha;"> + <li> + Create an {@link android.content.Intent} that starts the + {@link android.app.Activity}. + </li> + <li> + Set the {@link android.app.Activity} to start in a new, empty task by calling + {@link android.content.Intent#setFlags setFlags()} with the flags + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} + and + {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK FLAG_ACTIVITY_CLEAR_TASK}. + </li> + <li> + Set any other options you need for the {@link android.content.Intent}. + </li> + <li> + Create a {@link android.app.PendingIntent} from the {@link android.content.Intent} + by calling {@link android.app.PendingIntent#getActivity getActivity()}. + You can then use this {@link android.app.PendingIntent} as the argument to + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()}. + </li> + </ol> + <p> + The following code snippet demonstrates the process: + </p> <pre> -<?xml version="1.0" encoding="utf-8"?> -<resources> - <style name="NotificationText"> - <item name="android:textColor">?android:attr/textColorPrimary</item> - </style> - <style name="NotificationTitle"> - <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:textStyle">bold</item> - </style> - <!-- If you want a slightly different color for some text, - consider using ?android:attr/textColorSecondary --> -</resources> +// Instantiate a Builder object. +NotificationCompat.Builder builder = new NotificationCompat.Builder(this); +// Creates an Intent for the Activity +Intent notifyIntent = + new Intent(new ComponentName(this, ResultActivity.class)); +// Sets the Activity to start in a new, empty task +notifyIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); +// Creates the PendingIntent +PendingIntent notifyIntent = + PendingIntent.getActivity( + this, + 0, + notifyIntent + PendingIntent.FLAG_UPDATE_CURRENT +); + +// Puts the PendingIntent into the notification builder +builder.setContentIntent(notifyIntent); +// Notifications are issued by sending them to the +// NotificationManager system service. +NotificationManager mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); +// Builds an anonymous Notification object from the builder, and +// passes it to the NotificationManager +mNotificationManager.notify(id, builder.build()); </pre> - <p>Then, to apply the system's default colors for notifications on Android -2.3 and higher, use the following styles for {@code res/values-v9/styles.xml}:</p> + </li> +</ol> +<!-- ------------------------------------------------------------------------------------------ --> +<!-- ------------------------------------------------------------------------------------------ --> +<h2 id="Progress">Displaying Progress in a Notification</h2> +<p> + Notifications can include an animated progress indicator that shows users the status + of an ongoing operation. If you can estimate how long the operation takes and how much of it + is complete at any time, use the "determinate" form of the indicator + (a progress bar). If you can't estimate the length of the operation, use the + "indeterminate" form of the indicator (an activity indicator). +</p> +<p> + Progress indicators are displayed with the platform's implementation of the + {@link android.widget.ProgressBar} class. +</p> +<p> + To use a progress indicator, call + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()}. The + determinate and indeterminate forms are described in the following sections. +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="FixedProgress">Displaying a fixed-duration progress indicator</h3> +<p> + To display a determinate progress bar, add the bar to your notification by calling + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress() + setProgress(max, progress, false)} and then issue the notification. As your operation proceeds, + increment <code>progress</code>, and update the notification. At the end of the operation, + <code>progress</code> should equal <code>max</code>. A common way to call + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} + is to set <code>max</code> to 100 and then increment <code>progress</code> as a + "percent complete" value for the operation. +</p> +<p> + You can either leave the progress bar showing when the operation is done, or remove it. In + either case, remember to update the notification text to show that the operation is complete. + To remove the progress bar, call + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress() + setProgress(0, 0, false)}. For example: +</p> <pre> -<?xml version="1.0" encoding="utf-8"?> -<resources> - <style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" /> - <style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" /> -</resources> +... +mNotifyManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); +mBuilder = new NotificationCompat.Builder(this); +mBuilder.setContentTitle("Picture Download") + .setContentText("Download in progress") + .setSmallIcon(R.drawable.ic_notification); +// Start a lengthy operation in a background thread +new Thread( + new Runnable() { + @Override + public void run() { + int incr; + // Do the "lengthy" operation 20 times + for (incr = 0; incr <= 100; incr+=5) { + // Sets the progress indicator to a max value, the + // current completion percentage, and "determinate" + // state + mBuilder.setProgress(100, incr, false); + // Displays the progress bar for the first time. + mNotifyManager.notify(0, mBuilder.build()); + // Sleeps the thread, simulating an operation + // that takes time + try { + // Sleep for 5 seconds + Thread.sleep(5*1000); + } catch (InterruptedException e) { + Log.d(TAG, "sleep failure"); + } + } + // When the loop is finished, updates the notification + mBuilder.setContentText("Download complete") + // Removes the progress bar + .setProgress(0,0,false); + mNotifyManager.notify(ID, mBuilder.build()); + } + } +// Starts the thread by calling the run() method in its Runnable +).start(); </pre> - <p>Now, when running on Android 2.3 (API level 9) or higher, the text in your custom view will -use the same colors that the system does for default notifications. This is important because later -versions of Android actually change the background color of the notifications to be dark. Inheriting -the system's styles ensures that your text will be light in such cases, but also if the background -is some other unexpected color, your text will also change as appropriate.</p> - </li> - - <li>Now, in the application code, use the RemoveViews - methods to define the image and text. Then pass the RemoteViews object to the <var>contentView</var> - field of the Notification, as shown in this example: +<p> + The resulting notifications are shown in figure 6. On the left side is a snapshot of the + notification during the operation; on the right side is a snapshot of it after the operation + has finished. +</p> +<img + id="figure6" + src="{@docRoot}images/ui/notifications/progress_bar_summary.png" + height="84" + alt="" /> +<p class="img-caption"> +<strong>Figure 6.</strong> The progress bar during and after the operation.</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="ActivityIndicator">Displaying a continuing activity indicator</h3> +<p> + To display an indeterminate activity indicator, add it to your notification with + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, true)} + (the first two arguments are ignored), and issue the notification. The result is an indicator + that has the same style as a progress bar, except that its animation is ongoing. +</p> +<p> + Issue the notification at the beginning of the operation. The animation will run until you + modify your notification. When the operation is done, call + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress() + setProgress(0, 0, false)} and then update the notification to remove the activity indicator. + Always do this; otherwise, the animation will run even when the operation is complete. Also + remember to change the notification text to indicate that the operation is complete. +</p> +<p> + To see how activity indicators work, refer to the preceding snippet. Locate the following lines: +</p> <pre> -RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.custom_notification_layout); -contentView.setImageViewResource(R.id.image, R.drawable.notification_image); -contentView.setTextViewText(R.id.title, "Custom notification"); -contentView.setTextViewText(R.id.text, "This is a custom layout"); -notification.contentView = contentView; +// Sets the progress indicator to a max value, the current completion +// percentage, and "determinate" state +mBuilder.setProgress(100, incr, false); +// Issues the notification +mNotifyManager.notify(0, mBuilder.build()); </pre> - - <p>As shown here, pass the application's package name and the layout - resource ID to the RemoteViews constructor. Then, define the content for the ImageView and TextView, - using the {@link android.widget.RemoteViews#setImageViewResource(int, int) setImageViewResource()} - and {@link android.widget.RemoteViews#setTextViewText(int, CharSequence) setTextViewText()}. - In each case, pass the reference ID of the appropriate View object that you want to set, along with - the value for that View. Finally, the RemoteViews object is passed to the Notification in the - <var>contentView</var> field.</p> - </li> - - <li>Because you don't need the - {@link android.app.Notification#setLatestEventInfo(Context,CharSequence,CharSequence,PendingIntent) - setLatestEventInfo()} method when using a custom view, you must define the Intent for the Notification - with the <var>contentIntent</var> field, as in this example: +<p> + Replace the lines you've found with the following lines: +</p> <pre> -Intent notificationIntent = new Intent(this, MyClass.class); -PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); -notification.contentIntent = contentIntent; + // Sets an activity indicator for an operation of indeterminate length +mBuilder.setProgress(0, 0, false); +// Issues the notification +mNotifyManager.notify(0, mBuilder.build()); </pre> - </li> - - <li>The notification can now be sent as usual: - <pre>mNotificationManager.notify(CUSTOM_VIEW_ID, notification);</pre> - </li> +<p> + The resulting indicator is shown in figure 7: +</p> +<img + id="figure7" + src="{@docRoot}images/ui/notifications/activity_indicator.png" + height="99" + alt="" /> +<p class="img-caption"><strong>Figure 7.</strong> An ongoing activity indicator.</p> + +<!-- ------------------------------------------------------------------------------------------ --> +<!-- ------------------------------------------------------------------------------------------ --> +<!-- ------------------------------------------------------------------------------------------ --> + +<!-- ------------------------------------------------------------------------------------------ --> +<h2 id="CustomNotification">Custom Notification Layouts</h2> +<p> + The notifications framework allows you to define a custom notification layout, which + defines the notification's appearance in a {@link android.widget.RemoteViews} object. + Custom layout notifications are similar to normal notifications, but they're based on a + {@link android.widget.RemoteViews} defined in a XML layout file. +</p> +<p> + To define a custom notification layout, start by instantiating a + {@link android.widget.RemoteViews} object that inflates an XML layout file. Then, + instead of calling methods such as + {@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()}, + call {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()}. To set + content details in the custom notification, use the methods in + {@link android.widget.RemoteViews} to set the values of the view's children: +</p> +<ol> + <li> + Create an XML layout for the notification in a separate file. You can use any file name + you wish, but you must use the extension <code>.xml</code> + </li> + <li> + In your app, use {@link android.widget.RemoteViews} methods to define your notification's + icons and text. Put this {@link android.widget.RemoteViews} object into your + {@link android.support.v4.app.NotificationCompat.Builder} by calling + {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()}. Avoid + setting a background {@link android.graphics.drawable.Drawable} on your + {@link android.widget.RemoteViews} object, because your text color may become unreadable. + </li> </ol> - - -<p>The {@link android.widget.RemoteViews} class also includes methods that you can use to easily add -a {@link android.widget.Chronometer} or {@link android.widget.ProgressBar} -in your notification's layout. For more information about creating custom layouts for your -notification, refer to the {@link android.widget.RemoteViews} class reference.</p> - -<p class="caution"><strong>Caution:</strong> -When creating a custom notification layout, you must take special care to ensure that your -custom layout functions properly in different device orientations and resolutions. While this -advice applies to all View layouts created on Android, it is especially important in this case -because your layout real estate is very restricted. So don't make your custom layout too -complex and be sure to test it in various configurations.</p> - - - - +<p> + The {@link android.widget.RemoteViews} class also includes methods that you can use to easily + add a {@link android.widget.Chronometer} or {@link android.widget.ProgressBar} + to your notification's layout. For more information about creating custom layouts for your + notification, refer to the {@link android.widget.RemoteViews} reference documentation. +</p> +<p class="caution"> + <strong>Caution:</strong> When you use a custom notification layout, take special care to + ensure that your custom layout works with different device orientations and resolutions. While + this advice applies to all View layouts, it's especially important for notifications because + the space in the notification drawer is very restricted. Don't make your custom layout too + complex, and be sure to test it in various configurations. +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h4>Using style resources for custom notification text</h4> +<p> + Always use style resources for the text of a custom notification. The background color of the + notification can vary across different devices and platform versions, and using style resources + helps you account for this. Starting in API level 9, the system defined a style for the + standard notification layout text. If you use the same style in applications that target API + level 9 or higher, you'll ensure that your text is visible against the display background. +</p> diff --git a/docs/html/images/ui/notifications/activity_indicator.png b/docs/html/images/ui/notifications/activity_indicator.png Binary files differnew file mode 100644 index 0000000..e21fae2 --- /dev/null +++ b/docs/html/images/ui/notifications/activity_indicator.png diff --git a/docs/html/images/ui/notifications/bigpicture_notification.png b/docs/html/images/ui/notifications/bigpicture_notification.png Binary files differnew file mode 100644 index 0000000..ced6380 --- /dev/null +++ b/docs/html/images/ui/notifications/bigpicture_notification.png diff --git a/docs/html/images/ui/notifications/bigpicture_notification_callouts.png b/docs/html/images/ui/notifications/bigpicture_notification_callouts.png Binary files differnew file mode 100644 index 0000000..e2d313a --- /dev/null +++ b/docs/html/images/ui/notifications/bigpicture_notification_callouts.png diff --git a/docs/html/images/ui/notifications/bigtext_notification.png b/docs/html/images/ui/notifications/bigtext_notification.png Binary files differnew file mode 100644 index 0000000..cd6e764 --- /dev/null +++ b/docs/html/images/ui/notifications/bigtext_notification.png diff --git a/docs/html/images/ui/notifications/bigtext_notification_callouts.png b/docs/html/images/ui/notifications/bigtext_notification_callouts.png Binary files differnew file mode 100644 index 0000000..4cfa403 --- /dev/null +++ b/docs/html/images/ui/notifications/bigtext_notification_callouts.png diff --git a/docs/html/images/ui/notifications/custom_message.png b/docs/html/images/ui/notifications/custom_message.png Binary files differnew file mode 100755 index 0000000..00b7632 --- /dev/null +++ b/docs/html/images/ui/notifications/custom_message.png diff --git a/docs/html/images/ui/notifications/iconic_notification.png b/docs/html/images/ui/notifications/iconic_notification.png Binary files differnew file mode 100644 index 0000000..4cabfdb --- /dev/null +++ b/docs/html/images/ui/notifications/iconic_notification.png diff --git a/docs/html/images/ui/notifications/inbox_notification.png b/docs/html/images/ui/notifications/inbox_notification.png Binary files differnew file mode 100644 index 0000000..fb182d5 --- /dev/null +++ b/docs/html/images/ui/notifications/inbox_notification.png diff --git a/docs/html/images/ui/notifications/inbox_notification_callouts.png b/docs/html/images/ui/notifications/inbox_notification_callouts.png Binary files differnew file mode 100644 index 0000000..2ec818e --- /dev/null +++ b/docs/html/images/ui/notifications/inbox_notification_callouts.png diff --git a/docs/html/images/ui/notifications/normal_notification.png b/docs/html/images/ui/notifications/normal_notification.png Binary files differnew file mode 100644 index 0000000..3cf0231 --- /dev/null +++ b/docs/html/images/ui/notifications/normal_notification.png diff --git a/docs/html/images/ui/notifications/normal_notification_callouts.png b/docs/html/images/ui/notifications/normal_notification_callouts.png Binary files differnew file mode 100644 index 0000000..db57daf --- /dev/null +++ b/docs/html/images/ui/notifications/normal_notification_callouts.png diff --git a/docs/html/images/ui/notifications/notifications_window.png b/docs/html/images/ui/notifications/notifications_window.png Binary files differnew file mode 100755 index 0000000..0354ee9 --- /dev/null +++ b/docs/html/images/ui/notifications/notifications_window.png diff --git a/docs/html/images/ui/notifications/progress_bar_summary.png b/docs/html/images/ui/notifications/progress_bar_summary.png Binary files differnew file mode 100644 index 0000000..073e697 --- /dev/null +++ b/docs/html/images/ui/notifications/progress_bar_summary.png diff --git a/docs/html/images/ui/notifications/progress_indicator_1.png b/docs/html/images/ui/notifications/progress_indicator_1.png Binary files differnew file mode 100644 index 0000000..f4c2365 --- /dev/null +++ b/docs/html/images/ui/notifications/progress_indicator_1.png diff --git a/docs/html/images/ui/notifications/progress_indicator_2.png b/docs/html/images/ui/notifications/progress_indicator_2.png Binary files differnew file mode 100644 index 0000000..975c90e --- /dev/null +++ b/docs/html/images/ui/notifications/progress_indicator_2.png diff --git a/docs/html/images/ui/notifications/status_bar.png b/docs/html/images/ui/notifications/status_bar.png Binary files differnew file mode 100755 index 0000000..f0240a5 --- /dev/null +++ b/docs/html/images/ui/notifications/status_bar.png diff --git a/docs/html/images/ui/notifications/updated_notification.png b/docs/html/images/ui/notifications/updated_notification.png Binary files differnew file mode 100644 index 0000000..f69fa4b --- /dev/null +++ b/docs/html/images/ui/notifications/updated_notification.png diff --git a/docs/html/tools/revisions/platforms.jd b/docs/html/tools/revisions/platforms.jd index 62ec422..178ab90 100644 --- a/docs/html/tools/revisions/platforms.jd +++ b/docs/html/tools/revisions/platforms.jd @@ -22,12 +22,12 @@ Highlights and APIs</a></li> <p>To develop an Android app, you must install at least one Android platform from the SDK Manager against which you can compile your app. Often, any given version of the Android will be revised -with bug fixes or other changes, as denoted by the "revision" number. Below, you'll find the +with bug fixes or other changes, as denoted by the revision number. Below, you'll find the release notes for each version of the platform and the subsequent revisions to the platform version.</p> -<p>To determine what revision of an Android platform you -have installed, refer to the "Installed Packages" listing in the Android SDK Manager.</p> +<p>To determine what revision of an Android platform you have installed, refer to the +<strong>Installed Packages</strong> listing in the Android SDK Manager.</p> @@ -45,10 +45,29 @@ have installed, refer to the "Installed Packages" listing in the Android SDK Man SDK tools to revision 20 or later and restart the Android SDK Manager. If you do not, the Android 4.1 system components will not be available for download.</p> + <div class="toggle-content opened"> <p><a href="#" onclick="return toggleContent(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" +class="toggle-content-img" alt="" />Revision 3</a> <em>(October 2012)</em> + </p> + + <div class="toggle-content-toggleme"> + + <p>Maintenance update. The system version is 4.1.2.</p> + <dl> + <dt>Dependencies:</dt> + <dd>SDK Tools r20 or higher is required.</dd> + </dl> + + </div> +</div> + +<div class="toggle-content closed"> + + <p><a href="#" onclick="return toggleContent(this)"> + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt="" />Revision 2</a> <em>(July 2012)</em> </p> @@ -226,10 +245,10 @@ class="toggle-content-img" alt="" />Revision 1</a> <em>(December 2011)</em> WVGA854 (480x854 high density, normal screen) </li> <li> - WXGA720 (1280x720, extra-high density, normal screen) + WXGA720 (1280x720, extra-high density, normal screen) </li> <li> - WSVGA (1024x600, medium density, large screen) + WSVGA (1024x600, medium density, large screen) </li> <li> WXGA (1280x800, medium density, xlarge screen) @@ -534,12 +553,12 @@ ADT 12.</p> <div class="toggle-content closed"> <p><a href="#" onclick="return toggleContent(this)"> - <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt="" />Android 3.0, Revision 1</a> <em>(February 2011)</em> </p> <div class="toggle-content-toggleme"> - + <dl> <dt>Dependencies:</dt> <dd> @@ -592,7 +611,7 @@ the "Installed Packages" listing in the Android SDK and AVD Manager.</p> <div class="toggle-content closed" > <p><a href="#" onclick="return toggleContent(this)"> - <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt="" />Android 2.3.4, Revision 1</a> <em>(May 2011)</em> </p> @@ -721,7 +740,7 @@ emulator skins are:</p> WVGA854 (480x854 high density, normal screen) </li> </ul> - + @@ -747,7 +766,7 @@ the "Installed Packages" listing in the Android SDK and AVD Manager.</p> <div class="toggle-content closed" > <p><a href="#" onclick="return toggleContent(this)"> - <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt="" />Android 2.3, Revision 1</a> <em>(December 2010)</em> </p> @@ -790,7 +809,7 @@ emulator skins are:</p> </ul> - + diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 4604437..6238edb 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -967,7 +967,7 @@ public final class Bitmap implements Parcelable { * @hide */ static public int scaleFromDensity(int size, int sdensity, int tdensity) { - if (sdensity == DENSITY_NONE || sdensity == tdensity) { + if (sdensity == DENSITY_NONE || tdensity == DENSITY_NONE || sdensity == tdensity) { return size; } diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 8e9384ee..82ed432 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -751,18 +751,10 @@ public class MediaRouter { RouteInfo.STATUS_AVAILABLE : RouteInfo.STATUS_CONNECTING); newRoute.mEnabled = available; - newRoute.mName = makeWifiDisplayName(display); + newRoute.mName = display.getFriendlyDisplayName(); return newRoute; } - static String makeWifiDisplayName(WifiDisplay display) { - String name = display.getDeviceAlias(); - if (TextUtils.isEmpty(name)) { - name = display.getDeviceName(); - } - return name; - } - private static void updateWifiDisplayRoute(RouteInfo route, WifiDisplay display, boolean available, WifiDisplayStatus wifiDisplayStatus) { final boolean isScanning = @@ -792,8 +784,8 @@ public class MediaRouter { } } - final String newName = makeWifiDisplayName(display); - if (route.getName().equals(newName)) { + final String newName = display.getFriendlyDisplayName(); + if (!route.getName().equals(newName)) { route.mName = newName; changed = true; } @@ -814,11 +806,11 @@ public class MediaRouter { } } - private static WifiDisplay findMatchingDisplay(WifiDisplay address, WifiDisplay[] displays) { + private static WifiDisplay findMatchingDisplay(WifiDisplay d, WifiDisplay[] displays) { for (int i = 0; i < displays.length; i++) { - final WifiDisplay d = displays[i]; - if (d.equals(address)) { - return d; + final WifiDisplay other = displays[i]; + if (d.getDeviceAddress().equals(other.getDeviceAddress())) { + return other; } } return null; diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png Binary files differindex 3ed7418..55c46b0 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_pressed.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_pressed.png Binary files differindex 5e20eea..e30cb8f 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_pressed.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_pressed.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png Binary files differindex 44cfc5b..b1910cf 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_pressed.png Binary files differindex 0c3fdcd..3abafdd 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_pressed.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_pressed.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png Binary files differindex 80fdb79..5dc93c2 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_pressed.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_pressed.png Binary files differindex ac7c1a7..a97de79 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_pressed.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_pressed.png diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml index dc1c6f3..f1a8d82 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml @@ -26,22 +26,31 @@ android:gravity="center_vertical" android:baselineAligned="false" > - <com.android.systemui.statusbar.policy.Clock - android:id="@+id/clock" + <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="8dp" - android:singleLine="true" - android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" - /> + android:orientation="horizontal" + > + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="8dp" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" + android:gravity="bottom" + /> - <com.android.systemui.statusbar.policy.DateView android:id="@+id/date" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date" - /> + <com.android.systemui.statusbar.policy.DateView android:id="@+id/date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date" + android:gravity="bottom" + /> + </LinearLayout> <Space android:layout_width="0dp" diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml index 089a54d..41ea6f3 100644 --- a/packages/SystemUI/res/values/donottranslate.xml +++ b/packages/SystemUI/res/values/donottranslate.xml @@ -17,10 +17,7 @@ */ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- For formatting day of week and date in DateView. %1$s is DOW, %2$s is date. - We show both (DOW on one line, then the date) but this can be overridden for locales as - necessary. - --> - <string name="status_bar_date_formatter">%1$s\n%2$s</string> + <!-- Date format for display: should match the lockscreen in /policy. --> + <string name="abbrev_wday_month_day_no_year">@*android:string/abbrev_wday_month_day_no_year</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index dcfd0b3..4e9013f 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -86,6 +86,8 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { private float mInitialTouchFocusY; private float mInitialTouchY; private float mInitialTouchSpan; + private float mLastFocusY; + private float mLastSpanY; private int mTouchSlop; private int mLastMotionY; private float mPopLimit; @@ -207,11 +209,6 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { float focusX = detector.getFocusX(); float focusY = detector.getFocusY(); - // your fingers have to be somewhat close to the bounds of the view in question - mInitialTouchFocusY = focusY; - mInitialTouchSpan = Math.abs(detector.getCurrentSpan()); - if (DEBUG_SCALE) Slog.d(TAG, "got mInitialTouchSpan: (" + mInitialTouchSpan + ")"); - final View underFocus = findView(focusX, focusY); if (underFocus != null) { startExpanding(underFocus, STRETCH); @@ -222,24 +219,19 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { @Override public boolean onScale(ScaleGestureDetector detector) { if (DEBUG_SCALE) Slog.v(TAG, "onscale() on " + mCurrView); - updateExpansion(); return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { - if (DEBUG_SCALE) Slog.v(TAG, "onscaleend()"); - // I guess we're alone now - if (DEBUG_SCALE) Slog.d(TAG, "scale end"); - finishExpanding(false); - clearView(); } }); } private void updateExpansion() { + if (DEBUG_SCALE) Slog.v(TAG, "updateExpansion()"); // are we scaling or dragging? - float span = Math.abs(mSGD.getCurrentSpan()) - mInitialTouchSpan; + float span = mSGD.getCurrentSpan() - mInitialTouchSpan; span *= USE_SPAN ? 1f : 0f; float drag = mSGD.getFocusY() - mInitialTouchFocusY; drag *= USE_DRAG ? 1f : 0f; @@ -251,6 +243,8 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { mScaler.setHeight(newHeight); setGlow(calculateGlow(target, newHeight)); + mLastFocusY = mSGD.getFocusY(); + mLastSpanY = mSGD.getCurrentSpan(); } private float clamp(float target) { @@ -362,6 +356,13 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { mSGD.onTouchEvent(ev); final int x = (int) mSGD.getFocusX(); final int y = (int) mSGD.getFocusY(); + + mInitialTouchFocusY = y; + mInitialTouchSpan = mSGD.getCurrentSpan(); + mLastFocusY = mInitialTouchFocusY; + mLastSpanY = mInitialTouchSpan; + if (DEBUG_SCALE) Slog.d(TAG, "set initial span: " + mInitialTouchSpan); + if (mExpanding) { return true; } else { @@ -376,8 +377,6 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { // detect a vertical pulling gesture with fingers somewhat separated if (DEBUG_SCALE) Slog.v(TAG, "got pull gesture (xspan=" + xspan + "px)"); - mInitialTouchFocusY = y; - final View underFocus = findView(x, y); if (underFocus != null) { startExpanding(underFocus, PULL); @@ -424,7 +423,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { @Override public boolean onTouchEvent(MotionEvent ev) { - final int action = ev.getAction(); + final int action = ev.getActionMasked(); if (DEBUG_SCALE) Slog.d(TAG, "touch: act=" + MotionEvent.actionToString(action) + " expanding=" + mExpanding + (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") + @@ -481,6 +480,14 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { break; } + + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_POINTER_DOWN: + if (DEBUG) Slog.d(TAG, "pointer change"); + mInitialTouchY += mSGD.getFocusY() - mLastFocusY; + mInitialTouchSpan += mSGD.getCurrentSpan() - mLastSpanY; + break; + case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (DEBUG) Slog.d(TAG, "up/cancel"); @@ -492,8 +499,11 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { } private void startExpanding(View v, int expandType) { + mExpansionStyle = expandType; + if (mExpanding && v == mCurrView) { + return; + } mExpanding = true; - mExpansionStyle = expandType; if (DEBUG) Slog.d(TAG, "scale type " + expandType + " beginning on view: " + v); mCallback.setUserLockedChild(v, true); setView(v); @@ -515,6 +525,8 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { private void finishExpanding(boolean force) { if (!mExpanding) return; + if (DEBUG) Slog.d(TAG, "scale in finishing on view: " + mCurrView); + float currentHeight = mScaler.getHeight(); float targetHeight = mSmallSize; float h = mScaler.getHeight(); @@ -539,6 +551,10 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { mExpanding = false; mExpansionStyle = NONE; + if (DEBUG) Slog.d(TAG, "wasClosed is: " + wasClosed); + if (DEBUG) Slog.d(TAG, "currentHeight is: " + currentHeight); + if (DEBUG) Slog.d(TAG, "mSmallSize is: " + mSmallSize); + if (DEBUG) Slog.d(TAG, "targetHeight is: " + targetHeight); if (DEBUG) Slog.d(TAG, "scale was finished on view: " + mCurrView); } diff --git a/packages/SystemUI/src/com/android/systemui/Somnambulator.java b/packages/SystemUI/src/com/android/systemui/Somnambulator.java index 9356ff2..0dd6d92 100644 --- a/packages/SystemUI/src/com/android/systemui/Somnambulator.java +++ b/packages/SystemUI/src/com/android/systemui/Somnambulator.java @@ -1,4 +1,4 @@ -/*); +/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,38 +18,23 @@ package com.android.systemui; import android.app.Activity; import android.content.Intent; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.UserHandle; -import android.provider.Settings; -import android.service.dreams.DreamService; -import android.service.dreams.IDreamManager; -import android.util.Slog; - +import android.service.dreams.Sandman; + +/** + * A simple activity that launches a dream. + * <p> + * Note: This Activity is special. If this class is moved to another package or + * renamed, be sure to update the component name in {@link Sandman}. + * </p> + */ public class Somnambulator extends Activity { - public static final String TAG = "Somnambulator"; - - public static final int DEFAULT_SCREENSAVER_ENABLED = 1; - public static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1; - public Somnambulator() { } - private boolean isScreenSaverEnabled() { - return Settings.Secure.getIntForUser(getContentResolver(), - Settings.Secure.SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED, - UserHandle.USER_CURRENT) != 0; - } - - private boolean isScreenSaverActivatedOnDock() { - return Settings.Secure.getIntForUser(getContentResolver(), - Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, - DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK, UserHandle.USER_CURRENT) != 0; - } - @Override public void onStart() { super.onStart(); + final Intent launchIntent = getIntent(); final String action = launchIntent.getAction(); if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) { @@ -64,23 +49,12 @@ public class Somnambulator extends Activity { setResult(RESULT_OK, resultIntent); } else { boolean docked = launchIntent.hasCategory(Intent.CATEGORY_DESK_DOCK); - - if (docked && !(isScreenSaverEnabled() && isScreenSaverActivatedOnDock())) { - Slog.i(TAG, "Dreams currently disabled for docks."); + if (docked) { + Sandman.startDreamWhenDockedIfAppropriate(this); } else { - IDreamManager somnambulist = IDreamManager.Stub.asInterface( - ServiceManager.checkService(DreamService.DREAM_SERVICE)); - if (somnambulist != null) { - try { - Slog.v(TAG, "Dreaming on " + (docked ? "dock insertion" : "user request")); - somnambulist.dream(); - } catch (RemoteException e) { - // fine, stay asleep then - } - } + Sandman.startDreamByUserRequest(this); } } finish(); } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java index 32af5e3..8c390c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java @@ -291,7 +291,7 @@ class QuickSettings { mBar.collapseAllPanels(true); final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (um.getUsers().size() > 1) { + if (um.getUsers(true).size() > 1) { try { WindowManagerGlobal.getWindowManagerService().lockNow( LockPatternUtils.USER_SWITCH_LOCK_OPTIONS); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java index 640dcca..1d6b3d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java @@ -87,11 +87,8 @@ public class DateView extends TextView { } protected void updateClock() { - final Context context = getContext(); - Date now = new Date(); - CharSequence dow = DateFormat.format("EEEE", now); - CharSequence date = DateFormat.getLongDateFormat(context).format(now); - setText(context.getString(R.string.status_bar_date_formatter, dow, date)); + final String dateFormat = getContext().getString(R.string.abbrev_wday_month_day_no_year); + setText(DateFormat.format(dateFormat, new Date())); } private boolean isVisible() { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/BiometricSensorUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/BiometricSensorUnlock.java index 39afaa2..e65a716 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/BiometricSensorUnlock.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/BiometricSensorUnlock.java @@ -37,21 +37,9 @@ interface BiometricSensorUnlock { public boolean isRunning(); /** - * Covers the backup unlock mechanism by showing the contents of the view initialized in - * {@link BiometricSensorUnlock#initializeView(View)}. The view should disappear after the - * specified timeout. If the timeout is 0, the interface shows until another event, such as - * calling {@link BiometricSensorUnlock#hide()}, causes it to disappear. Called on the UI - * thread. - * @param timeoutMilliseconds Amount of time in milliseconds to display the view before - * disappearing. A value of 0 means the view should remain visible. - */ - public void show(long timeoutMilliseconds); - - /** - * Uncovers the backup unlock mechanism by hiding the contents of the view initialized in - * {@link BiometricSensorUnlock#initializeView(View)}. + * Stops and removes the biometric unlock and shows the backup unlock */ - public void hide(); + public void stopAndShowBackup(); /** * Binds to the biometric unlock service and starts the unlock procedure. Called on the UI diff --git a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java index 203ba3c..b295f14 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java @@ -109,7 +109,9 @@ public class EmergencyButton extends Button { } else { // True if we need to show a secure screen (pin/pattern/SIM pin/SIM puk); // hides emergency button on "Slide" screen if device is not secure. - enabled = mLockPatternUtils.isSecure(); + + // XXX we decided to show this always. See bug 7276760 + enabled = true || mLockPatternUtils.isSecure(); } } mLockPatternUtils.updateEmergencyCallButtonState(this, phoneState, enabled, diff --git a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java index 4f2f6bf..3fe16cf 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java @@ -52,26 +52,19 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { private View mFaceUnlockView; private Handler mHandler; - private final int MSG_SHOW_FACE_UNLOCK_VIEW = 0; - private final int MSG_HIDE_FACE_UNLOCK_VIEW = 1; - private final int MSG_SERVICE_CONNECTED = 2; - private final int MSG_SERVICE_DISCONNECTED = 3; - private final int MSG_UNLOCK = 4; - private final int MSG_CANCEL = 5; - private final int MSG_REPORT_FAILED_ATTEMPT = 6; - private final int MSG_EXPOSE_FALLBACK = 7; - private final int MSG_POKE_WAKELOCK = 8; + private final int MSG_SERVICE_CONNECTED = 0; + private final int MSG_SERVICE_DISCONNECTED = 1; + private final int MSG_UNLOCK = 2; + private final int MSG_CANCEL = 3; + private final int MSG_REPORT_FAILED_ATTEMPT = 4; + private final int MSG_EXPOSE_FALLBACK = 5; + private final int MSG_POKE_WAKELOCK = 6; // TODO: This was added for the purpose of adhering to what the biometric interface expects // the isRunning() function to return. However, it is probably not necessary to have both // mRunning and mServiceRunning. I'd just rather wait to change that logic. private volatile boolean mIsRunning = false; - // Long enough to stay visible while the service starts - // Short enough to not have to wait long for backup if service fails to start or crashes - // The service can take a couple of seconds to start on the first try after boot - private final int SERVICE_STARTUP_VIEW_TIMEOUT = 3000; - // So the user has a consistent amount of time when brought to the backup method from Face // Unlock private final int BACKUP_LOCK_TIMEOUT = 5000; @@ -110,30 +103,11 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { } /** - * Sets the Face Unlock view to visible, hiding it after the specified amount of time. If - * timeoutMillis is 0, no hide is performed. Called on the UI thread. + * Dismisses face unlock and goes to the backup lock */ - public void show(long timeoutMillis) { - if (DEBUG) Log.d(TAG, "show()"); - if (mHandler.getLooper() != Looper.myLooper()) { - Log.e(TAG, "show() called off of the UI thread"); - } - removeDisplayMessages(); - if (timeoutMillis > 0) { - mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACE_UNLOCK_VIEW, timeoutMillis); - } - } - - /** - * Hides the Face Unlock view. - */ - public void hide() { - if (DEBUG) Log.d(TAG, "hide()"); - // Removes any wakelock messages to make sure they don't cause the screen to turn back on. - mHandler.removeMessages(MSG_POKE_WAKELOCK); - // Remove messages to prevent a delayed show message from undo-ing the hide - removeDisplayMessages(); - mHandler.sendEmptyMessage(MSG_HIDE_FACE_UNLOCK_VIEW); + public void stopAndShowBackup() { + if (DEBUG) Log.d(TAG, "stopAndShowBackup()"); + mHandler.sendEmptyMessage(MSG_CANCEL); } /** @@ -151,10 +125,6 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { Log.w(TAG, "start() called when already running"); } - // Show Face Unlock view, but only for a little bit so lockpattern will become visible if - // Face Unlock fails to start or crashes - // This must show before bind to guarantee that Face Unlock has a place to display - show(SERVICE_STARTUP_VIEW_TIMEOUT); if (!mBoundToService) { Log.d(TAG, "Binding to Face Unlock service for user=" + mLockPatternUtils.getCurrentUser()); @@ -234,12 +204,6 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { */ public boolean handleMessage(Message msg) { switch (msg.what) { - case MSG_SHOW_FACE_UNLOCK_VIEW: - handleShowFaceUnlockView(); - break; - case MSG_HIDE_FACE_UNLOCK_VIEW: - handleHideFaceUnlockView(); - break; case MSG_SERVICE_CONNECTED: handleServiceConnected(); break; @@ -269,22 +233,6 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { } /** - * Sets the Face Unlock view to visible, thus covering the backup lock. - */ - void handleShowFaceUnlockView() { - if (DEBUG) Log.d(TAG, "handleShowFaceUnlockView()"); - // Not required - } - - /** - * Hide face unlock and show backup - */ - void handleHideFaceUnlockView() { - if (DEBUG) Log.d(TAG, "handleHideFaceUnlockView()"); - mKeyguardScreenCallback.showBackupSecurity(); - } - - /** * Tells the service to start its UI via an AIDL interface. Called when the * onServiceConnected() callback is received. */ @@ -347,23 +295,21 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { } /** - * Stops the Face Unlock service and tells the device to grant access to the user. Shows the - * Face Unlock view to keep the backup lock covered while the device unlocks. + * Stops the Face Unlock service and tells the device to grant access to the user. */ void handleUnlock() { if (DEBUG) Log.d(TAG, "handleUnlock()"); - removeDisplayMessages(); stop(); mKeyguardScreenCallback.reportSuccessfulUnlockAttempt(); mKeyguardScreenCallback.dismiss(true); } /** - * Stops the Face Unlock service and exposes the backup lock. + * Stops the Face Unlock service and goes to the backup lock. */ void handleCancel() { if (DEBUG) Log.d(TAG, "handleCancel()"); - mKeyguardScreenCallback.dismiss(false); + mKeyguardScreenCallback.showBackupSecurity(); stop(); mKeyguardScreenCallback.userActivity(BACKUP_LOCK_TIMEOUT); } @@ -398,15 +344,6 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { } /** - * Removes show and hide messages from the message queue. Called to prevent delayed show/hide - * messages from undoing a new message. - */ - private void removeDisplayMessages() { - mHandler.removeMessages(MSG_SHOW_FACE_UNLOCK_VIEW); - mHandler.removeMessages(MSG_HIDE_FACE_UNLOCK_VIEW); - } - - /** * Implements service connection methods. */ private ServiceConnection mConnection = new ServiceConnection() { @@ -508,7 +445,7 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { * Called when the Face Unlock service starts displaying the UI, indicating that the backup * unlock can be exposed because the Face Unlock service is now covering the backup with its * UI. - **/ + */ public void exposeFallback() { if (DEBUG) Log.d(TAG, "exposeFallback()"); mHandler.sendEmptyMessage(MSG_EXPOSE_FALLBACK); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java index 8776a80..d059e36 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java @@ -20,6 +20,7 @@ import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; import android.view.View; +import android.widget.ImageButton; import android.widget.LinearLayout; import com.android.internal.R; @@ -29,11 +30,13 @@ import com.android.internal.widget.LockPatternUtils; public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecurityView { private static final String TAG = "KeyguardFaceUnlockView"; + private static final boolean DEBUG = false; private KeyguardSecurityCallback mKeyguardSecurityCallback; private LockPatternUtils mLockPatternUtils; private BiometricSensorUnlock mBiometricUnlock; private KeyguardNavigationManager mNavigationManager; private View mFaceUnlockAreaView; + private ImageButton mCancelButton; public KeyguardFaceUnlockView(Context context) { this(context, null); @@ -70,25 +73,25 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu @Override public void onDetachedFromWindow() { + if (DEBUG) Log.d(TAG, "onDetachedFromWindow()"); if (mBiometricUnlock != null) { - mBiometricUnlock.hide(); - mBiometricUnlock.stop(); + mBiometricUnlock.stopAndShowBackup(); } } @Override public void onPause() { + if (DEBUG) Log.d(TAG, "onPause()"); if (mBiometricUnlock != null) { - mBiometricUnlock.hide(); - mBiometricUnlock.stop(); + mBiometricUnlock.stopAndShowBackup(); } KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback); } @Override public void onResume() { + if (DEBUG) Log.d(TAG, "onResume()"); maybeStartBiometricUnlock(); - mBiometricUnlock.show(0); KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback); } @@ -112,6 +115,14 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu mFaceUnlockAreaView = findViewById(R.id.face_unlock_area_view); if (mFaceUnlockAreaView != null) { mBiometricUnlock = new FaceUnlock(mContext); + + mCancelButton = (ImageButton) findViewById(R.id.face_unlock_cancel_button); + mCancelButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mBiometricUnlock.stopAndShowBackup(); + } + }); } else { Log.w(TAG, "Couldn't find biometric unlock view"); } @@ -123,17 +134,20 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu * unlock area. */ private void maybeStartBiometricUnlock() { + if (DEBUG) Log.d(TAG, "maybeStartBiometricUnlock()"); if (mBiometricUnlock != null) { KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); final boolean backupIsTimedOut = ( monitor.getFailedUnlockAttempts() >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT); - if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE + // TODO: These max attempts checks are also checked in KeyguardSecurityModel so they + // might not be necessary here anymore. + if (monitor.getPhoneState() != TelephonyManager.CALL_STATE_RINGING && !monitor.getMaxBiometricUnlockAttemptsReached() && !backupIsTimedOut) { mBiometricUnlock.start(); } else { - mBiometricUnlock.hide(); + mBiometricUnlock.stopAndShowBackup(); } } } @@ -142,14 +156,15 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu // We need to stop the biometric unlock when a phone call comes in @Override public void onPhoneStateChanged(int phoneState) { + if (DEBUG) Log.d(TAG, "onPhoneStateChanged(" + phoneState + ")"); if (phoneState == TelephonyManager.CALL_STATE_RINGING) { - mBiometricUnlock.stop(); - mBiometricUnlock.hide(); + mBiometricUnlock.stopAndShowBackup(); } } @Override public void onUserSwitched(int userId) { + if (DEBUG) Log.d(TAG, "onUserSwitched(" + userId + ")"); if (mBiometricUnlock != null) { mBiometricUnlock.stop(); } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java index a413a13..460f3c6 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -415,6 +415,7 @@ public class KeyguardHostView extends KeyguardViewBase { * account unlock screen and biometric unlock to show the user's normal unlock. */ private void showBackupSecurity() { + if (DEBUG) Log.d(TAG, "showBackupSecurity()"); showSecurityScreen(mSecurityModel.getBackupSecurityMode()); } @@ -431,6 +432,7 @@ public class KeyguardHostView extends KeyguardViewBase { } private void showNextSecurityScreenOrFinish(boolean authenticated) { + if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); boolean finish = false; if (SecurityMode.None == mCurrentSecuritySelection) { SecurityMode securityMode = mSecurityModel.getSecurityMode(); @@ -559,7 +561,7 @@ public class KeyguardHostView extends KeyguardViewBase { * @param securityMode */ private void showSecurityScreen(SecurityMode securityMode) { - + if (DEBUG) Log.d(TAG, "showSecurityScreen"); if (securityMode == mCurrentSecuritySelection) return; KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); @@ -577,16 +579,11 @@ public class KeyguardHostView extends KeyguardViewBase { // Find and show this child. final int childCount = mSecurityViewContainer.getChildCount(); - // If we're go to/from the selector view, do flip animation, otherwise use fade animation. - final boolean doFlip = mCurrentSecuritySelection == SecurityMode.None - || securityMode == SecurityMode.None; - final int inAnimation = doFlip ? R.anim.keyguard_security_animate_in - : R.anim.keyguard_security_fade_in; - final int outAnimation = doFlip ? R.anim.keyguard_security_animate_out - : R.anim.keyguard_security_fade_out; - - mSecurityViewContainer.setInAnimation(AnimationUtils.loadAnimation(mContext, inAnimation)); - mSecurityViewContainer.setOutAnimation(AnimationUtils.loadAnimation(mContext, outAnimation)); + // Do flip animation to the next screen + mSecurityViewContainer.setInAnimation( + AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_in)); + mSecurityViewContainer.setOutAnimation( + AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_out)); final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); for (int i = 0; i < childCount; i++) { if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) { diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java index 41617c8..9fea6f3 100644 --- a/services/java/com/android/server/AppWidgetServiceImpl.java +++ b/services/java/com/android/server/AppWidgetServiceImpl.java @@ -943,7 +943,10 @@ class AppWidgetServiceImpl { ensureStateLoadedLocked(); for (int i = 0; i < N; i++) { AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]); - updateAppWidgetInstanceLocked(id, views, true); + if (id.views != null) { + // Only trigger a partial update for a widget if it has received a full update + updateAppWidgetInstanceLocked(id, views, true); + } } } } diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java index e9e3163..0e456f1 100644 --- a/services/java/com/android/server/UiModeManagerService.java +++ b/services/java/com/android/server/UiModeManagerService.java @@ -37,11 +37,9 @@ import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; -import android.service.dreams.DreamService; -import android.service.dreams.IDreamManager; +import android.service.dreams.Sandman; import android.util.Slog; import java.io.FileDescriptor; @@ -51,7 +49,7 @@ import com.android.internal.R; import com.android.internal.app.DisableCarModeActivity; import com.android.server.TwilightService.TwilightState; -class UiModeManagerService extends IUiModeManager.Stub { +final class UiModeManagerService extends IUiModeManager.Stub { private static final String TAG = UiModeManager.class.getSimpleName(); private static final boolean LOG = false; @@ -59,9 +57,6 @@ class UiModeManagerService extends IUiModeManager.Stub { private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true; private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true; - private static final int DEFAULT_SCREENSAVER_ENABLED = 1; - private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1; - private final Context mContext; private final TwilightService mTwilightService; private final Handler mHandler = new Handler(); @@ -186,57 +181,79 @@ class UiModeManagerService extends IUiModeManager.Stub { mTwilightService.registerListener(mTwilightListener, mHandler); } + @Override // Binder call public void disableCarMode(int flags) { - synchronized (mLock) { - setCarModeLocked(false); - if (mSystemReady) { - updateLocked(0, flags); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + setCarModeLocked(false); + if (mSystemReady) { + updateLocked(0, flags); + } } + } finally { + Binder.restoreCallingIdentity(ident); } } + @Override // Binder call public void enableCarMode(int flags) { - synchronized (mLock) { - setCarModeLocked(true); - if (mSystemReady) { - updateLocked(flags, 0); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + setCarModeLocked(true); + if (mSystemReady) { + updateLocked(flags, 0); + } } + } finally { + Binder.restoreCallingIdentity(ident); } } + @Override // Binder call public int getCurrentModeType() { - synchronized (mLock) { - return mCurUiMode & Configuration.UI_MODE_TYPE_MASK; + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + return mCurUiMode & Configuration.UI_MODE_TYPE_MASK; + } + } finally { + Binder.restoreCallingIdentity(ident); } } - public void setNightMode(int mode) throws RemoteException { - synchronized (mLock) { - switch (mode) { - case UiModeManager.MODE_NIGHT_NO: - case UiModeManager.MODE_NIGHT_YES: - case UiModeManager.MODE_NIGHT_AUTO: - break; - default: - throw new IllegalArgumentException("Unknown mode: " + mode); - } - if (!isDoingNightMode()) { - return; - } + @Override // Binder call + public void setNightMode(int mode) { + switch (mode) { + case UiModeManager.MODE_NIGHT_NO: + case UiModeManager.MODE_NIGHT_YES: + case UiModeManager.MODE_NIGHT_AUTO: + break; + default: + throw new IllegalArgumentException("Unknown mode: " + mode); + } - if (mNightMode != mode) { - long ident = Binder.clearCallingIdentity(); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.UI_NIGHT_MODE, mode); - Binder.restoreCallingIdentity(ident); - mNightMode = mode; - updateLocked(0, 0); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + if (isDoingNightModeLocked() && mNightMode != mode) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.UI_NIGHT_MODE, mode); + mNightMode = mode; + updateLocked(0, 0); + } } + } finally { + Binder.restoreCallingIdentity(ident); } } - public int getNightMode() throws RemoteException { - return mNightMode; + @Override // Binder call + public int getNightMode() { + synchronized (mLock) { + return mNightMode; + } } void systemReady() { @@ -248,17 +265,17 @@ class UiModeManagerService extends IUiModeManager.Stub { } } - boolean isDoingNightMode() { + private boolean isDoingNightModeLocked() { return mCarModeEnabled || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED; } - void setCarModeLocked(boolean enabled) { + private void setCarModeLocked(boolean enabled) { if (mCarModeEnabled != enabled) { mCarModeEnabled = enabled; } } - void updateDockState(int newState) { + private void updateDockState(int newState) { synchronized (mLock) { if (newState != mDockState) { mDockState = newState; @@ -270,7 +287,7 @@ class UiModeManagerService extends IUiModeManager.Stub { } } - final static boolean isDeskDockState(int state) { + private static boolean isDeskDockState(int state) { switch (state) { case Intent.EXTRA_DOCK_STATE_DESK: case Intent.EXTRA_DOCK_STATE_LE_DESK: @@ -281,7 +298,7 @@ class UiModeManagerService extends IUiModeManager.Stub { } } - final void updateConfigurationLocked() { + private void updateConfigurationLocked() { int uiMode = mTelevision ? Configuration.UI_MODE_TYPE_TELEVISION : mDefaultUiModeType; if (mCarModeEnabled) { uiMode = Configuration.UI_MODE_TYPE_CAR; @@ -315,7 +332,7 @@ class UiModeManagerService extends IUiModeManager.Stub { } } - final void sendConfigurationLocked() { + private void sendConfigurationLocked() { if (mSetUiMode != mConfiguration.uiMode) { mSetUiMode = mConfiguration.uiMode; @@ -327,105 +344,99 @@ class UiModeManagerService extends IUiModeManager.Stub { } } - final void updateLocked(int enableFlags, int disableFlags) { - long ident = Binder.clearCallingIdentity(); + private void updateLocked(int enableFlags, int disableFlags) { + String action = null; + String oldAction = null; + if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) { + adjustStatusBarCarModeLocked(); + oldAction = UiModeManager.ACTION_EXIT_CAR_MODE; + } else if (isDeskDockState(mLastBroadcastState)) { + oldAction = UiModeManager.ACTION_EXIT_DESK_MODE; + } - try { - String action = null; - String oldAction = null; - if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) { + if (mCarModeEnabled) { + if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) { adjustStatusBarCarModeLocked(); - oldAction = UiModeManager.ACTION_EXIT_CAR_MODE; - } else if (isDeskDockState(mLastBroadcastState)) { - oldAction = UiModeManager.ACTION_EXIT_DESK_MODE; - } - if (mCarModeEnabled) { - if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) { - adjustStatusBarCarModeLocked(); - - if (oldAction != null) { - mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL); - } - mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR; - action = UiModeManager.ACTION_ENTER_CAR_MODE; + if (oldAction != null) { + mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL); } - } else if (isDeskDockState(mDockState)) { - if (!isDeskDockState(mLastBroadcastState)) { - if (oldAction != null) { - mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL); - } - mLastBroadcastState = mDockState; - action = UiModeManager.ACTION_ENTER_DESK_MODE; + mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR; + action = UiModeManager.ACTION_ENTER_CAR_MODE; + } + } else if (isDeskDockState(mDockState)) { + if (!isDeskDockState(mLastBroadcastState)) { + if (oldAction != null) { + mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL); } - } else { - mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; - action = oldAction; + mLastBroadcastState = mDockState; + action = UiModeManager.ACTION_ENTER_DESK_MODE; } + } else { + mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + action = oldAction; + } - if (action != null) { - if (LOG) { - Slog.v(TAG, String.format( - "updateLocked: preparing broadcast: action=%s enable=0x%08x disable=0x%08x", - action, enableFlags, disableFlags)); - } + if (action != null) { + if (LOG) { + Slog.v(TAG, String.format( + "updateLocked: preparing broadcast: action=%s enable=0x%08x disable=0x%08x", + action, enableFlags, disableFlags)); + } - // Send the ordered broadcast; the result receiver will receive after all - // broadcasts have been sent. If any broadcast receiver changes the result - // code from the initial value of RESULT_OK, then the result receiver will - // not launch the corresponding dock application. This gives apps a chance - // to override the behavior and stay in their app even when the device is - // placed into a dock. - Intent intent = new Intent(action); - intent.putExtra("enableFlags", enableFlags); - intent.putExtra("disableFlags", disableFlags); - mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, - mResultReceiver, null, Activity.RESULT_OK, null, null); - - // Attempting to make this transition a little more clean, we are going - // to hold off on doing a configuration change until we have finished - // the broadcast and started the home activity. - mHoldingConfiguration = true; - updateConfigurationLocked(); - } else { - String category = null; - if (mCarModeEnabled) { - if (ENABLE_LAUNCH_CAR_DOCK_APP - && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { - category = Intent.CATEGORY_CAR_DOCK; - } - } else if (isDeskDockState(mDockState)) { - if (ENABLE_LAUNCH_DESK_DOCK_APP - && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { - category = Intent.CATEGORY_DESK_DOCK; - } - } else { - if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { - category = Intent.CATEGORY_HOME; - } + // Send the ordered broadcast; the result receiver will receive after all + // broadcasts have been sent. If any broadcast receiver changes the result + // code from the initial value of RESULT_OK, then the result receiver will + // not launch the corresponding dock application. This gives apps a chance + // to override the behavior and stay in their app even when the device is + // placed into a dock. + Intent intent = new Intent(action); + intent.putExtra("enableFlags", enableFlags); + intent.putExtra("disableFlags", disableFlags); + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, + mResultReceiver, null, Activity.RESULT_OK, null, null); + + // Attempting to make this transition a little more clean, we are going + // to hold off on doing a configuration change until we have finished + // the broadcast and started the home activity. + mHoldingConfiguration = true; + updateConfigurationLocked(); + } else { + String category = null; + if (mCarModeEnabled) { + if (ENABLE_LAUNCH_CAR_DOCK_APP + && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + category = Intent.CATEGORY_CAR_DOCK; } - - if (LOG) { - Slog.v(TAG, "updateLocked: null action, mDockState=" - + mDockState +", category=" + category); + } else if (isDeskDockState(mDockState)) { + if (ENABLE_LAUNCH_DESK_DOCK_APP + && (enableFlags & UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + category = Intent.CATEGORY_DESK_DOCK; } + } else { + if ((disableFlags & UiModeManager.DISABLE_CAR_MODE_GO_HOME) != 0) { + category = Intent.CATEGORY_HOME; + } + } - sendConfigurationAndStartDreamOrDockAppLocked(category); + if (LOG) { + Slog.v(TAG, "updateLocked: null action, mDockState=" + + mDockState +", category=" + category); } - // keep screen on when charging and in car mode - boolean keepScreenOn = mCharging && - ((mCarModeEnabled && mCarModeKeepsScreenOn) || - (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn)); - if (keepScreenOn != mWakeLock.isHeld()) { - if (keepScreenOn) { - mWakeLock.acquire(); - } else { - mWakeLock.release(); - } + sendConfigurationAndStartDreamOrDockAppLocked(category); + } + + // keep screen on when charging and in car mode + boolean keepScreenOn = mCharging && + ((mCarModeEnabled && mCarModeKeepsScreenOn) || + (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn)); + if (keepScreenOn != mWakeLock.isHeld()) { + if (keepScreenOn) { + mWakeLock.acquire(); + } else { + mWakeLock.release(); } - } finally { - Binder.restoreCallingIdentity(ident); } } @@ -480,18 +491,20 @@ class UiModeManagerService extends IUiModeManager.Stub { // activity manager take care of both the start and config // change. Intent homeIntent = buildHomeIntent(category); - try { - int result = ActivityManagerNative.getDefault().startActivityWithConfig( - null, homeIntent, null, null, null, 0, 0, - mConfiguration, null, UserHandle.USER_CURRENT); - if (result >= ActivityManager.START_SUCCESS) { - dockAppStarted = true; - } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) { - Slog.e(TAG, "Could not start dock app: " + homeIntent - + ", startActivityWithConfig result " + result); + if (Sandman.shouldStartDockApp(mContext, homeIntent)) { + try { + int result = ActivityManagerNative.getDefault().startActivityWithConfig( + null, homeIntent, null, null, null, 0, 0, + mConfiguration, null, UserHandle.USER_CURRENT); + if (result >= ActivityManager.START_SUCCESS) { + dockAppStarted = true; + } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) { + Slog.e(TAG, "Could not start dock app: " + homeIntent + + ", startActivityWithConfig result " + result); + } + } catch (RemoteException ex) { + Slog.e(TAG, "Could not start dock app: " + homeIntent, ex); } - } catch (RemoteException ex) { - Slog.e(TAG, "Could not start dock app: " + homeIntent, ex); } } @@ -499,44 +512,15 @@ class UiModeManagerService extends IUiModeManager.Stub { sendConfigurationLocked(); // If we did not start a dock app, then start dreaming if supported. - if (category != null && !dockAppStarted - && isScreenSaverEnabled() && isScreenSaverActivatedOnDock()) { - Slog.i(TAG, "Activating dream while docked."); - try { - IDreamManager dreamManagerService = IDreamManager.Stub.asInterface( - ServiceManager.getService(DreamService.DREAM_SERVICE)); - if (dreamManagerService != null && !dreamManagerService.isDreaming()) { - // Wake up. - // The power manager will wake up the system when it starts receiving power - // but there is a race between that happening and the UI mode manager - // starting a dream. We want the system to already be awake - // by the time this happens. Otherwise the dream may not start. - mPowerManager.wakeUp(SystemClock.uptimeMillis()); - - // Dream. - dreamManagerService.dream(); - } - } catch (RemoteException ex) { - Slog.e(TAG, "Could not start dream when docked.", ex); - } + if (category != null && !dockAppStarted) { + Sandman.startDreamWhenDockedIfAppropriate(mContext); } } - private boolean isScreenSaverEnabled() { - return Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED, - UserHandle.USER_CURRENT) != 0; - } - - private boolean isScreenSaverActivatedOnDock() { - return Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, - DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK, UserHandle.USER_CURRENT) != 0; - } - private void adjustStatusBarCarModeLocked() { if (mStatusBarManager == null) { - mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE); + mStatusBarManager = (StatusBarManager) + mContext.getSystemService(Context.STATUS_BAR_SERVICE); } // Fear not: StatusBarManagerService manages a list of requests to disable @@ -581,7 +565,7 @@ class UiModeManagerService extends IUiModeManager.Stub { private void updateTwilight() { synchronized (mLock) { - if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { + if (isDoingNightModeLocked() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { updateComputedNightModeLocked(); updateLocked(0, 0); } diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java index 51ccd47..c8931f4 100644 --- a/services/java/com/android/server/accessibility/ScreenMagnifier.java +++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java @@ -376,7 +376,8 @@ public final class ScreenMagnifier implements EventStreamTransformation { } if (event.getActionMasked() == MotionEvent.ACTION_UP) { clear(); - final float scale = mMagnificationController.getScale(); + final float scale = Math.min(Math.max(mMagnificationController.getScale(), + MIN_SCALE), MAX_SCALE); if (scale != getPersistedScale()) { persistScale(scale); } @@ -996,6 +997,7 @@ public final class ScreenMagnifier implements EventStreamTransformation { // TODO: Are these all the windows we want to make // visible when they appear on the screen? // Do we need to take some of them out? + case WindowManager.LayoutParams.TYPE_APPLICATION: case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java index b2beb5e..3e541dd 100644 --- a/services/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/java/com/android/server/display/WifiDisplayAdapter.java @@ -50,6 +50,8 @@ import java.util.Arrays; final class WifiDisplayAdapter extends DisplayAdapter { private static final String TAG = "WifiDisplayAdapter"; + private static final boolean DEBUG = false; + private final PersistentDataStore mPersistentDataStore; private final boolean mSupportsProtectedBuffers; @@ -116,6 +118,10 @@ final class WifiDisplayAdapter extends DisplayAdapter { } public void requestScanLocked() { + if (DEBUG) { + Slog.d(TAG, "requestScanLocked"); + } + getHandler().post(new Runnable() { @Override public void run() { @@ -127,6 +133,10 @@ final class WifiDisplayAdapter extends DisplayAdapter { } public void requestConnectLocked(final String address, final boolean trusted) { + if (DEBUG) { + Slog.d(TAG, "requestConnectLocked: address=" + address + ", trusted=" + trusted); + } + if (!trusted) { synchronized (getSyncRoot()) { if (!isRememberedDisplayLocked(address)) { @@ -157,6 +167,10 @@ final class WifiDisplayAdapter extends DisplayAdapter { } public void requestDisconnectLocked() { + if (DEBUG) { + Slog.d(TAG, "requestDisconnectedLocked"); + } + getHandler().post(new Runnable() { @Override public void run() { @@ -168,9 +182,13 @@ final class WifiDisplayAdapter extends DisplayAdapter { } public void requestRenameLocked(String address, String alias) { + if (DEBUG) { + Slog.d(TAG, "requestRenameLocked: address=" + address + ", alias=" + alias); + } + if (alias != null) { alias = alias.trim(); - if (alias.isEmpty()) { + if (alias.isEmpty() || alias.equals(address)) { alias = null; } } @@ -183,6 +201,10 @@ final class WifiDisplayAdapter extends DisplayAdapter { } public void requestForgetLocked(String address) { + if (DEBUG) { + Slog.d(TAG, "requestForgetLocked: address=" + address); + } + if (mPersistentDataStore.forgetWifiDisplay(address)) { mPersistentDataStore.saveIfNeeded(); updateRememberedDisplaysLocked(); @@ -200,6 +222,10 @@ final class WifiDisplayAdapter extends DisplayAdapter { mFeatureState, mScanState, mActiveDisplayState, mActiveDisplay, mAvailableDisplays, mRememberedDisplays); } + + if (DEBUG) { + Slog.d(TAG, "getWifiDisplayStatusLocked: result=" + mCurrentStatus); + } return mCurrentStatus; } @@ -295,6 +321,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { } } + @Override public void onScanFinished(WifiDisplay[] availableDisplays) { synchronized (getSyncRoot()) { availableDisplays = mPersistentDataStore.applyWifiDisplayAliases( diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index b8d7286..f59e30d 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -1835,7 +1835,8 @@ public class PackageManagerService extends IPackageManager.Stub { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) return null; // Note: isEnabledLP() does not apply here - always return info - return PackageParser.generateApplicationInfo(p, flags, ps.readUserState(userId)); + return PackageParser.generateApplicationInfo( + p, flags, ps.readUserState(userId), userId); } if ("android".equals(packageName)||"system".equals(packageName)) { return mAndroidApplication; diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 54f6deb..77d815b 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -1213,6 +1213,7 @@ public class WindowManagerService extends IWindowManager.Stub final WindowState curTarget = mInputMethodTarget; if (curTarget != null && w != null && curTarget.isDisplayedLw() + && curTarget.isClosing() && (curTarget.mWinAnimator.mAnimLayer > w.mWinAnimator.mAnimLayer)) { if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Current target higher, not changing"); return windows.indexOf(curTarget) + 1; diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index 23892f6..feb29b1 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -1024,6 +1024,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { return mClient.asBinder().isBinderAlive(); } + boolean isClosing() { + return mExiting || (mService.mClosingApps.contains(mAppToken)); + } + @Override public boolean isDefaultDisplay() { return mDisplayContent.isDefaultDisplay; @@ -1234,7 +1238,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { mWasPaused = mToken.paused; mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this)) + " u" + UserHandle.getUserId(mSession.mUid) - + " " + mLastTitle + (mWasPaused ? " PAUSED}" : "}"); + + " " + mLastTitle + (mExiting ? " EXITING}" : "}"); } return mStringNameCache; } diff --git a/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/img1600x1067.jpg b/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/img1600x1067.jpg Binary files differnew file mode 100644 index 0000000..05d3ee2 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/img1600x1067.jpg diff --git a/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/img1600x1067b.jpg b/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/img1600x1067b.jpg Binary files differnew file mode 100644 index 0000000..aed0781 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/res/drawable-nodpi/img1600x1067b.jpg diff --git a/tests/RenderScriptTests/ImageProcessing2/res/layout/main.xml b/tests/RenderScriptTests/ImageProcessing2/res/layout/main.xml index bd56d62..f0a2b92 100644 --- a/tests/RenderScriptTests/ImageProcessing2/res/layout/main.xml +++ b/tests/RenderScriptTests/ImageProcessing2/res/layout/main.xml @@ -54,6 +54,10 @@ android:id="@+id/filterselection" android:layout_width="fill_parent" android:layout_height="wrap_content"/> + <Spinner + android:id="@+id/spinner1" + android:layout_width="fill_parent" + android:layout_height="wrap_content"/> <TextView android:id="@+id/slider1Text" android:layout_width="match_parent" @@ -124,6 +128,11 @@ android:layout_marginRight="10sp" android:layout_width="match_parent" android:layout_height="wrap_content"/> + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/benchmark_all" + android:onClick="benchmark_all"/> </LinearLayout> </ScrollView> </LinearLayout> diff --git a/tests/RenderScriptTests/ImageProcessing2/res/values/strings.xml b/tests/RenderScriptTests/ImageProcessing2/res/values/strings.xml index cc5cc4d..a7dd165 100644 --- a/tests/RenderScriptTests/ImageProcessing2/res/values/strings.xml +++ b/tests/RenderScriptTests/ImageProcessing2/res/values/strings.xml @@ -29,5 +29,6 @@ <string name="gamma">Gamma</string> <string name="saturation">Saturation</string> <string name="benchmark">Benchmark</string> + <string name="benchmark_all">Benchmark All</string> </resources> diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blend.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blend.java new file mode 100644 index 0000000..ac02101 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blend.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2012 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.rs.image2; + +import java.lang.Math; +import java.lang.Short; + +import android.support.v8.renderscript.*; +import android.util.Log; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.view.View; +import android.widget.Spinner; + +public class Blend extends TestBase { + private ScriptIntrinsicBlend mBlend; + private ScriptC_blend mBlendHelper; + private short image1Alpha = 128; + private short image2Alpha = 128; + + String mIntrinsicNames[]; + + private Allocation image1; + private Allocation image2; + private int currentIntrinsic = 0; + + private AdapterView.OnItemSelectedListener mIntrinsicSpinnerListener = + new AdapterView.OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + currentIntrinsic = pos; + runTest(); + act.updateDisplay(); + } + + public void onNothingSelected(AdapterView parent) { + + } + }; + + public void createTest(android.content.res.Resources res) { + mBlend = ScriptIntrinsicBlend.create(mRS, Element.U8_4(mRS)); + mBlendHelper = new ScriptC_blend(mRS); + mBlendHelper.set_alpha((short)128); + + image1 = Allocation.createTyped(mRS, mInPixelsAllocation.getType()); + image2 = Allocation.createTyped(mRS, mInPixelsAllocation2.getType()); + + mIntrinsicNames = new String[14]; + mIntrinsicNames[0] = "Source"; + mIntrinsicNames[1] = "Destination"; + mIntrinsicNames[2] = "Source Over"; + mIntrinsicNames[3] = "Destination Over"; + mIntrinsicNames[4] = "Source In"; + mIntrinsicNames[5] = "Destination In"; + mIntrinsicNames[6] = "Source Out"; + mIntrinsicNames[7] = "Destination Out"; + mIntrinsicNames[8] = "Source Atop"; + mIntrinsicNames[9] = "Destination Atop"; + mIntrinsicNames[10] = "XOR"; + mIntrinsicNames[11] = "Add"; + mIntrinsicNames[12] = "Subtract"; + mIntrinsicNames[13] = "Multiply"; + } + + public boolean onSpinner1Setup(Spinner s) { + s.setAdapter(new ArrayAdapter<String>( + act, R.layout.spinner_layout, mIntrinsicNames)); + s.setOnItemSelectedListener(mIntrinsicSpinnerListener); + return true; + } + + public boolean onBar1Setup(SeekBar b, TextView t) { + t.setText("Image 1 Alpha"); + b.setMax(255); + b.setProgress(image1Alpha); + return true; + } + + public void onBar1Changed(int progress) { + image1Alpha = (short)progress; + } + + public boolean onBar2Setup(SeekBar b, TextView t) { + t.setText("Image 2 Alpha"); + b.setMax(255); + b.setProgress(image2Alpha); + return true; + } + + public void onBar2Changed(int progress) { + image2Alpha = (short)progress; + } + + public void runTest() { + image1.copy2DRangeFrom(0, 0, mInPixelsAllocation.getType().getX(), mInPixelsAllocation.getType().getY(), mInPixelsAllocation, 0, 0); + image2.copy2DRangeFrom(0, 0, mInPixelsAllocation2.getType().getX(), mInPixelsAllocation2.getType().getY(), mInPixelsAllocation2, 0, 0); + + mBlendHelper.set_alpha(image1Alpha); + mBlendHelper.forEach_setImageAlpha(image1); + + mBlendHelper.set_alpha(image2Alpha); + mBlendHelper.forEach_setImageAlpha(image2); + + switch (currentIntrinsic) { + case 0: + mBlend.forEachSrc(image1, image2); + break; + case 1: + mBlend.forEachDst(image1, image2); + break; + case 2: + mBlend.forEachSrcOver(image1, image2); + break; + case 3: + mBlend.forEachDstOver(image1, image2); + break; + case 4: + mBlend.forEachSrcIn(image1, image2); + break; + case 5: + mBlend.forEachDstIn(image1, image2); + break; + case 6: + mBlend.forEachSrcOut(image1, image2); + break; + case 7: + mBlend.forEachDstOut(image1, image2); + break; + case 8: + mBlend.forEachSrcAtop(image1, image2); + break; + case 9: + mBlend.forEachDstAtop(image1, image2); + break; + case 10: + mBlend.forEachXor(image1, image2); + break; + case 11: + mBlend.forEachAdd(image1, image2); + break; + case 12: + mBlend.forEachSubtract(image1, image2); + break; + case 13: + mBlend.forEachMultiply(image1, image2); + break; + } + + mOutPixelsAllocation.copy2DRangeFrom(0, 0, image2.getType().getX(), image2.getType().getY(), image2, 0, 0); + } + +} diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25.java index be87716..b518b02 100644 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25.java +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Blur25.java @@ -24,37 +24,38 @@ import android.widget.SeekBar; import android.widget.TextView; public class Blur25 extends TestBase { + private boolean mUseIntrinsic = false; + private ScriptIntrinsicBlur mIntrinsic; + private int MAX_RADIUS = 25; private ScriptC_threshold mScript; - private ScriptC_vertical_blur mScriptVBlur; - private ScriptC_horizontal_blur mScriptHBlur; - private int mRadius = MAX_RADIUS; + private float mRadius = MAX_RADIUS; private float mSaturation = 1.0f; private Allocation mScratchPixelsAllocation1; private Allocation mScratchPixelsAllocation2; + public Blur25(boolean useIntrinsic) { + mUseIntrinsic = useIntrinsic; + } + public boolean onBar1Setup(SeekBar b, TextView t) { t.setText("Radius"); b.setProgress(100); return true; } - public boolean onBar2Setup(SeekBar b, TextView t) { - b.setProgress(50); - t.setText("Saturation"); - return true; - } public void onBar1Changed(int progress) { - float fRadius = progress / 100.0f; - fRadius *= (float)(MAX_RADIUS); - mRadius = (int)fRadius; - mScript.set_radius(mRadius); - } - public void onBar2Changed(int progress) { - mSaturation = (float)progress / 50.0f; - mScriptVBlur.invoke_setSaturation(mSaturation); + mRadius = ((float)progress) / 100.0f * MAX_RADIUS; + if (mRadius <= 0.10f) { + mRadius = 0.10f; + } + if (mUseIntrinsic) { + mIntrinsic.setRadius(mRadius); + } else { + mScript.invoke_setRadius((int)mRadius); + } } @@ -62,40 +63,52 @@ public class Blur25 extends TestBase { int width = mInPixelsAllocation.getType().getX(); int height = mInPixelsAllocation.getType().getY(); - Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS)); - tb.setX(width); - tb.setY(height); - mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create()); - mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create()); - - mScriptVBlur = new ScriptC_vertical_blur(mRS, res, R.raw.vertical_blur); - mScriptHBlur = new ScriptC_horizontal_blur(mRS, res, R.raw.horizontal_blur); - - mScript = new ScriptC_threshold(mRS, res, R.raw.threshold); - mScript.set_width(width); - mScript.set_height(height); - mScript.set_radius(mRadius); - - mScriptVBlur.invoke_setSaturation(mSaturation); - - mScript.bind_InPixel(mInPixelsAllocation); - mScript.bind_OutPixel(mOutPixelsAllocation); - mScript.bind_ScratchPixel1(mScratchPixelsAllocation1); - mScript.bind_ScratchPixel2(mScratchPixelsAllocation2); - - mScript.set_vBlurScript(mScriptVBlur); - mScript.set_hBlurScript(mScriptHBlur); + if (mUseIntrinsic) { + mIntrinsic = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS)); + mIntrinsic.setRadius(MAX_RADIUS); + mIntrinsic.setInput(mInPixelsAllocation); + } else { + + Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS)); + tb.setX(width); + tb.setY(height); + mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create()); + mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create()); + + mScript = new ScriptC_threshold(mRS, res, R.raw.threshold); + mScript.set_width(width); + mScript.set_height(height); + mScript.invoke_setRadius(MAX_RADIUS); + + mScript.set_InPixel(mInPixelsAllocation); + mScript.set_ScratchPixel1(mScratchPixelsAllocation1); + mScript.set_ScratchPixel2(mScratchPixelsAllocation2); + } } public void runTest() { - mScript.invoke_filter(); + if (mUseIntrinsic) { + mIntrinsic.forEach(mOutPixelsAllocation); + } else { + mScript.forEach_copyIn(mInPixelsAllocation, mScratchPixelsAllocation1); + mScript.forEach_horz(mScratchPixelsAllocation2); + mScript.forEach_vert(mOutPixelsAllocation); + } } public void setupBenchmark() { - mScript.set_radius(MAX_RADIUS); + if (mUseIntrinsic) { + mIntrinsic.setRadius(MAX_RADIUS); + } else { + mScript.invoke_setRadius(MAX_RADIUS); + } } public void exitBenchmark() { - mScript.set_radius(mRadius); + if (mUseIntrinsic) { + mIntrinsic.setRadius(mRadius); + } else { + mScript.invoke_setRadius((int)mRadius); + } } } diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ColorMatrix.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ColorMatrix.java new file mode 100644 index 0000000..3b0f86a --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ColorMatrix.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 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.rs.image2; + +import java.lang.Math; + +import android.support.v8.renderscript.*; +import android.util.Log; + +public class ColorMatrix extends TestBase { + private ScriptC_colormatrix mScript; + private ScriptIntrinsicColorMatrix mIntrinsic; + private boolean mUseIntrinsic; + private boolean mUseGrey; + + public ColorMatrix(boolean useIntrinsic, boolean useGrey) { + mUseIntrinsic = useIntrinsic; + mUseGrey = useGrey; + } + + public void createTest(android.content.res.Resources res) { + Matrix4f m = new Matrix4f(); + m.set(1, 0, 0.2f); + m.set(1, 1, 0.9f); + m.set(1, 2, 0.2f); + + if (mUseIntrinsic) { + mIntrinsic = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS)); + if (mUseGrey) { + mIntrinsic.setGreyscale(); + } else { + mIntrinsic.setColorMatrix(m); + } + } else { + mScript = new ScriptC_colormatrix(mRS, res, R.raw.colormatrix); + mScript.invoke_setMatrix(m); + } + } + + public void runTest() { + if (mUseIntrinsic) { + mIntrinsic.forEach(mInPixelsAllocation, mOutPixelsAllocation); + } else { + mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation); + } + } + +} diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Convolve3x3.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Convolve3x3.java new file mode 100644 index 0000000..7635e13 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Convolve3x3.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 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.rs.image2; + +import java.lang.Math; + +import android.support.v8.renderscript.*; +import android.util.Log; + +public class Convolve3x3 extends TestBase { + private ScriptC_convolve3x3 mScript; + private ScriptIntrinsicConvolve3x3 mIntrinsic; + + private int mWidth; + private int mHeight; + private boolean mUseIntrinsic; + + public Convolve3x3(boolean useIntrinsic) { + mUseIntrinsic = useIntrinsic; + } + + public void createTest(android.content.res.Resources res) { + mWidth = mInPixelsAllocation.getType().getX(); + mHeight = mInPixelsAllocation.getType().getY(); + + float f[] = new float[9]; + f[0] = 0.f; f[1] = -1.f; f[2] = 0.f; + f[3] = -1.f; f[4] = 5.f; f[5] = -1.f; + f[6] = 0.f; f[7] = -1.f; f[8] = 0.f; + + if (mUseIntrinsic) { + mIntrinsic = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS)); + mIntrinsic.setCoefficients(f); + mIntrinsic.setInput(mInPixelsAllocation); + } else { + mScript = new ScriptC_convolve3x3(mRS, res, R.raw.convolve3x3); + mScript.set_gCoeffs(f); + mScript.set_gIn(mInPixelsAllocation); + mScript.set_gWidth(mWidth); + mScript.set_gHeight(mHeight); + } + } + + public void runTest() { + if (mUseIntrinsic) { + mIntrinsic.forEach(mOutPixelsAllocation); + } else { + mScript.forEach_root(mOutPixelsAllocation); + } + } + +} diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Convolve5x5.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Convolve5x5.java new file mode 100644 index 0000000..d2da3c4 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Convolve5x5.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 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.rs.image2; + +import java.lang.Math; + +import android.support.v8.renderscript.*; +import android.util.Log; + +public class Convolve5x5 extends TestBase { + private ScriptC_convolve5x5 mScript; + private ScriptIntrinsicConvolve5x5 mIntrinsic; + + private int mWidth; + private int mHeight; + private boolean mUseIntrinsic; + + public Convolve5x5(boolean useIntrinsic) { + mUseIntrinsic = useIntrinsic; + } + + public void createTest(android.content.res.Resources res) { + mWidth = mInPixelsAllocation.getType().getX(); + mHeight = mInPixelsAllocation.getType().getY(); + + float f[] = new float[25]; + //f[0] = 0.012f; f[1] = 0.025f; f[2] = 0.031f; f[3] = 0.025f; f[4] = 0.012f; + //f[5] = 0.025f; f[6] = 0.057f; f[7] = 0.075f; f[8] = 0.057f; f[9] = 0.025f; + //f[10]= 0.031f; f[11]= 0.075f; f[12]= 0.095f; f[13]= 0.075f; f[14]= 0.031f; + //f[15]= 0.025f; f[16]= 0.057f; f[17]= 0.075f; f[18]= 0.057f; f[19]= 0.025f; + //f[20]= 0.012f; f[21]= 0.025f; f[22]= 0.031f; f[23]= 0.025f; f[24]= 0.012f; + + //f[0] = 1.f; f[1] = 2.f; f[2] = 0.f; f[3] = -2.f; f[4] = -1.f; + //f[5] = 4.f; f[6] = 8.f; f[7] = 0.f; f[8] = -8.f; f[9] = -4.f; + //f[10]= 6.f; f[11]=12.f; f[12]= 0.f; f[13]=-12.f; f[14]= -6.f; + //f[15]= 4.f; f[16]= 8.f; f[17]= 0.f; f[18]= -8.f; f[19]= -4.f; + //f[20]= 1.f; f[21]= 2.f; f[22]= 0.f; f[23]= -2.f; f[24]= -1.f; + + f[0] = -1.f; f[1] = -3.f; f[2] = -4.f; f[3] = -3.f; f[4] = -1.f; + f[5] = -3.f; f[6] = 0.f; f[7] = 6.f; f[8] = 0.f; f[9] = -3.f; + f[10]= -4.f; f[11]= 6.f; f[12]= 20.f; f[13]= 6.f; f[14]= -4.f; + f[15]= -3.f; f[16]= 0.f; f[17]= 6.f; f[18]= 0.f; f[19]= -3.f; + f[20]= -1.f; f[21]= -3.f; f[22]= -4.f; f[23]= -3.f; f[24]= -1.f; + + if (mUseIntrinsic) { + mIntrinsic = ScriptIntrinsicConvolve5x5.create(mRS, Element.U8_4(mRS)); + mIntrinsic.setCoefficients(f); + mIntrinsic.setInput(mInPixelsAllocation); + } else { + mScript = new ScriptC_convolve5x5(mRS, res, R.raw.convolve5x5); + mScript.set_gCoeffs(f); + mScript.set_gIn(mInPixelsAllocation); + mScript.set_gWidth(mWidth); + mScript.set_gHeight(mHeight); + } + } + + public void runTest() { + if (mUseIntrinsic) { + mIntrinsic.forEach(mOutPixelsAllocation); + } else { + mScript.forEach_root(mOutPixelsAllocation); + } + } + +} diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Copy.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Copy.java new file mode 100644 index 0000000..ef71907 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Copy.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012 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.rs.image2; + +import java.lang.Math; + +import android.support.v8.renderscript.*; +import android.util.Log; + +public class Copy extends TestBase { + private ScriptC_copy mScript; + + public void createTest(android.content.res.Resources res) { + mScript = new ScriptC_copy(mRS, res, R.raw.copy); + } + + public void runTest() { + mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation); + } + +} diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/CrossProcess.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/CrossProcess.java new file mode 100644 index 0000000..96787d7 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/CrossProcess.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 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.rs.image2; + +import java.lang.Math; + +import android.support.v8.renderscript.*; +import android.util.Log; + +public class CrossProcess extends TestBase { + private ScriptIntrinsicLUT mIntrinsic; + + public void createTest(android.content.res.Resources res) { + mIntrinsic = ScriptIntrinsicLUT.create(mRS, Element.U8_4(mRS)); + for (int ct=0; ct < 256; ct++) { + float f = ((float)ct) / 255.f; + + float r = f; + if (r < 0.5f) { + r = 4.0f * r * r * r; + } else { + r = 1.0f - r; + r = 1.0f - (4.0f * r * r * r); + } + mIntrinsic.setRed(ct, (int)(r * 255.f + 0.5f)); + + float g = f; + if (g < 0.5f) { + g = 2.0f * g * g; + } else { + g = 1.0f - g; + g = 1.0f - (2.0f * g * g); + } + mIntrinsic.setGreen(ct, (int)(g * 255.f + 0.5f)); + + float b = f * 0.5f + 0.25f; + mIntrinsic.setBlue(ct, (int)(b * 255.f + 0.5f)); + } + + } + + public void runTest() { + mIntrinsic.forEach(mInPixelsAllocation, mOutPixelsAllocation); + } + +} diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Fisheye.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Fisheye.java index 995cf9d..97beb88 100644 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Fisheye.java +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Fisheye.java @@ -23,12 +23,16 @@ import android.widget.TextView; public class Fisheye extends TestBase { private ScriptC_fisheye_full mScript_full = null; private ScriptC_fisheye_relaxed mScript_relaxed = null; + private ScriptC_fisheye_approx_full mScript_approx_full = null; + private ScriptC_fisheye_approx_relaxed mScript_approx_relaxed = null; + private final boolean approx; private final boolean relaxed; private float center_x = 0.5f; private float center_y = 0.5f; private float scale = 0.5f; - public Fisheye(boolean relaxed) { + public Fisheye(boolean approx, boolean relaxed) { + this.approx = approx; this.relaxed = relaxed; } @@ -65,7 +69,18 @@ public class Fisheye extends TestBase { } private void do_init() { - if (relaxed) + if (approx) { + if (relaxed) + mScript_approx_relaxed.invoke_init_filter( + mInPixelsAllocation.getType().getX(), + mInPixelsAllocation.getType().getY(), center_x, + center_y, scale); + else + mScript_approx_full.invoke_init_filter( + mInPixelsAllocation.getType().getX(), + mInPixelsAllocation.getType().getY(), center_x, + center_y, scale); + } else if (relaxed) mScript_relaxed.invoke_init_filter( mInPixelsAllocation.getType().getX(), mInPixelsAllocation.getType().getY(), center_x, center_y, @@ -78,7 +93,19 @@ public class Fisheye extends TestBase { } public void createTest(android.content.res.Resources res) { - if (relaxed) { + if (approx) { + if (relaxed) { + mScript_approx_relaxed = new ScriptC_fisheye_approx_relaxed(mRS, + res, R.raw.fisheye_approx_relaxed); + mScript_approx_relaxed.set_in_alloc(mInPixelsAllocation); + mScript_approx_relaxed.set_sampler(Sampler.CLAMP_LINEAR(mRS)); + } else { + mScript_approx_full = new ScriptC_fisheye_approx_full(mRS, res, + R.raw.fisheye_approx_full); + mScript_approx_full.set_in_alloc(mInPixelsAllocation); + mScript_approx_full.set_sampler(Sampler.CLAMP_LINEAR(mRS)); + } + } else if (relaxed) { mScript_relaxed = new ScriptC_fisheye_relaxed(mRS, res, R.raw.fisheye_relaxed); mScript_relaxed.set_in_alloc(mInPixelsAllocation); @@ -93,7 +120,12 @@ public class Fisheye extends TestBase { } public void runTest() { - if (relaxed) + if (approx) { + if (relaxed) + mScript_approx_relaxed.forEach_root(mOutPixelsAllocation); + else + mScript_approx_full.forEach_root(mOutPixelsAllocation); + } else if (relaxed) mScript_relaxed.forEach_root(mOutPixelsAllocation); else mScript_full.forEach_root(mOutPixelsAllocation); diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Grain.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Grain.java index e00edd7..dfd3c32 100644 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Grain.java +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Grain.java @@ -40,19 +40,40 @@ public class Grain extends TestBase { mScript.set_gNoiseStrength(s); } + private int findHighBit(int v) { + int bit = 0; + while (v > 1) { + bit++; + v >>= 1; + } + return bit; + } + + public void createTest(android.content.res.Resources res) { int width = mInPixelsAllocation.getType().getX(); int height = mInPixelsAllocation.getType().getY(); + int noiseW = findHighBit(width); + int noiseH = findHighBit(height); + if (noiseW > 9) { + noiseW = 9; + } + if (noiseH > 9) { + noiseH = 9; + } + noiseW = 1 << noiseW; + noiseH = 1 << noiseH; + Type.Builder tb = new Type.Builder(mRS, Element.U8(mRS)); - tb.setX(width); - tb.setY(height); + tb.setX(noiseW); + tb.setY(noiseH); mNoise = Allocation.createTyped(mRS, tb.create()); mNoise2 = Allocation.createTyped(mRS, tb.create()); mScript = new ScriptC_grain(mRS, res, R.raw.grain); - mScript.set_gWidth(width); - mScript.set_gHeight(height); + mScript.set_gWMask(noiseW - 1); + mScript.set_gHMask(noiseH - 1); mScript.set_gNoiseStrength(0.5f); mScript.set_gBlendSource(mNoise); mScript.set_gNoise(mNoise2); diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/GroupTest.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/GroupTest.java index b9fbb59..a7ceebe 100644 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/GroupTest.java +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/GroupTest.java @@ -22,8 +22,8 @@ import android.support.v8.renderscript.*; import android.util.Log; public class GroupTest extends TestBase { - private ScriptC_convolve3x3 mConvolve; - private ScriptC_colormatrix mMatrix; + private ScriptIntrinsicConvolve3x3 mConvolve; + private ScriptIntrinsicColorMatrix mMatrix; private Allocation mScratchPixelsAllocation1; private ScriptGroup mGroup; @@ -41,20 +41,20 @@ public class GroupTest extends TestBase { mWidth = mInPixelsAllocation.getType().getX(); mHeight = mInPixelsAllocation.getType().getY(); - mConvolve = new ScriptC_convolve3x3(mRS, res, R.raw.convolve3x3); - mMatrix = new ScriptC_colormatrix(mRS, res, R.raw.colormatrix); + mConvolve = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS)); + mMatrix = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS)); float f[] = new float[9]; f[0] = 0.f; f[1] = -1.f; f[2] = 0.f; f[3] = -1.f; f[4] = 5.f; f[5] = -1.f; f[6] = 0.f; f[7] = -1.f; f[8] = 0.f; - mConvolve.set_gCoeffs(f); + mConvolve.setCoefficients(f); Matrix4f m = new Matrix4f(); m.set(1, 0, 0.2f); m.set(1, 1, 0.9f); m.set(1, 2, 0.2f); - mMatrix.invoke_setMatrix(m); + mMatrix.setColorMatrix(m); Type.Builder tb = new Type.Builder(mRS, Element.U8_4(mRS)); tb.setX(mWidth); @@ -63,24 +63,23 @@ public class GroupTest extends TestBase { if (mUseNative) { ScriptGroup.Builder b = new ScriptGroup.Builder(mRS); - b.addConnection(connect, mConvolve, mMatrix, null); + b.addKernel(mConvolve.getKernelID()); + b.addKernel(mMatrix.getKernelID()); + b.addConnection(connect, mConvolve.getKernelID(), mMatrix.getKernelID()); mGroup = b.create(); - } else { mScratchPixelsAllocation1 = Allocation.createTyped(mRS, connect); } } public void runTest() { - mConvolve.set_gIn(mInPixelsAllocation); - mConvolve.set_gWidth(mWidth); - mConvolve.set_gHeight(mHeight); + mConvolve.setInput(mInPixelsAllocation); if (mUseNative) { - mGroup.setOutput(mMatrix, mOutPixelsAllocation); + mGroup.setOutput(mMatrix.getKernelID(), mOutPixelsAllocation); mGroup.execute(); } else { - mConvolve.forEach_root(mScratchPixelsAllocation1); - mMatrix.forEach_root(mScratchPixelsAllocation1, mOutPixelsAllocation); + mConvolve.forEach(mScratchPixelsAllocation1); + mMatrix.forEach(mScratchPixelsAllocation1, mOutPixelsAllocation); } } diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java index 9b36da14..227518f 100644 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ImageProcessingActivity2.java @@ -34,13 +34,27 @@ import android.view.View; import android.util.Log; import java.lang.Math; +import android.os.Environment; +import android.app.Instrumentation; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + public class ImageProcessingActivity2 extends Activity implements SeekBar.OnSeekBarChangeListener { private final String TAG = "Img"; + private final String RESULT_FILE = "image_processing_result.csv"; + Bitmap mBitmapIn; + Bitmap mBitmapIn2; Bitmap mBitmapOut; String mTestNames[]; + private Spinner mSpinner; private SeekBar mBar1; private SeekBar mBar2; private SeekBar mBar3; @@ -64,6 +78,10 @@ public class ImageProcessingActivity2 extends Activity private TestBase mTest; + public void updateDisplay() { + mTest.updateBitmap(mBitmapOut); + mDisplayView.invalidate(); + } public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { @@ -81,8 +99,7 @@ public class ImageProcessingActivity2 extends Activity } mTest.runTest(); - mTest.updateBitmap(mBitmapOut); - mDisplayView.invalidate(); + updateDisplay(); } } @@ -93,6 +110,9 @@ public class ImageProcessingActivity2 extends Activity } void setupBars() { + mSpinner.setVisibility(View.VISIBLE); + mTest.onSpinner1Setup(mSpinner); + mBar1.setVisibility(View.VISIBLE); mText1.setVisibility(View.VISIBLE); mTest.onBar1Setup(mBar1, mText1); @@ -116,6 +136,9 @@ public class ImageProcessingActivity2 extends Activity void changeTest(int testID) { + if (mTest != null) { + mTest.destroy(); + } switch(testID) { case 0: mTest = new LevelsV4(false, false); @@ -130,58 +153,122 @@ public class ImageProcessingActivity2 extends Activity mTest = new LevelsV4(true, true); break; case 4: - mTest = new Blur25(); + mTest = new Blur25(false); break; case 5: - mTest = new Greyscale(); + mTest = new Blur25(true); break; case 6: - mTest = new Grain(); + mTest = new Greyscale(); break; case 7: - mTest = new Fisheye(false); + mTest = new Grain(); break; case 8: - mTest = new Fisheye(true); + mTest = new Fisheye(false, false); break; case 9: - mTest = new Vignette(false); + mTest = new Fisheye(false, true); break; case 10: - mTest = new Vignette(true); + mTest = new Fisheye(true, false); break; case 11: - mTest = new GroupTest(false); + mTest = new Fisheye(true, true); break; case 12: + mTest = new Vignette(false, false); + break; + case 13: + mTest = new Vignette(false, true); + break; + case 14: + mTest = new Vignette(true, false); + break; + case 15: + mTest = new Vignette(true, true); + break; + case 16: + mTest = new GroupTest(false); + break; + case 17: mTest = new GroupTest(true); break; + case 18: + mTest = new Convolve3x3(false); + break; + case 19: + mTest = new Convolve3x3(true); + break; + case 20: + mTest = new ColorMatrix(false, false); + break; + case 21: + mTest = new ColorMatrix(true, false); + break; + case 22: + mTest = new ColorMatrix(true, true); + break; + case 23: + mTest = new Copy(); + break; + case 24: + mTest = new CrossProcess(); + break; + case 25: + mTest = new Convolve5x5(false); + break; + case 26: + mTest = new Convolve5x5(true); + break; + case 27: + mTest = new Mandelbrot(); + break; + case 28: + mTest = new Blend(); + break; } - mTest.createBaseTest(this, mBitmapIn); + mTest.createBaseTest(this, mBitmapIn, mBitmapIn2); setupBars(); mTest.runTest(); - mTest.updateBitmap(mBitmapOut); - mDisplayView.invalidate(); + updateDisplay(); mBenchmarkResult.setText("Result: not run"); } void setupTests() { - mTestNames = new String[13]; + mTestNames = new String[29]; mTestNames[0] = "Levels Vec3 Relaxed"; mTestNames[1] = "Levels Vec4 Relaxed"; mTestNames[2] = "Levels Vec3 Full"; mTestNames[3] = "Levels Vec4 Full"; mTestNames[4] = "Blur radius 25"; - mTestNames[5] = "Greyscale"; - mTestNames[6] = "Grain"; - mTestNames[7] = "Fisheye Full"; - mTestNames[8] = "Fisheye Relaxed"; - mTestNames[9] = "Vignette Full"; - mTestNames[10] = "Vignette Relaxed"; - mTestNames[11] = "Group Test (emulated)"; - mTestNames[12] = "Group Test (native)"; + mTestNames[5] = "Intrinsic Blur radius 25"; + mTestNames[6] = "Greyscale"; + mTestNames[7] = "Grain"; + mTestNames[8] = "Fisheye Full"; + mTestNames[9] = "Fisheye Relaxed"; + mTestNames[10] = "Fisheye Approximate Full"; + mTestNames[11] = "Fisheye Approximate Relaxed"; + mTestNames[12] = "Vignette Full"; + mTestNames[13] = "Vignette Relaxed"; + mTestNames[14] = "Vignette Approximate Full"; + mTestNames[15] = "Vignette Approximate Relaxed"; + mTestNames[16] = "Group Test (emulated)"; + mTestNames[17] = "Group Test (native)"; + mTestNames[18] = "Convolve 3x3"; + mTestNames[19] = "Intrinsics Convolve 3x3"; + mTestNames[20] = "ColorMatrix"; + mTestNames[21] = "Intrinsics ColorMatrix"; + mTestNames[22] = "Intrinsics ColorMatrix Grey"; + mTestNames[23] = "Copy"; + mTestNames[24] = "CrossProcess (using LUT)"; + mTestNames[25] = "Convolve 5x5"; + mTestNames[26] = "Intrinsics Convolve 5x5"; + mTestNames[27] = "Mandelbrot"; + mTestNames[28] = "Intrinsics Blend"; + mTestSpinner.setAdapter(new ArrayAdapter<String>( this, R.layout.spinner_layout, mTestNames)); } @@ -202,14 +289,17 @@ public class ImageProcessingActivity2 extends Activity super.onCreate(savedInstanceState); setContentView(R.layout.main); - mBitmapIn = loadBitmap(R.drawable.city); - mBitmapOut = loadBitmap(R.drawable.city); + mBitmapIn = loadBitmap(R.drawable.img1600x1067); + mBitmapIn2 = loadBitmap(R.drawable.img1600x1067b); + mBitmapOut = loadBitmap(R.drawable.img1600x1067); mSurfaceView = (SurfaceView) findViewById(R.id.surface); mDisplayView = (ImageView) findViewById(R.id.display); mDisplayView.setImageBitmap(mBitmapOut); + mSpinner = (Spinner) findViewById(R.id.spinner1); + mBar1 = (SeekBar) findViewById(R.id.slider1); mBar2 = (SeekBar) findViewById(R.id.slider2); mBar3 = (SeekBar) findViewById(R.id.slider3); @@ -255,37 +345,69 @@ public class ImageProcessingActivity2 extends Activity // button hook public void benchmark(View v) { - long t = getBenchmark(); + float t = getBenchmark(); //long javaTime = javaFilter(); //mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms"); mBenchmarkResult.setText("Result: " + t + " ms"); + Log.v(TAG, "getBenchmark: Renderscript frame time core ms " + t); + } + + public void benchmark_all(View v) { + // write result into a file + File externalStorage = Environment.getExternalStorageDirectory(); + if (!externalStorage.canWrite()) { + Log.v(TAG, "sdcard is not writable"); + return; + } + File resultFile = new File(externalStorage, RESULT_FILE); + //resultFile.setWritable(true, false); + try { + BufferedWriter rsWriter = new BufferedWriter(new FileWriter(resultFile)); + Log.v(TAG, "Saved results in: " + resultFile.getAbsolutePath()); + for (int i = 0; i < mTestNames.length; i++ ) { + changeTest(i); + float t = getBenchmark(); + String s = new String("" + mTestNames[i] + ", " + t); + rsWriter.write(s + "\n"); + Log.v(TAG, "Test " + s + "ms\n"); + } + rsWriter.close(); + } catch (IOException e) { + Log.v(TAG, "Unable to write result file " + e.getMessage()); + } + changeTest(0); } // For benchmark test - public long getBenchmark() { + public float getBenchmark() { mDoingBenchmark = true; mTest.setupBenchmark(); long result = 0; - Log.v(TAG, "Warming"); - long t = java.lang.System.currentTimeMillis() + 2000; + //Log.v(TAG, "Warming"); + long t = java.lang.System.currentTimeMillis() + 250; do { mTest.runTest(); mTest.finish(); } while (t > java.lang.System.currentTimeMillis()); - Log.v(TAG, "Benchmarking"); + //Log.v(TAG, "Benchmarking"); + int ct = 0; t = java.lang.System.currentTimeMillis(); - mTest.runTest(); - mTest.finish(); + do { + mTest.runTest(); + mTest.finish(); + ct++; + } while ((t+1000) > java.lang.System.currentTimeMillis()); t = java.lang.System.currentTimeMillis() - t; + float ft = (float)t; + ft /= ct; - Log.v(TAG, "getBenchmark: Renderscript frame time core ms " + t); mTest.exitBenchmark(); mDoingBenchmark = false; - return t; + return ft; } } diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Mandelbrot.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Mandelbrot.java new file mode 100644 index 0000000..556d797 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Mandelbrot.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012 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.rs.image2; + +import java.lang.Math; + +import android.support.v8.renderscript.*; +import android.util.Log; +import android.widget.SeekBar; +import android.widget.TextView; + +public class Mandelbrot extends TestBase { + private ScriptC_mandelbrot mScript; + + public boolean onBar1Setup(SeekBar b, TextView t) { + t.setText("Iterations"); + b.setProgress(0); + return true; + } + + public void onBar1Changed(int progress) { + int iters = progress * 3 + 50; + mScript.set_gMaxIteration(iters); + } + + public boolean onBar2Setup(SeekBar b, TextView t) { + t.setText("Lower Bound: X"); + b.setProgress(0); + return true; + } + + public void onBar2Changed(int progress) { + float scaleFactor = mScript.get_scaleFactor(); + // allow viewport to be moved by 2x scale factor + float lowerBoundX = -2.f + ((progress / scaleFactor) / 50.f); + mScript.set_lowerBoundX(lowerBoundX); + } + + public boolean onBar3Setup(SeekBar b, TextView t) { + t.setText("Lower Bound: Y"); + b.setProgress(0); + return true; + } + + public void onBar3Changed(int progress) { + float scaleFactor = mScript.get_scaleFactor(); + // allow viewport to be moved by 2x scale factor + float lowerBoundY = -2.f + ((progress / scaleFactor) / 50.f); + mScript.set_lowerBoundY(lowerBoundY); + } + + public boolean onBar4Setup(SeekBar b, TextView t) { + t.setText("Scale Factor"); + b.setProgress(0); + return true; + } + + public void onBar4Changed(int progress) { + float scaleFactor = 4.f - (3.96f * (progress / 100.f)); + mScript.set_scaleFactor(scaleFactor); + } + + public void createTest(android.content.res.Resources res) { + int width = mOutPixelsAllocation.getType().getX(); + int height = mOutPixelsAllocation.getType().getY(); + + mScript = new ScriptC_mandelbrot(mRS, res, R.raw.mandelbrot); + mScript.set_gDimX(width); + mScript.set_gDimY(height); + mScript.set_gMaxIteration(50); + } + + public void runTest() { + mScript.forEach_root(mOutPixelsAllocation); + mRS.finish(); + } + +} + diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java index 35170af..9df2eff 100644 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/TestBase.java @@ -31,14 +31,18 @@ import android.widget.TextView; import android.view.View; import android.util.Log; import java.lang.Math; +import android.widget.Spinner; public class TestBase { protected final String TAG = "Img"; protected RenderScript mRS; protected Allocation mInPixelsAllocation; + protected Allocation mInPixelsAllocation2; protected Allocation mOutPixelsAllocation; + protected ImageProcessingActivity2 act; + // Override to use UI elements public void onBar1Changed(int progress) { } @@ -79,11 +83,20 @@ public class TestBase { return false; } - public final void createBaseTest(ImageProcessingActivity2 act, Bitmap b) { + public boolean onSpinner1Setup(Spinner s) { + s.setVisibility(View.INVISIBLE); + return false; + } + + public final void createBaseTest(ImageProcessingActivity2 ipact, Bitmap b, Bitmap b2) { + act = ipact; mRS = RenderScript.create(act); mInPixelsAllocation = Allocation.createFromBitmap(mRS, b, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); + mInPixelsAllocation2 = Allocation.createFromBitmap(mRS, b2, + Allocation.MipmapControl.MIPMAP_NONE, + Allocation.USAGE_SCRIPT); mOutPixelsAllocation = Allocation.createFromBitmap(mRS, b, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); @@ -103,6 +116,10 @@ public class TestBase { mRS.finish(); } + public void destroy() { + mRS.destroy(); + } + public void updateBitmap(Bitmap b) { mOutPixelsAllocation.copyTo(b); } diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java index fc69eba..8618d5a 100644 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/Vignette.java @@ -23,6 +23,9 @@ import android.widget.TextView; public class Vignette extends TestBase { private ScriptC_vignette_full mScript_full = null; private ScriptC_vignette_relaxed mScript_relaxed = null; + private ScriptC_vignette_approx_full mScript_approx_full = null; + private ScriptC_vignette_approx_relaxed mScript_approx_relaxed = null; + private final boolean approx; private final boolean relaxed; private float center_x = 0.5f; private float center_y = 0.5f; @@ -30,7 +33,8 @@ public class Vignette extends TestBase { private float shade = 0.5f; private float slope = 20.0f; - public Vignette(boolean relaxed) { + public Vignette(boolean approx, boolean relaxed) { + this.approx = approx; this.relaxed = relaxed; } @@ -87,7 +91,18 @@ public class Vignette extends TestBase { } private void do_init() { - if (relaxed) + if (approx) { + if (relaxed) + mScript_approx_relaxed.invoke_init_vignette( + mInPixelsAllocation.getType().getX(), + mInPixelsAllocation.getType().getY(), center_x, + center_y, scale, shade, slope); + else + mScript_approx_full.invoke_init_vignette( + mInPixelsAllocation.getType().getX(), + mInPixelsAllocation.getType().getY(), center_x, + center_y, scale, shade, slope); + } else if (relaxed) mScript_relaxed.invoke_init_vignette( mInPixelsAllocation.getType().getX(), mInPixelsAllocation.getType().getY(), center_x, center_y, @@ -100,21 +115,36 @@ public class Vignette extends TestBase { } public void createTest(android.content.res.Resources res) { - if (relaxed) { + if (approx) { + if (relaxed) + mScript_approx_relaxed = new ScriptC_vignette_approx_relaxed( + mRS, res, R.raw.vignette_approx_relaxed); + else + mScript_approx_full = new ScriptC_vignette_approx_full( + mRS, res, R.raw.vignette_approx_full); + } else if (relaxed) mScript_relaxed = new ScriptC_vignette_relaxed(mRS, res, R.raw.vignette_relaxed); - } else { + else mScript_full = new ScriptC_vignette_full(mRS, res, R.raw.vignette_full); - } do_init(); } public void runTest() { - if (relaxed) - mScript_relaxed.forEach_root(mInPixelsAllocation, mOutPixelsAllocation); + if (approx) { + if (relaxed) + mScript_approx_relaxed.forEach_root(mInPixelsAllocation, + mOutPixelsAllocation); + else + mScript_approx_full.forEach_root(mInPixelsAllocation, + mOutPixelsAllocation); + } else if (relaxed) + mScript_relaxed.forEach_root(mInPixelsAllocation, + mOutPixelsAllocation); else - mScript_full.forEach_root(mInPixelsAllocation, mOutPixelsAllocation); + mScript_full.forEach_root(mInPixelsAllocation, + mOutPixelsAllocation); } } diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/blend.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/blend.rs new file mode 100644 index 0000000..4d90725 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/blend.rs @@ -0,0 +1,24 @@ +// 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. + +#pragma version(1) +#pragma rs java_package_name(com.android.rs.image2) + +uchar alpha = 0x0; + +void setImageAlpha(uchar4 *v_out, uint32_t x, uint32_t y) { + v_out->rgba = convert_uchar4((convert_uint4(v_out->rgba) * alpha) >> (uint4)8); + v_out->a = alpha; +} + diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.rs new file mode 100644 index 0000000..b110b88 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/convolve5x5.rs @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 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. + */ + +#pragma version(1) +#pragma rs java_package_name(com.android.rs.image2) +#pragma rs_fp_relaxed + +int32_t gWidth; +int32_t gHeight; +rs_allocation gIn; + +float gCoeffs[25]; + +void root(uchar4 *out, uint32_t x, uint32_t y) { + uint32_t x0 = max((int32_t)x-2, 0); + uint32_t x1 = max((int32_t)x-1, 0); + uint32_t x2 = x; + uint32_t x3 = min((int32_t)x+1, gWidth-1); + uint32_t x4 = min((int32_t)x+2, gWidth-1); + + uint32_t y0 = max((int32_t)y-2, 0); + uint32_t y1 = max((int32_t)y-1, 0); + uint32_t y2 = y; + uint32_t y3 = min((int32_t)y+1, gHeight-1); + uint32_t y4 = min((int32_t)y+2, gHeight-1); + + float4 p0 = convert_float4(rsGetElementAt_uchar4(gIn, x0, y0)) * gCoeffs[0] + + convert_float4(rsGetElementAt_uchar4(gIn, x1, y0)) * gCoeffs[1] + + convert_float4(rsGetElementAt_uchar4(gIn, x2, y0)) * gCoeffs[2] + + convert_float4(rsGetElementAt_uchar4(gIn, x3, y0)) * gCoeffs[3] + + convert_float4(rsGetElementAt_uchar4(gIn, x4, y0)) * gCoeffs[4]; + + float4 p1 = convert_float4(rsGetElementAt_uchar4(gIn, x0, y1)) * gCoeffs[5] + + convert_float4(rsGetElementAt_uchar4(gIn, x1, y1)) * gCoeffs[6] + + convert_float4(rsGetElementAt_uchar4(gIn, x2, y1)) * gCoeffs[7] + + convert_float4(rsGetElementAt_uchar4(gIn, x3, y1)) * gCoeffs[8] + + convert_float4(rsGetElementAt_uchar4(gIn, x4, y1)) * gCoeffs[9]; + + float4 p2 = convert_float4(rsGetElementAt_uchar4(gIn, x0, y2)) * gCoeffs[10] + + convert_float4(rsGetElementAt_uchar4(gIn, x1, y2)) * gCoeffs[11] + + convert_float4(rsGetElementAt_uchar4(gIn, x2, y2)) * gCoeffs[12] + + convert_float4(rsGetElementAt_uchar4(gIn, x3, y2)) * gCoeffs[13] + + convert_float4(rsGetElementAt_uchar4(gIn, x4, y2)) * gCoeffs[14]; + + float4 p3 = convert_float4(rsGetElementAt_uchar4(gIn, x0, y3)) * gCoeffs[15] + + convert_float4(rsGetElementAt_uchar4(gIn, x1, y3)) * gCoeffs[16] + + convert_float4(rsGetElementAt_uchar4(gIn, x2, y3)) * gCoeffs[17] + + convert_float4(rsGetElementAt_uchar4(gIn, x3, y3)) * gCoeffs[18] + + convert_float4(rsGetElementAt_uchar4(gIn, x4, y3)) * gCoeffs[19]; + + float4 p4 = convert_float4(rsGetElementAt_uchar4(gIn, x0, y4)) * gCoeffs[20] + + convert_float4(rsGetElementAt_uchar4(gIn, x1, y4)) * gCoeffs[21] + + convert_float4(rsGetElementAt_uchar4(gIn, x2, y4)) * gCoeffs[22] + + convert_float4(rsGetElementAt_uchar4(gIn, x3, y4)) * gCoeffs[23] + + convert_float4(rsGetElementAt_uchar4(gIn, x4, y4)) * gCoeffs[24]; + + p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f); + *out = convert_uchar4(p0); +} + + diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/copy.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/copy.rs new file mode 100644 index 0000000..31e4241 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/copy.rs @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 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. + */ + +#pragma version(1) +#pragma rs java_package_name(com.android.rs.image2) + +void root(const uchar4 *v_in, uchar4 *v_out) { + *v_out = *v_in; +} + + diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh index 4dcfc1d..3809912 100644 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye.rsh @@ -17,46 +17,41 @@ rs_allocation in_alloc; rs_sampler sampler; -static float2 center, dimensions; -static float2 scale; -static float alpha; -static float radius2; -static float factor; - -void init_filter(uint32_t dim_x, uint32_t dim_y, float focus_x, float focus_y, float k) { - center.x = focus_x; - center.y = focus_y; - dimensions.x = (float)dim_x; - dimensions.y = (float)dim_y; +static float2 center, neg_center, inv_dimensions, axis_scale; +static float alpha, radius2, factor; +void init_filter(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y, float k) { + center.x = center_x; + center.y = center_y; + neg_center = -center; + inv_dimensions.x = 1.f / (float)dim_x; + inv_dimensions.y = 1.f / (float)dim_y; alpha = k * 2.0 + 0.75; - float bound2 = 0.25; - if (dim_x > dim_y) { - scale.x = 1.0; - scale.y = dimensions.y / dimensions.x; - bound2 *= (scale.y*scale.y + 1); - } else { - scale.x = dimensions.x / dimensions.y; - scale.y = 1.0; - bound2 *= (scale.x*scale.x + 1); - } + + axis_scale = (float2)1.f; + if (dim_x > dim_y) + axis_scale.y = (float)dim_y / (float)dim_x; + else + axis_scale.x = (float)dim_x / (float)dim_y; + + const float bound2 = 0.25 * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y); const float bound = sqrt(bound2); const float radius = 1.15 * bound; radius2 = radius*radius; - const float max_radian = 0.5f * M_PI - atan(alpha / bound * sqrt(radius2 - bound2)); + const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2)); factor = bound / max_radian; } void root(uchar4 *out, uint32_t x, uint32_t y) { // Convert x and y to floating point coordinates with center as origin - float2 coord; - coord.x = (float)x / dimensions.x; - coord.y = (float)y / dimensions.y; - coord -= center; - const float dist = length(scale * coord); - const float radian = M_PI_2 - atan((alpha * sqrt(radius2 - dist * dist)) / dist); - const float scalar = radian * factor / dist; - const float2 new_coord = coord * scalar + center; + const float2 inCoord = {(float)x, (float)y}; + const float2 coord = mad(inCoord, inv_dimensions, neg_center); + const float2 scaledCoord = axis_scale * coord; + const float dist2 = scaledCoord.x*scaledCoord.x + scaledCoord.y*scaledCoord.y; + const float inv_dist = rsqrt(dist2); + const float radian = M_PI_2 - atan((alpha * sqrt(radius2 - dist2)) * inv_dist); + const float scalar = radian * factor * inv_dist; + const float2 new_coord = mad(coord, scalar, center); const float4 fout = rsSample(in_alloc, sampler, new_coord); *out = rsPackColorTo8888(fout); } diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx.rsh new file mode 100644 index 0000000..08b4126 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx.rsh @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 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. + */ + +rs_allocation in_alloc; +rs_sampler sampler; + +static float2 center, neg_center, inv_dimensions, axis_scale; +static float alpha, radius2, factor; + +void init_filter(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y, float k) { + center.x = center_x; + center.y = center_y; + neg_center = -center; + inv_dimensions.x = 1.f / (float)dim_x; + inv_dimensions.y = 1.f / (float)dim_y; + alpha = k * 2.0 + 0.75; + + axis_scale = (float2)1.f; + if (dim_x > dim_y) + axis_scale.y = (float)dim_y / (float)dim_x; + else + axis_scale.x = (float)dim_x / (float)dim_y; + + const float bound2 = 0.25 * (axis_scale.x*axis_scale.x + axis_scale.y*axis_scale.y); + const float bound = sqrt(bound2); + const float radius = 1.15 * bound; + radius2 = radius*radius; + const float max_radian = M_PI_2 - atan(alpha / bound * sqrt(radius2 - bound2)); + factor = bound / max_radian; +} + +void root(uchar4 *out, uint32_t x, uint32_t y) { + // Convert x and y to floating point coordinates with center as origin + const float2 inCoord = {(float)x, (float)y}; + const float2 coord = mad(inCoord, inv_dimensions, neg_center); + const float2 scaledCoord = axis_scale * coord; + const float dist2 = scaledCoord.x*scaledCoord.x + scaledCoord.y*scaledCoord.y; + const float inv_dist = half_rsqrt(dist2); + const float radian = M_PI_2 - atan((alpha * half_sqrt(radius2 - dist2)) * inv_dist); + const float scalar = radian * factor * inv_dist; + const float2 new_coord = mad(coord, scalar, center); + const float4 fout = rsSample(in_alloc, sampler, new_coord); + *out = rsPackColorTo8888(fout); +} + diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_full.rs new file mode 100644 index 0000000..cce42f9 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_full.rs @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2012 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. + */ + +#pragma version(1) +#pragma rs java_package_name(com.android.rs.image2) + +#include "fisheye_approx.rsh" + diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs new file mode 100644 index 0000000..64d27ed --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/fisheye_approx_relaxed.rs @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 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. + */ + +#pragma version(1) +#pragma rs java_package_name(com.android.rs.image2) +#pragma rs_fp_relaxed + +#include "fisheye_approx.rsh" + diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs index 75f4021..44320a5 100644 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/grain.rs @@ -38,25 +38,25 @@ void genRand(uchar *out) { * 1 2 1 */ -int32_t gWidth; -int32_t gHeight; +int32_t gWMask; +int32_t gHMask; rs_allocation gBlendSource; void blend9(uchar *out, uint32_t x, uint32_t y) { - uint32_t x1 = min(x+1, (uint32_t)gWidth); - uint32_t x2 = max(x-1, (uint32_t)0); - uint32_t y1 = min(y+1, (uint32_t)gHeight); - uint32_t y2 = max(y-1, (uint32_t)0); + uint32_t x1 = (x-1) & gWMask; + uint32_t x2 = (x+1) & gWMask; + uint32_t y1 = (y-1) & gHMask; + uint32_t y2 = (y+1) & gHMask; - uint p00 = 56 * ((uchar *)rsGetElementAt(gBlendSource, x1, y1))[0]; - uint p01 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x, y1))[0]; - uint p02 = 56 * ((uchar *)rsGetElementAt(gBlendSource, x2, y1))[0]; - uint p10 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x1, y))[0]; - uint p11 = 230 * ((uchar *)rsGetElementAt(gBlendSource, x, y))[0]; - uint p12 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x2, y))[0]; - uint p20 = 56 * ((uchar *)rsGetElementAt(gBlendSource, x1, y2))[0]; - uint p21 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x, y2))[0]; - uint p22 = 56 * ((uchar *)rsGetElementAt(gBlendSource, x2, y2))[0]; + uint p00 = 56 * rsGetElementAt_uchar(gBlendSource, x1, y1); + uint p01 = 114 * rsGetElementAt_uchar(gBlendSource, x, y1); + uint p02 = 56 * rsGetElementAt_uchar(gBlendSource, x2, y1); + uint p10 = 114 * rsGetElementAt_uchar(gBlendSource, x1, y); + uint p11 = 230 * rsGetElementAt_uchar(gBlendSource, x, y); + uint p12 = 114 * rsGetElementAt_uchar(gBlendSource, x2, y); + uint p20 = 56 * rsGetElementAt_uchar(gBlendSource, x1, y2); + uint p21 = 114 * rsGetElementAt_uchar(gBlendSource, x, y2); + uint p22 = 56 * rsGetElementAt_uchar(gBlendSource, x2, y2); p00 += p01; p02 += p10; @@ -69,7 +69,8 @@ void blend9(uchar *out, uint32_t x, uint32_t y) { p20 += p22; p20 += p02; - *out = (uchar)(p20 >> 10); + p20 = min(p20 >> 10, (uint)255); + *out = (uchar)p20; } float gNoiseStrength; @@ -77,7 +78,7 @@ float gNoiseStrength; rs_allocation gNoise; void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) { float4 ip = convert_float4(*in); - float pnoise = (float) ((uchar *)rsGetElementAt(gNoise, x, y))[0]; + float pnoise = (float) rsGetElementAt_uchar(gNoise, x & gWMask, y & gHMask); float energy_level = ip.r + ip.g + ip.b; float energy_mask = (28.f - sqrt(energy_level)) * 0.03571f; diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/horizontal_blur.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/horizontal_blur.rs deleted file mode 100644 index ee83496..0000000 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/horizontal_blur.rs +++ /dev/null @@ -1,28 +0,0 @@ -#pragma version(1) -#pragma rs_fp_relaxed - -#include "ip.rsh" - -void root(float4 *out, const void *usrData, uint32_t x, uint32_t y) { - const FilterStruct *fs = (const FilterStruct *)usrData; - float3 blurredPixel = 0; - const float *gPtr = fs->gaussian; - if ((x > fs->radius) && (x < (fs->width - fs->radius))) { - for (int r = -fs->radius; r <= fs->radius; r ++) { - const float4 *i = (const float4 *)rsGetElementAt(fs->ain, x + r, y); - blurredPixel += i->xyz * gPtr[0]; - gPtr++; - } - } else { - for (int r = -fs->radius; r <= fs->radius; r ++) { - // Stepping left and right away from the pixel - int validX = rsClamp((int)x + r, (int)0, (int)(fs->width - 1)); - const float4 *i = (const float4 *)rsGetElementAt(fs->ain, validX, y); - blurredPixel += i->xyz * gPtr[0]; - gPtr++; - } - } - - out->xyz = blurredPixel; -} - diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh deleted file mode 100644 index 0cdf9e1..0000000 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/ip.rsh +++ /dev/null @@ -1,15 +0,0 @@ -#pragma rs java_package_name(com.android.rs.image2) - -#define MAX_RADIUS 25 - -typedef struct FilterStruct_s { - rs_allocation ain; - - float *gaussian; //[MAX_RADIUS * 2 + 1]; - int height; - int width; - int radius; - -} FilterStruct; - - diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/mandelbrot.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/mandelbrot.rs new file mode 100644 index 0000000..55e5304 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/mandelbrot.rs @@ -0,0 +1,56 @@ +// 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. + +#pragma version(1) +#pragma rs java_package_name(com.android.rs.image2) + +uint32_t gMaxIteration = 500; +uint32_t gDimX = 1024; +uint32_t gDimY = 1024; + +float lowerBoundX = -2.f; +float lowerBoundY = -2.f; +float scaleFactor = 4.f; + +void root(uchar4 *v_out, uint32_t x, uint32_t y) { + float2 p; + p.x = lowerBoundX + ((float)x / gDimX) * scaleFactor; + p.y = lowerBoundY + ((float)y / gDimY) * scaleFactor; + + float2 t = 0; + float2 t2 = t * t; + int iter = 0; + while((t2.x + t2.y < 4.f) && (iter < gMaxIteration)) { + float xtemp = t2.x - t2.y + p.x; + t.y = 2 * t.x * t.y + p.y; + t.x = xtemp; + iter++; + t2 = t * t; + } + + if(iter >= gMaxIteration) { + // write a non-transparent black pixel + *v_out = (uchar4){0, 0, 0, 0xff}; + } else { + float mi3 = gMaxIteration / 3.; + if (iter <= (gMaxIteration / 3)) + *v_out = (uchar4){0xff * (iter / mi3), 0, 0, 0xff}; + else if (iter <= (((gMaxIteration / 3) * 2))) + *v_out = (uchar4){0xff - (0xff * ((iter - mi3) / mi3)), + (0xff * ((iter - mi3) / mi3)), 0, 0xff}; + else + *v_out = (uchar4){0, 0xff - (0xff * ((iter - (mi3 * 2)) / mi3)), + (0xff * ((iter - (mi3 * 2)) / mi3)), 0xff}; + } +} diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs index 77cd5be..9ef4898 100644 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/threshold.rs @@ -1,26 +1,23 @@ #pragma version(1) +#pragma rs java_package_name(com.android.rs.image2) +#pragma rs_fp_relaxed -#include "ip.rsh" int height; int width; -int radius; +static int radius; -uchar4 * InPixel; -uchar4 * OutPixel; -float4 * ScratchPixel1; -float4 * ScratchPixel2; +rs_allocation InPixel; +rs_allocation ScratchPixel1; +rs_allocation ScratchPixel2; -rs_script vBlurScript; -rs_script hBlurScript; - -const int CMD_FINISHED = 1; +const int MAX_RADIUS = 25; // Store our coefficients here static float gaussian[MAX_RADIUS * 2 + 1]; - -static void computeGaussianWeights() { +void setRadius(int rad) { + radius = rad; // Compute gaussian weights for the blur // e is the euler's number float e = 2.718281828459045f; @@ -45,8 +42,7 @@ static void computeGaussianWeights() { float normalizeFactor = 0.0f; float floatR = 0.0f; - int r; - for (r = -radius; r <= radius; r ++) { + for (int r = -radius; r <= radius; r ++) { floatR = (float)r; gaussian[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); normalizeFactor += gaussian[r + radius]; @@ -54,40 +50,57 @@ static void computeGaussianWeights() { //Now we need to normalize the weights because all our coefficients need to add up to one normalizeFactor = 1.0f / normalizeFactor; - for (r = -radius; r <= radius; r ++) { + for (int r = -radius; r <= radius; r ++) { floatR = (float)r; gaussian[r + radius] *= normalizeFactor; } } +void copyIn(const uchar4 *in, float4 *out) { + *out = convert_float4(*in); +} -static void copyInput() { - rs_allocation ain; - ain = rsGetAllocation(InPixel); - uint32_t dimx = rsAllocationGetDimX(ain); - uint32_t dimy = rsAllocationGetDimY(ain); - for (uint32_t y = 0; y < dimy; y++) { - for (uint32_t x = 0; x < dimx; x++) { - ScratchPixel1[x + y * dimx] = convert_float4(InPixel[x + y * dimx]); +void vert(uchar4 *out, uint32_t x, uint32_t y) { + float3 blurredPixel = 0; + const float *gPtr = gaussian; + if ((y > radius) && (y < (height - radius))) { + for (int r = -radius; r <= radius; r ++) { + const float4 *i = (const float4 *)rsGetElementAt(ScratchPixel2, x, y + r); + blurredPixel += i->xyz * gPtr[0]; + gPtr++; + } + } else { + for (int r = -radius; r <= radius; r ++) { + int validH = rsClamp((int)y + r, (int)0, (int)(height - 1)); + const float4 *i = (const float4 *)rsGetElementAt(ScratchPixel2, x, validH); + blurredPixel += i->xyz * gPtr[0]; + gPtr++; } } -} -void filter() { - copyInput(); - computeGaussianWeights(); - - FilterStruct fs; - fs.gaussian = gaussian; - fs.width = width; - fs.height = height; - fs.radius = radius; + out->xyz = convert_uchar3(clamp(blurredPixel, 0.f, 255.f)); + out->w = 0xff; +} - fs.ain = rsGetAllocation(ScratchPixel1); - rsForEach(hBlurScript, fs.ain, rsGetAllocation(ScratchPixel2), &fs, sizeof(fs)); +void horz(float4 *out, uint32_t x, uint32_t y) { + float3 blurredPixel = 0; + const float *gPtr = gaussian; + if ((x > radius) && (x < (width - radius))) { + for (int r = -radius; r <= radius; r ++) { + const float4 *i = (const float4 *)rsGetElementAt(ScratchPixel1, x + r, y); + blurredPixel += i->xyz * gPtr[0]; + gPtr++; + } + } else { + for (int r = -radius; r <= radius; r ++) { + // Stepping left and right away from the pixel + int validX = rsClamp((int)x + r, (int)0, (int)(width - 1)); + const float4 *i = (const float4 *)rsGetElementAt(ScratchPixel1, validX, y); + blurredPixel += i->xyz * gPtr[0]; + gPtr++; + } + } - fs.ain = rsGetAllocation(ScratchPixel2); - rsForEach(vBlurScript, fs.ain, rsGetAllocation(OutPixel), &fs, sizeof(fs)); - //rsSendToClientBlocking(CMD_FINISHED); + out->xyz = blurredPixel; } diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vertical_blur.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vertical_blur.rs deleted file mode 100644 index 60fd71b..0000000 --- a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vertical_blur.rs +++ /dev/null @@ -1,59 +0,0 @@ -#pragma version(1) -#pragma rs_fp_relaxed - -#include "ip.rsh" - -static float saturation; -static rs_matrix3x3 colorMat; - -void setSaturation(float sat) { - saturation = sat; - - // Saturation - // Linear weights - //float rWeight = 0.3086f; - //float gWeight = 0.6094f; - //float bWeight = 0.0820f; - - // Gamma 2.2 weights (we haven't converted our image to linear space yet for perf reasons) - float rWeight = 0.299f; - float gWeight = 0.587f; - float bWeight = 0.114f; - - float oneMinusS = 1.0f - saturation; - rsMatrixSet(&colorMat, 0, 0, oneMinusS * rWeight + saturation); - rsMatrixSet(&colorMat, 0, 1, oneMinusS * rWeight); - rsMatrixSet(&colorMat, 0, 2, oneMinusS * rWeight); - rsMatrixSet(&colorMat, 1, 0, oneMinusS * gWeight); - rsMatrixSet(&colorMat, 1, 1, oneMinusS * gWeight + saturation); - rsMatrixSet(&colorMat, 1, 2, oneMinusS * gWeight); - rsMatrixSet(&colorMat, 2, 0, oneMinusS * bWeight); - rsMatrixSet(&colorMat, 2, 1, oneMinusS * bWeight); - rsMatrixSet(&colorMat, 2, 2, oneMinusS * bWeight + saturation); -} - -void root(uchar4 *out, const void *usrData, uint32_t x, uint32_t y) { - const FilterStruct *fs = (const FilterStruct *)usrData; - float3 blurredPixel = 0; - const float *gPtr = fs->gaussian; - if ((y > fs->radius) && (y < (fs->height - fs->radius))) { - for (int r = -fs->radius; r <= fs->radius; r ++) { - const float4 *i = (const float4 *)rsGetElementAt(fs->ain, x, y + r); - blurredPixel += i->xyz * gPtr[0]; - gPtr++; - } - } else { - for (int r = -fs->radius; r <= fs->radius; r ++) { - int validH = rsClamp((int)y + r, (int)0, (int)(fs->height - 1)); - const float4 *i = (const float4 *)rsGetElementAt(fs->ain, x, validH); - blurredPixel += i->xyz * gPtr[0]; - gPtr++; - } - } - - float3 temp = rsMatrixMultiply(&colorMat, blurredPixel); - temp = clamp(temp, 0.f, 255.f); - out->xyz = convert_uchar3(temp); - out->w = 0xff; -} - diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx.rsh b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx.rsh new file mode 100644 index 0000000..7f7bdcf --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx.rsh @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 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. + */ + +static float2 neg_center, axis_scale, inv_dimensions; +static float sloped_neg_range, sloped_inv_max_dist, shade, opp_shade; + +void init_vignette(uint32_t dim_x, uint32_t dim_y, float center_x, float center_y, + float desired_scale, float desired_shade, float desired_slope) { + + neg_center.x = -center_x; + neg_center.y = -center_y; + inv_dimensions.x = 1.f / (float)dim_x; + inv_dimensions.y = 1.f / (float)dim_y; + + axis_scale = (float2)1.f; + if (dim_x > dim_y) + axis_scale.y = (float)dim_y / (float)dim_x; + else + axis_scale.x = (float)dim_x / (float)dim_y; + + const float max_dist = 0.5 * length(axis_scale); + sloped_inv_max_dist = desired_slope * 1.f/max_dist; + + // Range needs to be between 1.3 to 0.6. When scale is zero then range is + // 1.3 which means no vignette at all because the luminousity difference is + // less than 1/256. Expect input scale to be between 0.0 and 1.0. + const float neg_range = 0.7*sqrt(desired_scale) - 1.3; + sloped_neg_range = exp(neg_range * desired_slope); + + shade = desired_shade; + opp_shade = 1.f - desired_shade; +} + +void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) { + // Convert x and y to floating point coordinates with center as origin + const float4 fin = convert_float4(*in); + const float2 inCoord = {(float)x, (float)y}; + const float2 coord = mad(inCoord, inv_dimensions, neg_center); + const float sloped_dist_ratio = fast_length(axis_scale * coord) * sloped_inv_max_dist; + // TODO: add half_exp once implemented + const float lumen = opp_shade + shade * half_recip(1.f + sloped_neg_range * exp(sloped_dist_ratio)); + float4 fout; + fout.rgb = fin.rgb * lumen; + fout.w = fin.w; + *out = convert_uchar4(fout); +} + diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_full.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_full.rs new file mode 100644 index 0000000..3612509 --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_full.rs @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2012 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. + */ + +#pragma version(1) +#pragma rs java_package_name(com.android.rs.image2) + +#include "vignette_approx.rsh" + diff --git a/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs new file mode 100644 index 0000000..b714e9b --- /dev/null +++ b/tests/RenderScriptTests/ImageProcessing2/src/com/android/rs/image/vignette_approx_relaxed.rs @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 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. + */ + +#pragma version(1) +#pragma rs java_package_name(com.android.rs.image2) +#pragma rs_fp_relaxed + +#include "vignette_approx.rsh" + |