Merge "Patch to support MIFARE with SAK 0x01"
diff --git a/Android.mk b/Android.mk
index 15a4196..f65f9e4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -117,6 +117,7 @@
core/java/android/net/INetworkPolicyListener.aidl \
core/java/android/net/INetworkPolicyManager.aidl \
core/java/android/net/INetworkStatsService.aidl \
+ core/java/android/net/nsd/INsdManager.aidl \
core/java/android/nfc/INdefPushCallback.aidl \
core/java/android/nfc/INfcAdapter.aidl \
core/java/android/nfc/INfcAdapterExtras.aidl \
@@ -441,6 +442,8 @@
resources/samples/HoneycombGallery "Honeycomb Gallery" \
-samplecode $(sample_dir)/JetBoy \
resources/samples/JetBoy "JetBoy" \
+ -samplecode $(sample_dir)/KeyChainDemo \
+ resources/samples/KeyChainDemo "KeyChain Demo" \
-samplecode $(sample_dir)/LunarLander \
resources/samples/LunarLander "Lunar Lander" \
-samplecode $(sample_dir)/training/ads-and-ux \
diff --git a/NOTICE b/NOTICE
index 152be20..4e69963 100644
--- a/NOTICE
+++ b/NOTICE
@@ -56,6 +56,17 @@
=========================================================================
== NOTICE file corresponding to the section 4 d of ==
== the Apache License, Version 2.0, ==
+ == in this case for the mDnsResponder code. ==
+ =========================================================================
+
+mDnsResponder TXTRecord
+This file is Copyright 2004 Apple Computer, Inc. but released under
+the Apache2 License.
+
+
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
== in this case for the TagSoup code. ==
=========================================================================
diff --git a/api/current.txt b/api/current.txt
index 233938b5..d8461e7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -596,7 +596,7 @@
field public static final int layerType = 16843604; // 0x1010354
field public static final int layout = 16842994; // 0x10100f2
field public static final int layoutAnimation = 16842988; // 0x10100ec
- field public static final int layoutDirection = 16843689; // 0x10103a9
+ field public static final int layoutDirection = 16843691; // 0x10103ab
field public static final int layout_above = 16843140; // 0x1010184
field public static final int layout_alignBaseline = 16843142; // 0x1010186
field public static final int layout_alignBottom = 16843146; // 0x101018a
@@ -618,10 +618,10 @@
field public static final int layout_height = 16842997; // 0x10100f5
field public static final int layout_margin = 16842998; // 0x10100f6
field public static final int layout_marginBottom = 16843002; // 0x10100fa
- field public static final int layout_marginEnd = 16843693; // 0x10103ad
+ field public static final int layout_marginEnd = 16843695; // 0x10103af
field public static final int layout_marginLeft = 16842999; // 0x10100f7
field public static final int layout_marginRight = 16843001; // 0x10100f9
- field public static final int layout_marginStart = 16843692; // 0x10103ac
+ field public static final int layout_marginStart = 16843694; // 0x10103ae
field public static final int layout_marginTop = 16843000; // 0x10100f8
field public static final int layout_row = 16843643; // 0x101037b
field public static final int layout_rowSpan = 16843644; // 0x101037c
@@ -717,10 +717,10 @@
field public static final int packageNames = 16843649; // 0x1010381
field public static final int padding = 16842965; // 0x10100d5
field public static final int paddingBottom = 16842969; // 0x10100d9
- field public static final int paddingEnd = 16843691; // 0x10103ab
+ field public static final int paddingEnd = 16843693; // 0x10103ad
field public static final int paddingLeft = 16842966; // 0x10100d6
field public static final int paddingRight = 16842968; // 0x10100d8
- field public static final int paddingStart = 16843690; // 0x10103aa
+ field public static final int paddingStart = 16843692; // 0x10103ac
field public static final int paddingTop = 16842967; // 0x10100d7
field public static final int panelBackground = 16842846; // 0x101005e
field public static final int panelColorBackground = 16842849; // 0x1010061
@@ -931,6 +931,7 @@
field public static final int summaryColumn = 16843426; // 0x10102a2
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
+ field public static final int supportsRtl = 16843688; // 0x10103a8
field public static final int supportsUploading = 16843419; // 0x101029b
field public static final int switchMinWidth = 16843632; // 0x1010370
field public static final int switchPadding = 16843633; // 0x1010371
@@ -961,6 +962,7 @@
field public static final int tension = 16843370; // 0x101026a
field public static final int testOnly = 16843378; // 0x1010272
field public static final int text = 16843087; // 0x101014f
+ field public static final int textAlignment = 16843690; // 0x10103aa
field public static final int textAllCaps = 16843660; // 0x101038c
field public static final int textAppearance = 16842804; // 0x1010034
field public static final int textAppearanceButton = 16843271; // 0x1010207
@@ -1000,7 +1002,7 @@
field public static final int textColorTertiary = 16843282; // 0x1010212
field public static final int textColorTertiaryInverse = 16843283; // 0x1010213
field public static final int textCursorDrawable = 16843618; // 0x1010362
- field public static final int textDirection = 16843688; // 0x10103a8
+ field public static final int textDirection = 16843689; // 0x10103a9
field public static final int textEditNoPasteWindowLayout = 16843541; // 0x1010315
field public static final int textEditPasteWindowLayout = 16843540; // 0x1010314
field public static final int textEditSideNoPasteWindowLayout = 16843615; // 0x101035f
@@ -6131,6 +6133,7 @@
field public static final int FLAG_STOPPED = 2097152; // 0x200000
field public static final int FLAG_SUPPORTS_LARGE_SCREENS = 2048; // 0x800
field public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1024; // 0x400
+ field public static final int FLAG_SUPPORTS_RTL = 4194304; // 0x400000
field public static final int FLAG_SUPPORTS_SCREEN_DENSITIES = 8192; // 0x2000
field public static final int FLAG_SUPPORTS_SMALL_SCREENS = 512; // 0x200
field public static final int FLAG_SUPPORTS_XLARGE_SCREENS = 524288; // 0x80000
@@ -12897,6 +12900,7 @@
method public byte[] getHistoricalBytes();
method public int getMaxTransceiveLength();
method public int getTimeout();
+ method public boolean isExtendedLengthApduSupported();
method public void setTimeout(int);
method public byte[] transceive(byte[]) throws java.io.IOException;
}
@@ -22490,6 +22494,7 @@
field public static final int KEYCODE_DPAD_UP = 19; // 0x13
field public static final int KEYCODE_DVR = 173; // 0xad
field public static final int KEYCODE_E = 33; // 0x21
+ field public static final int KEYCODE_EISU = 212; // 0xd4
field public static final int KEYCODE_ENDCALL = 6; // 0x6
field public static final int KEYCODE_ENTER = 66; // 0x42
field public static final int KEYCODE_ENVELOPE = 65; // 0x41
@@ -22518,12 +22523,15 @@
field public static final int KEYCODE_GUIDE = 172; // 0xac
field public static final int KEYCODE_H = 36; // 0x24
field public static final int KEYCODE_HEADSETHOOK = 79; // 0x4f
+ field public static final int KEYCODE_HENKAN = 214; // 0xd6
field public static final int KEYCODE_HOME = 3; // 0x3
field public static final int KEYCODE_I = 37; // 0x25
field public static final int KEYCODE_INFO = 165; // 0xa5
field public static final int KEYCODE_INSERT = 124; // 0x7c
field public static final int KEYCODE_J = 38; // 0x26
field public static final int KEYCODE_K = 39; // 0x27
+ field public static final int KEYCODE_KANA = 218; // 0xda
+ field public static final int KEYCODE_KATAKANA_HIRAGANA = 215; // 0xd7
field public static final int KEYCODE_L = 40; // 0x28
field public static final int KEYCODE_LANGUAGE_SWITCH = 204; // 0xcc
field public static final int KEYCODE_LEFT_BRACKET = 71; // 0x47
@@ -22546,6 +22554,7 @@
field public static final int KEYCODE_MINUS = 69; // 0x45
field public static final int KEYCODE_MOVE_END = 123; // 0x7b
field public static final int KEYCODE_MOVE_HOME = 122; // 0x7a
+ field public static final int KEYCODE_MUHENKAN = 213; // 0xd5
field public static final int KEYCODE_MUSIC = 209; // 0xd1
field public static final int KEYCODE_MUTE = 91; // 0x5b
field public static final int KEYCODE_N = 42; // 0x2a
@@ -22588,6 +22597,7 @@
field public static final int KEYCODE_Q = 45; // 0x2d
field public static final int KEYCODE_R = 46; // 0x2e
field public static final int KEYCODE_RIGHT_BRACKET = 72; // 0x48
+ field public static final int KEYCODE_RO = 217; // 0xd9
field public static final int KEYCODE_S = 47; // 0x2f
field public static final int KEYCODE_SCROLL_LOCK = 116; // 0x74
field public static final int KEYCODE_SEARCH = 84; // 0x54
@@ -22620,7 +22630,9 @@
field public static final int KEYCODE_WINDOW = 171; // 0xab
field public static final int KEYCODE_X = 52; // 0x34
field public static final int KEYCODE_Y = 53; // 0x35
+ field public static final int KEYCODE_YEN = 216; // 0xd8
field public static final int KEYCODE_Z = 54; // 0x36
+ field public static final int KEYCODE_ZENKAKU_HANKAKU = 211; // 0xd3
field public static final int KEYCODE_ZOOM_IN = 168; // 0xa8
field public static final int KEYCODE_ZOOM_OUT = 169; // 0xa9
field public static final deprecated int MAX_KEYCODE = 84; // 0x54
@@ -23199,6 +23211,7 @@
method public void buildLayer();
method public boolean callOnClick();
method public boolean canResolveLayoutDirection();
+ method public boolean canResolveTextAlignment();
method public boolean canResolveTextDirection();
method public boolean canScrollHorizontally(int);
method public boolean canScrollVertically(int);
@@ -23240,6 +23253,7 @@
method public boolean dispatchUnhandledMove(android.view.View, int);
method protected void dispatchVisibilityChanged(android.view.View, int);
method public void dispatchWindowFocusChanged(boolean);
+ method public void dispatchWindowSystemUiVisiblityChanged(int);
method public void dispatchWindowVisibilityChanged(int);
method public void draw(android.graphics.Canvas);
method protected void drawableStateChanged();
@@ -23300,6 +23314,8 @@
method public final int getMeasuredState();
method public final int getMeasuredWidth();
method public final int getMeasuredWidthAndState();
+ method public int getMinimumHeight();
+ method public int getMinimumWidth();
method public int getNextFocusDownId();
method public int getNextFocusForwardId();
method public int getNextFocusLeftId();
@@ -23318,6 +23334,7 @@
method public float getPivotY();
method public int getResolvedLayoutDirection();
method public int getResolvedLayoutDirection(android.graphics.drawable.Drawable);
+ method public int getResolvedTextAlignment();
method public int getResolvedTextDirection();
method public android.content.res.Resources getResources();
method public final int getRight();
@@ -23329,6 +23346,9 @@
method public float getRotationY();
method public float getScaleX();
method public float getScaleY();
+ method public int getScrollBarDefaultDelayBeforeFade();
+ method public int getScrollBarFadeDuration();
+ method public int getScrollBarSize();
method public int getScrollBarStyle();
method public final int getScrollX();
method public final int getScrollY();
@@ -23338,6 +23358,7 @@
method public int getSystemUiVisibility();
method public java.lang.Object getTag();
method public java.lang.Object getTag(int);
+ method public int getTextAlignment();
method public int getTextDirection();
method public final int getTop();
method protected float getTopFadingEdgeStrength();
@@ -23353,6 +23374,7 @@
method public int getVisibility();
method public final int getWidth();
method protected int getWindowAttachCount();
+ method public int getWindowSystemUiVisibility();
method public android.os.IBinder getWindowToken();
method public int getWindowVisibility();
method public void getWindowVisibleDisplayFrame(android.graphics.Rect);
@@ -23361,6 +23383,7 @@
method public boolean hasFocus();
method public boolean hasFocusable();
method public boolean hasOnClickListeners();
+ method public boolean hasOverlappingRendering();
method public boolean hasTransientState();
method public boolean hasWindowFocus();
method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
@@ -23396,6 +23419,7 @@
method public boolean isPressed();
method public boolean isSaveEnabled();
method public boolean isSaveFromParentEnabled();
+ method public boolean isScrollContainer();
method public boolean isScrollbarFadingEnabled();
method public boolean isSelected();
method public boolean isShown();
@@ -23443,6 +23467,8 @@
method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onResolvedLayoutDirectionChanged();
method public void onResolvedLayoutDirectionReset();
+ method public void onResolvedTextAlignmentChanged();
+ method public void onResolvedTextAlignmentReset();
method public void onResolvedTextDirectionChanged();
method public void onResolvedTextDirectionReset();
method protected void onRestoreInstanceState(android.os.Parcelable);
@@ -23456,6 +23482,7 @@
method public boolean onTrackballEvent(android.view.MotionEvent);
method protected void onVisibilityChanged(android.view.View, int);
method public void onWindowFocusChanged(boolean);
+ method public void onWindowSystemUiVisibilityChanged(int);
method protected void onWindowVisibilityChanged(int);
method protected boolean overScrollBy(int, int, int, int, int, int, int, int, boolean);
method public boolean performClick();
@@ -23473,6 +23500,7 @@
method public boolean removeCallbacks(java.lang.Runnable);
method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
+ method public void requestFitSystemWindows();
method public final boolean requestFocus();
method public final boolean requestFocus(int);
method public boolean requestFocus(int, android.graphics.Rect);
@@ -23481,11 +23509,13 @@
method public boolean requestRectangleOnScreen(android.graphics.Rect);
method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
method public void resetResolvedLayoutDirection();
+ method public void resetResolvedTextAlignment();
method public void resetResolvedTextDirection();
method public void resolveLayoutDirection();
method public void resolvePadding();
method public static int resolveSize(int, int);
method public static int resolveSizeAndState(int, int, int);
+ method public void resolveTextAlignment();
method public void resolveTextDirection();
method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable>);
@@ -23498,8 +23528,9 @@
method public void setActivated(boolean);
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
+ method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
- method public void setBackgroundDrawable(android.graphics.drawable.Drawable);
+ method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
method public void setBackgroundResource(int);
method public final void setBottom(int);
method public void setCameraDistance(float);
@@ -23559,6 +23590,9 @@
method public void setSaveFromParentEnabled(boolean);
method public void setScaleX(float);
method public void setScaleY(float);
+ method public void setScrollBarDefaultDelayBeforeFade(int);
+ method public void setScrollBarFadeDuration(int);
+ method public void setScrollBarSize(int);
method public void setScrollBarStyle(int);
method public void setScrollContainer(boolean);
method public void setScrollX(int);
@@ -23569,6 +23603,7 @@
method public void setSystemUiVisibility(int);
method public void setTag(java.lang.Object);
method public void setTag(int, java.lang.Object);
+ method public void setTextAlignment(int);
method public void setTextDirection(int);
method public final void setTop(int);
method public void setTouchDelegate(android.view.TouchDelegate);
@@ -23671,9 +23706,23 @@
field public static final int SOUND_EFFECTS_ENABLED = 134217728; // 0x8000000
field public static final deprecated int STATUS_BAR_HIDDEN = 1; // 0x1
field public static final deprecated int STATUS_BAR_VISIBLE = 0; // 0x0
+ field public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4
field public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
+ field public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
+ field public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
+ field public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
+ field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
+ field public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4
+ field protected static int TEXT_ALIGNMENT_DEFAULT;
+ field public static final int TEXT_ALIGNMENT_GRAVITY = 1; // 0x1
+ field public static final int TEXT_ALIGNMENT_INHERIT = 0; // 0x0
+ field public static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT = 131072; // 0x20000
+ field public static final int TEXT_ALIGNMENT_TEXT_END = 3; // 0x3
+ field public static final int TEXT_ALIGNMENT_TEXT_START = 2; // 0x2
+ field public static final int TEXT_ALIGNMENT_VIEW_END = 6; // 0x6
+ field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
field protected static int TEXT_DIRECTION_DEFAULT;
field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
@@ -24037,6 +24086,7 @@
method public abstract void requestChildFocus(android.view.View, android.view.View);
method public abstract boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
method public abstract void requestDisallowInterceptTouchEvent(boolean);
+ method public abstract void requestFitSystemWindows();
method public abstract void requestLayout();
method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public abstract void requestTransparentRegion(android.view.View);
@@ -24084,9 +24134,11 @@
ctor public ViewStub(android.content.Context, android.util.AttributeSet);
ctor public ViewStub(android.content.Context, android.util.AttributeSet, int);
method public int getInflatedId();
+ method public android.view.LayoutInflater getLayoutInflater();
method public int getLayoutResource();
method public android.view.View inflate();
method public void setInflatedId(int);
+ method public void setLayoutInflater(android.view.LayoutInflater);
method public void setLayoutResource(int);
method public void setOnInflateListener(android.view.ViewStub.OnInflateListener);
}
@@ -24096,15 +24148,18 @@
}
public final class ViewTreeObserver {
+ method public void addOnDrawListener(android.view.ViewTreeObserver.OnDrawListener);
method public void addOnGlobalFocusChangeListener(android.view.ViewTreeObserver.OnGlobalFocusChangeListener);
method public void addOnGlobalLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
method public void addOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener);
method public void addOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener);
method public void addOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
+ method public final void dispatchOnDraw();
method public final void dispatchOnGlobalLayout();
method public final boolean dispatchOnPreDraw();
method public boolean isAlive();
method public deprecated void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
+ method public void removeOnDrawListener(android.view.ViewTreeObserver.OnDrawListener);
method public void removeOnGlobalFocusChangeListener(android.view.ViewTreeObserver.OnGlobalFocusChangeListener);
method public void removeOnGlobalLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
method public void removeOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener);
@@ -24112,6 +24167,10 @@
method public void removeOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
}
+ public static abstract interface ViewTreeObserver.OnDrawListener {
+ method public abstract void onDraw();
+ }
+
public static abstract interface ViewTreeObserver.OnGlobalFocusChangeListener {
method public abstract void onGlobalFocusChanged(android.view.View, android.view.View);
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 9ebbe03..ddd7f7c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -37,6 +37,14 @@
* etc. Such a service can optionally request the capability for querying the content
* of the active window. Development of an accessibility service requires extending this
* class and implementing its abstract methods.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about creating AccessibilityServices, read the
+ * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
+ * developer guide.</p>
+ * </div>
+ *
* <h3>Lifecycle</h3>
* <p>
* The lifecycle of an accessibility service is managed exclusively by the system and
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index b55fda4..8e53431 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -42,6 +42,13 @@
* {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
* according to the information encapsulated in this class.
*
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about creating AccessibilityServices, read the
+ * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
+ * developer guide.</p>
+ * </div>
+ *
* @see AccessibilityService
* @see android.view.accessibility.AccessibilityEvent
* @see android.view.accessibility.AccessibilityManager
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 24d3a6b..cff16ff 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -611,6 +611,10 @@
* If the window hosting the ActionBar does not have the feature
* {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
* content to fit the new space available.
+ *
+ * <p>If you are hiding the ActionBar through
+ * {@link View#SYSTEM_UI_FLAG_FULLSCREEN View.SYSTEM_UI_FLAG_FULLSCREEN},
+ * you should not call this function directly.
*/
public abstract void show();
@@ -619,6 +623,12 @@
* If the window hosting the ActionBar does not have the feature
* {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
* content to fit the new space available.
+ *
+ * <p>Instead of calling this function directly, you can also cause an
+ * ActionBar using the overlay feature to hide through
+ * {@link View#SYSTEM_UI_FLAG_FULLSCREEN View.SYSTEM_UI_FLAG_FULLSCREEN}.
+ * Hiding the ActionBar through this system UI flag allows you to more
+ * seamlessly hide it in conjunction with other screen decorations.
*/
public abstract void hide();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ab4e73d..98c4e10 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -174,8 +174,10 @@
static final ThreadLocal<ActivityThread> sThreadLocal = new ThreadLocal<ActivityThread>();
Instrumentation mInstrumentation;
String mInstrumentationAppDir = null;
+ String mInstrumentationAppLibraryDir = null;
String mInstrumentationAppPackage = null;
String mInstrumentedAppDir = null;
+ String mInstrumentedAppLibraryDir = null;
boolean mSystemThread = false;
boolean mJitEnabled = false;
@@ -3936,8 +3938,10 @@
}
mInstrumentationAppDir = ii.sourceDir;
+ mInstrumentationAppLibraryDir = ii.nativeLibraryDir;
mInstrumentationAppPackage = ii.packageName;
mInstrumentedAppDir = data.info.getAppDir();
+ mInstrumentedAppLibraryDir = data.info.getLibDir();
ApplicationInfo instrApp = new ApplicationInfo();
instrApp.packageName = ii.packageName;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index d758ecae..5ffceb3 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -59,6 +59,8 @@
import android.net.ThrottleManager;
import android.net.IThrottleManager;
import android.net.Uri;
+import android.net.nsd.INsdManager;
+import android.net.nsd.NsdManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiManager;
import android.net.wifi.p2p.IWifiP2pManager;
@@ -372,6 +374,14 @@
ctx.mMainThread.getHandler());
}});
+ registerService(NSD_SERVICE, new ServiceFetcher() {
+ @Override
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(NSD_SERVICE);
+ INsdManager service = INsdManager.Stub.asInterface(b);
+ return new NsdManager(service);
+ }});
+
// Note: this was previously cached in a static variable, but
// constructed using mMainThread.getHandler(), so converting
// it to be a regular Context-cached service...
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index bf8fde0..c62e5cf 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -92,8 +92,7 @@
mCallBack = callBack;
Context themeContext = getContext();
- setButton(BUTTON_POSITIVE, themeContext.getText(R.string.date_time_set), this);
- setButton(BUTTON_NEGATIVE, themeContext.getText(R.string.cancel), (OnClickListener) null);
+ setButton(BUTTON_POSITIVE, themeContext.getText(R.string.date_time_done), this);
setIcon(0);
setTitle(R.string.date_picker_dialog_title);
@@ -106,11 +105,7 @@
}
public void onClick(DialogInterface dialog, int which) {
- if (mCallBack != null) {
- mDatePicker.clearFocus();
- mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
- mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
- }
+ tryNotifyDateSet();
}
public void onDateChanged(DatePicker view, int year,
@@ -138,6 +133,20 @@
mDatePicker.updateDate(year, monthOfYear, dayOfMonth);
}
+ private void tryNotifyDateSet() {
+ if (mCallBack != null) {
+ mDatePicker.clearFocus();
+ mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
+ mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ tryNotifyDateSet();
+ super.onStop();
+ }
+
@Override
public Bundle onSaveInstanceState() {
Bundle state = super.onSaveInstanceState();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 5340fbb..8ab1ed6 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -261,6 +261,7 @@
if (mIncludeCode && !mPackageName.equals("android")) {
String zip = mAppDir;
+ String libraryPath = mLibDir;
/*
* The following is a bit of a hack to inject
@@ -273,15 +274,20 @@
String instrumentationAppDir =
mActivityThread.mInstrumentationAppDir;
+ String instrumentationAppLibraryDir =
+ mActivityThread.mInstrumentationAppLibraryDir;
String instrumentationAppPackage =
mActivityThread.mInstrumentationAppPackage;
String instrumentedAppDir =
mActivityThread.mInstrumentedAppDir;
+ String instrumentedAppLibraryDir =
+ mActivityThread.mInstrumentedAppLibraryDir;
String[] instrumentationLibs = null;
if (mAppDir.equals(instrumentationAppDir)
|| mAppDir.equals(instrumentedAppDir)) {
zip = instrumentationAppDir + ":" + instrumentedAppDir;
+ libraryPath = instrumentationAppLibraryDir + ":" + instrumentedAppLibraryDir;
if (! instrumentedAppDir.equals(instrumentationAppDir)) {
instrumentationLibs =
getLibrariesFor(instrumentationAppPackage);
@@ -301,7 +307,7 @@
*/
if (ActivityThread.localLOGV)
- Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + mLibDir);
+ Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + libraryPath);
// Temporarily disable logging of disk reads on the Looper thread
// as this is early and necessary.
@@ -309,7 +315,7 @@
mClassLoader =
ApplicationLoaders.getDefault().getClassLoader(
- zip, mLibDir, mBaseClassLoader);
+ zip, libraryPath, mBaseClassLoader);
initializeJavaContextClassLoader();
StrictMode.setThreadPolicy(oldPolicy);
@@ -442,6 +448,10 @@
return mAppDir;
}
+ public String getLibDir() {
+ return mLibDir;
+ }
+
public String getResDir() {
return mResDir;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 1356801..04c64a0 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -26,8 +26,10 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.text.TextUtils;
import android.util.IntProperty;
+import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.RemoteViews;
@@ -807,8 +809,9 @@
@Deprecated
public void setLatestEventInfo(Context context,
CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
+ // TODO: rewrite this to use Builder
RemoteViews contentView = new RemoteViews(context.getPackageName(),
- R.layout.status_bar_latest_event_content);
+ R.layout.notification_template_base);
if (this.icon != 0) {
contentView.setImageViewResource(R.id.icon, this.icon);
}
@@ -819,6 +822,7 @@
contentView.setTextViewText(R.id.text, contentText);
}
if (this.when != 0) {
+ contentView.setViewVisibility(R.id.time, View.VISIBLE);
contentView.setLong(R.id.time, "setTime", when);
}
@@ -941,6 +945,7 @@
private ArrayList<Action> mActions = new ArrayList<Action>(3);
private boolean mCanHasIntruder;
private boolean mIntruderActionsShowText;
+ private boolean mUseChronometer;
/**
* Constructs a new Builder with the defaults:
@@ -982,6 +987,18 @@
}
/**
+ * @hide
+ *
+ * Show the {@link Notification#when} field as a countdown (or count-up) timer instead of a timestamp.
+ *
+ * @see Notification#when
+ */
+ public Builder setUsesChronometer(boolean b) {
+ mUseChronometer = b;
+ return this;
+ }
+
+ /**
* Set the small icon resource, which will be used to represent the notification in the
* status bar.
*
@@ -1433,17 +1450,42 @@
}
}
if (mWhen != 0) {
- contentView.setLong(R.id.time, "setTime", mWhen);
+ if (mUseChronometer) {
+ contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
+ contentView.setLong(R.id.chronometer, "setBase",
+ mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
+ contentView.setBoolean(R.id.chronometer, "setStarted", true);
+ } else {
+ contentView.setViewVisibility(R.id.time, View.VISIBLE);
+ contentView.setLong(R.id.time, "setTime", mWhen);
+ }
}
contentView.setViewVisibility(R.id.line3, hasLine3 ? View.VISIBLE : View.GONE);
return contentView;
}
+ private RemoteViews applyStandardTemplateWithActions(int layoutId) {
+ RemoteViews big = applyStandardTemplate(layoutId);
+
+ int N = mActions.size();
+ if (N > 0) {
+ Log.d("Notification", "has actions: " + mContentText);
+ big.setViewVisibility(R.id.actions, View.VISIBLE);
+ if (N>3) N=3;
+ for (int i=0; i<N; i++) {
+ final RemoteViews button = generateActionButton(mActions.get(i));
+ Log.d("Notification", "adding action " + i + ": " + mActions.get(i).title);
+ big.addView(R.id.actions, button);
+ }
+ }
+ return big;
+ }
+
private RemoteViews makeContentView() {
if (mContentView != null) {
return mContentView;
} else {
- return applyStandardTemplate(R.layout.status_bar_latest_event_content); // no more special large_icon flavor
+ return applyStandardTemplate(R.layout.notification_template_base); // no more special large_icon flavor
}
}
@@ -1461,6 +1503,12 @@
}
}
+ private RemoteViews makeBigContentView() {
+ if (mActions.size() == 0) return null;
+
+ return applyStandardTemplateWithActions(R.layout.notification_template_base);
+ }
+
private RemoteViews makeIntruderView(boolean showLabels) {
RemoteViews intruderView = new RemoteViews(mContext.getPackageName(),
R.layout.notification_intruder_content);
@@ -1500,6 +1548,15 @@
return intruderView;
}
+ private RemoteViews generateActionButton(Action action) {
+ RemoteViews button = new RemoteViews(mContext.getPackageName(), R.layout.notification_action);
+ button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0);
+ button.setTextViewText(R.id.action0, action.title);
+ button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
+ button.setContentDescription(R.id.action0, action.title);
+ return button;
+ }
+
/**
* Combine all of the options that have been set and return a new {@link Notification}
* object.
@@ -1528,6 +1585,7 @@
if (mCanHasIntruder) {
n.intruderView = makeIntruderView(mIntruderActionsShowText);
}
+ n.bigContentView = makeBigContentView();
if (mLedOnMs != 0 && mLedOffMs != 0) {
n.flags |= FLAG_SHOW_LIGHTS;
}
@@ -1583,7 +1641,7 @@
}
private RemoteViews makeBigContentView() {
- RemoteViews contentView = mBuilder.applyStandardTemplate(R.layout.notification_template_big_picture);
+ RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(R.layout.notification_template_big_picture);
contentView.setImageViewBitmap(R.id.big_picture, mPicture);
@@ -1630,7 +1688,7 @@
}
private RemoteViews makeBigContentView() {
- RemoteViews contentView = mBuilder.applyStandardTemplate(R.layout.status_bar_latest_event_content);
+ RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(R.layout.notification_template_base);
contentView.setTextViewText(R.id.big_text, mBigText);
contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 353b415..d773bc8 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -96,9 +96,7 @@
setTitle(R.string.time_picker_dialog_title);
Context themeContext = getContext();
- setButton(BUTTON_POSITIVE, themeContext.getText(R.string.date_time_set), this);
- setButton(BUTTON_NEGATIVE, themeContext.getText(R.string.cancel),
- (OnClickListener) null);
+ setButton(BUTTON_POSITIVE, themeContext.getText(R.string.date_time_done), this);
LayoutInflater inflater =
(LayoutInflater) themeContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -114,11 +112,7 @@
}
public void onClick(DialogInterface dialog, int which) {
- if (mCallback != null) {
- mTimePicker.clearFocus();
- mCallback.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
- mTimePicker.getCurrentMinute());
- }
+ tryNotifyTimeSet();
}
public void updateTime(int hourOfDay, int minutOfHour) {
@@ -130,6 +124,20 @@
/* do nothing */
}
+ private void tryNotifyTimeSet() {
+ if (mCallback != null) {
+ mTimePicker.clearFocus();
+ mCallback.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
+ mTimePicker.getCurrentMinute());
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ tryNotifyTimeSet();
+ super.onStop();
+ }
+
@Override
public Bundle onSaveInstanceState() {
Bundle state = super.onSaveInstanceState();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2902504..98ed117 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1769,6 +1769,18 @@
public static final String WIFI_P2P_SERVICE = "wifip2p";
/**
+ * Use with {@link #getSystemService} to retrieve a {@link
+ * android.net.NsdManager} for handling management of network service
+ * discovery
+ *
+ * @hide
+ * @see #getSystemService
+ * @see android.net.NsdManager
+ */
+ public static final String NSD_SERVICE = "servicediscovery";
+
+
+ /**
* Use with {@link #getSystemService} to retrieve a
* {@link android.media.AudioManager} for handling management of volume,
* ringer modes and audio routing.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 65a8750..cbabc7c 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -291,6 +291,17 @@
public static final int FLAG_STOPPED = 1<<21;
/**
+ * Value for {@link #flags}: true when the application is willing to support
+ * RTL (right to left). All activities will inherit this value.
+ *
+ * Set from the {@link android.R.attr#supportsRtl} attribute in the
+ * activity's manifest.
+ *
+ * Default value is false (no support for RTL).
+ */
+ public static final int FLAG_SUPPORTS_RTL = 1<<22;
+
+ /**
* Value for {@link #flags}: Set to true if the application has been
* installed using the forward lock option.
*
@@ -466,8 +477,17 @@
if (uiOptions != 0) {
pw.println(prefix + "uiOptions=0x" + Integer.toHexString(uiOptions));
}
+ pw.println(prefix + "supportsRtl=" + (hasRtlSupport() ? "true" : "false"));
super.dumpBack(pw, prefix);
}
+
+ /**
+ * @return true if "supportsRtl" has been set to true in the AndroidManifest
+ * @hide
+ */
+ public boolean hasRtlSupport() {
+ return (flags & FLAG_SUPPORTS_RTL) == FLAG_SUPPORTS_RTL;
+ }
public static class DisplayNameComparator
implements Comparator<ApplicationInfo> {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index eb8536f..a79b86a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1761,6 +1761,12 @@
ai.flags |= ApplicationInfo.FLAG_LARGE_HEAP;
}
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_supportsRtl,
+ false /* default is no RTL support*/)) {
+ ai.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL;
+ }
+
String str;
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
diff --git a/core/java/android/net/nsd/DnsSdServiceInfo.java b/core/java/android/net/nsd/DnsSdServiceInfo.java
new file mode 100644
index 0000000..47d6ec6
--- /dev/null
+++ b/core/java/android/net/nsd/DnsSdServiceInfo.java
@@ -0,0 +1,136 @@
+/*
+ * 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.net.nsd;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Defines a service based on DNS service discovery
+ * {@hide}
+ */
+public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable {
+
+ private String mServiceName;
+
+ private String mRegistrationType;
+
+ private DnsSdTxtRecord mTxtRecord;
+
+ private String mHostname;
+
+ private int mPort;
+
+ DnsSdServiceInfo() {
+ }
+
+ DnsSdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) {
+ mServiceName = sn;
+ mRegistrationType = rt;
+ mTxtRecord = tr;
+ }
+
+ @Override
+ /** @hide */
+ public String getServiceName() {
+ return mServiceName;
+ }
+
+ @Override
+ /** @hide */
+ public void setServiceName(String s) {
+ mServiceName = s;
+ }
+
+ @Override
+ /** @hide */
+ public String getServiceType() {
+ return mRegistrationType;
+ }
+
+ @Override
+ /** @hide */
+ public void setServiceType(String s) {
+ mRegistrationType = s;
+ }
+
+ public DnsSdTxtRecord getTxtRecord() {
+ return mTxtRecord;
+ }
+
+ public void setTxtRecord(DnsSdTxtRecord t) {
+ mTxtRecord = new DnsSdTxtRecord(t);
+ }
+
+ public String getHostName() {
+ return mHostname;
+ }
+
+ public void setHostName(String s) {
+ mHostname = s;
+ }
+
+ public int getPort() {
+ return mPort;
+ }
+
+ public void setPort(int p) {
+ mPort = p;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("name: ").append(mServiceName).
+ append("type: ").append(mRegistrationType).
+ append("txtRecord: ").append(mTxtRecord);
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mServiceName);
+ dest.writeString(mRegistrationType);
+ dest.writeParcelable(mTxtRecord, flags);
+ dest.writeString(mHostname);
+ dest.writeInt(mPort);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<DnsSdServiceInfo> CREATOR =
+ new Creator<DnsSdServiceInfo>() {
+ public DnsSdServiceInfo createFromParcel(Parcel in) {
+ DnsSdServiceInfo info = new DnsSdServiceInfo();
+ info.mServiceName = in.readString();
+ info.mRegistrationType = in.readString();
+ info.mTxtRecord = in.readParcelable(null);
+ info.mHostname = in.readString();
+ info.mPort = in.readInt();
+ return info;
+ }
+
+ public DnsSdServiceInfo[] newArray(int size) {
+ return new DnsSdServiceInfo[size];
+ }
+ };
+
+}
diff --git a/core/java/android/net/nsd/DnsSdTxtRecord.java b/core/java/android/net/nsd/DnsSdTxtRecord.java
new file mode 100644
index 0000000..6d4342c
--- /dev/null
+++ b/core/java/android/net/nsd/DnsSdTxtRecord.java
@@ -0,0 +1,305 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * 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.
+
+ To do:
+ - implement remove()
+ - fix set() to replace existing values
+ */
+
+package android.net.nsd;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * This class handles TXT record data for DNS based service discovery as specified at
+ * http://tools.ietf.org/html/draft-cheshire-dnsext-dns-sd-11
+ *
+ * DNS-SD specifies that a TXT record corresponding to an SRV record consist of
+ * a packed array of bytes, each preceded by a length byte. Each string
+ * is an attribute-value pair.
+ *
+ * The DnsSdTxtRecord object stores the entire TXT data as a single byte array, traversing it
+ * as need be to implement its various methods.
+ *
+ * @hide
+ */
+public class DnsSdTxtRecord implements Parcelable {
+ private static final byte mSeperator = '=';
+
+ private byte[] mData;
+
+ /** Constructs a new, empty TXT record. */
+ public DnsSdTxtRecord() {
+ mData = new byte[0];
+ }
+
+ /** Constructs a new TXT record from a byte array in the standard format. */
+ public DnsSdTxtRecord(byte[] data) {
+ mData = (byte[]) data.clone();
+ }
+
+ /** Copy constructor */
+ public DnsSdTxtRecord(DnsSdTxtRecord src) {
+ if (src != null && src.mData != null) {
+ mData = (byte[]) src.mData.clone();
+ }
+ }
+
+ /**
+ * Set a key/value pair. Setting an existing key will replace its value.
+ * @param key Must be ascii with no '='
+ * @param value matching value to key
+ */
+ public void set(String key, String value) {
+ byte[] keyBytes;
+ byte[] valBytes;
+ int valLen;
+
+ if (value != null) {
+ valBytes = value.getBytes();
+ valLen = valBytes.length;
+ } else {
+ valBytes = null;
+ valLen = 0;
+ }
+
+ try {
+ keyBytes = key.getBytes("US-ASCII");
+ }
+ catch (java.io.UnsupportedEncodingException e) {
+ throw new IllegalArgumentException("key should be US-ASCII");
+ }
+
+ for (int i = 0; i < keyBytes.length; i++) {
+ if (keyBytes[i] == '=') {
+ throw new IllegalArgumentException("= is not a valid character in key");
+ }
+ }
+
+ if (keyBytes.length + valLen >= 255) {
+ throw new IllegalArgumentException("Key and Value length cannot exceed 255 bytes");
+ }
+
+ int currentLoc = remove(key);
+ if (currentLoc == -1)
+ currentLoc = keyCount();
+
+ insert(keyBytes, valBytes, currentLoc);
+ }
+
+ /**
+ * Get a value for a key
+ *
+ * @param key
+ * @return The value associated with the key
+ */
+ public String get(String key) {
+ byte[] val = this.getValue(key);
+ return val != null ? new String(val) : null;
+ }
+
+ /** Remove a key/value pair. If found, returns the index or -1 if not found */
+ public int remove(String key) {
+ int avStart = 0;
+
+ for (int i=0; avStart < mData.length; i++) {
+ int avLen = mData[avStart];
+ if (key.length() <= avLen &&
+ (key.length() == avLen || mData[avStart + key.length() + 1] == mSeperator)) {
+ String s = new String(mData, avStart + 1, key.length());
+ if (0 == key.compareToIgnoreCase(s)) {
+ byte[] oldBytes = mData;
+ mData = new byte[oldBytes.length - avLen - 1];
+ System.arraycopy(oldBytes, 0, mData, 0, avStart);
+ System.arraycopy(oldBytes, avStart + avLen + 1, mData, avStart,
+ oldBytes.length - avStart - avLen - 1);
+ return i;
+ }
+ }
+ avStart += (0xFF & (avLen + 1));
+ }
+ return -1;
+ }
+
+ /** Return the count of keys */
+ public int keyCount() {
+ int count = 0, nextKey;
+ for (nextKey = 0; nextKey < mData.length; count++) {
+ nextKey += (0xFF & (mData[nextKey] + 1));
+ }
+ return count;
+ }
+
+ /** Return true if key is present, false if not. */
+ public boolean contains(String key) {
+ String s = null;
+ for (int i = 0; null != (s = this.getKey(i)); i++) {
+ if (0 == key.compareToIgnoreCase(s)) return true;
+ }
+ return false;
+ }
+
+ /* Gets the size in bytes */
+ public int size() {
+ return mData.length;
+ }
+
+ /* Gets the raw data in bytes */
+ public byte[] getRawData() {
+ return mData;
+ }
+
+ private void insert(byte[] keyBytes, byte[] value, int index) {
+ byte[] oldBytes = mData;
+ int valLen = (value != null) ? value.length : 0;
+ int insertion = 0;
+ int newLen, avLen;
+
+ for (int i = 0; i < index && insertion < mData.length; i++) {
+ insertion += (0xFF & (mData[insertion] + 1));
+ }
+
+ avLen = keyBytes.length + valLen + (value != null ? 1 : 0);
+ newLen = avLen + oldBytes.length + 1;
+
+ mData = new byte[newLen];
+ System.arraycopy(oldBytes, 0, mData, 0, insertion);
+ int secondHalfLen = oldBytes.length - insertion;
+ System.arraycopy(oldBytes, insertion, mData, newLen - secondHalfLen, secondHalfLen);
+ mData[insertion] = (byte) avLen;
+ System.arraycopy(keyBytes, 0, mData, insertion + 1, keyBytes.length);
+ if (value != null) {
+ mData[insertion + 1 + keyBytes.length] = mSeperator;
+ System.arraycopy(value, 0, mData, insertion + keyBytes.length + 2, valLen);
+ }
+ }
+
+ /** Return a key in the TXT record by zero-based index. Returns null if index exceeds the total number of keys. */
+ private String getKey(int index) {
+ int avStart = 0;
+
+ for (int i=0; i < index && avStart < mData.length; i++) {
+ avStart += mData[avStart] + 1;
+ }
+
+ if (avStart < mData.length) {
+ int avLen = mData[avStart];
+ int aLen = 0;
+
+ for (aLen=0; aLen < avLen; aLen++) {
+ if (mData[avStart + aLen + 1] == mSeperator) break;
+ }
+ return new String(mData, avStart + 1, aLen);
+ }
+ return null;
+ }
+
+ /**
+ * Look up a key in the TXT record by zero-based index and return its value.
+ * Returns null if index exceeds the total number of keys.
+ * Returns null if the key is present with no value.
+ */
+ private byte[] getValue(int index) {
+ int avStart = 0;
+ byte[] value = null;
+
+ for (int i=0; i < index && avStart < mData.length; i++) {
+ avStart += mData[avStart] + 1;
+ }
+
+ if (avStart < mData.length) {
+ int avLen = mData[avStart];
+ int aLen = 0;
+
+ for (aLen=0; aLen < avLen; aLen++) {
+ if (mData[avStart + aLen + 1] == mSeperator) {
+ value = new byte[avLen - aLen - 1];
+ System.arraycopy(mData, avStart + aLen + 2, value, 0, avLen - aLen - 1);
+ break;
+ }
+ }
+ }
+ return value;
+ }
+
+ private String getValueAsString(int index) {
+ byte[] value = this.getValue(index);
+ return value != null ? new String(value) : null;
+ }
+
+ private byte[] getValue(String forKey) {
+ String s = null;
+ int i;
+
+ for (i = 0; null != (s = this.getKey(i)); i++) {
+ if (0 == forKey.compareToIgnoreCase(s)) {
+ return this.getValue(i);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Return a string representation.
+ * Example : {key1=value1},{key2=value2}..
+ *
+ * For a key say like "key3" with null value
+ * {key1=value1},{key2=value2}{key3}
+ */
+ public String toString() {
+ String a, result = null;
+
+ for (int i = 0; null != (a = this.getKey(i)); i++) {
+ String av = "{" + a;
+ String val = this.getValueAsString(i);
+ if (val != null)
+ av += "=" + val + "}";
+ else
+ av += "}";
+ if (result == null)
+ result = av;
+ else
+ result = result + ", " + av;
+ }
+ return result != null ? result : "";
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(mData);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<DnsSdTxtRecord> CREATOR =
+ new Creator<DnsSdTxtRecord>() {
+ public DnsSdTxtRecord createFromParcel(Parcel in) {
+ DnsSdTxtRecord info = new DnsSdTxtRecord();
+ in.readByteArray(info.mData);
+ return info;
+ }
+
+ public DnsSdTxtRecord[] newArray(int size) {
+ return new DnsSdTxtRecord[size];
+ }
+ };
+}
diff --git a/core/java/android/net/nsd/INsdManager.aidl b/core/java/android/net/nsd/INsdManager.aidl
new file mode 100644
index 0000000..077a675
--- /dev/null
+++ b/core/java/android/net/nsd/INsdManager.aidl
@@ -0,0 +1,29 @@
+/**
+ * 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.net.nsd;
+
+import android.os.Messenger;
+
+/**
+ * Interface that NsdService implements
+ *
+ * {@hide}
+ */
+interface INsdManager
+{
+ Messenger getMessenger();
+}
diff --git a/core/java/android/net/nsd/NetworkServiceInfo.java b/core/java/android/net/nsd/NetworkServiceInfo.java
new file mode 100644
index 0000000..34d83d1
--- /dev/null
+++ b/core/java/android/net/nsd/NetworkServiceInfo.java
@@ -0,0 +1,32 @@
+/*
+ * 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.net.nsd;
+
+/**
+ * Interface for a network service.
+ *
+ * {@hide}
+ */
+public interface NetworkServiceInfo {
+
+ String getServiceName();
+ void setServiceName(String s);
+
+ String getServiceType();
+ void setServiceType(String s);
+
+}
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
new file mode 100644
index 0000000..a109a98
--- /dev/null
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -0,0 +1,394 @@
+/*
+ * 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.net.nsd;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.Messenger;
+import android.util.Log;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+/**
+ * The Network Service Discovery Manager class provides the API for service
+ * discovery. Service discovery enables applications to discover and connect with services
+ * on a network. Example applications include a game application discovering another instance
+ * of the game application or a printer application discovering other printers on a network.
+ *
+ * <p> The API is asynchronous and responses to requests from an application are on listener
+ * callbacks provided by the application. The application needs to do an initialization with
+ * {@link #initialize} before doing any operation.
+ *
+ * <p> Android currently supports DNS based service discovery and it is limited to a local
+ * network with the use of multicast DNS. In future, this class will be
+ * extended to support other service discovery mechanisms.
+ *
+ * Get an instance of this class by calling {@link android.content.Context#getSystemService(String)
+ * Context.getSystemService(Context.NSD_SERVICE)}.
+ * @hide
+ *
+ */
+public class NsdManager {
+ private static final String TAG = "NsdManager";
+ INsdManager mService;
+
+ private static final int BASE = Protocol.BASE_NSD_MANAGER;
+
+ /** @hide */
+ public static final int DISCOVER_SERVICES = BASE + 1;
+ /** @hide */
+ public static final int DISCOVER_SERVICES_STARTED = BASE + 2;
+ /** @hide */
+ public static final int DISCOVER_SERVICES_FAILED = BASE + 3;
+ /** @hide */
+ public static final int SERVICE_FOUND = BASE + 4;
+ /** @hide */
+ public static final int SERVICE_LOST = BASE + 5;
+
+ /** @hide */
+ public static final int STOP_DISCOVERY = BASE + 6;
+ /** @hide */
+ public static final int STOP_DISCOVERY_FAILED = BASE + 7;
+ /** @hide */
+ public static final int STOP_DISCOVERY_SUCCEEDED = BASE + 8;
+
+ /** @hide */
+ public static final int REGISTER_SERVICE = BASE + 9;
+ /** @hide */
+ public static final int REGISTER_SERVICE_FAILED = BASE + 10;
+ /** @hide */
+ public static final int REGISTER_SERVICE_SUCCEEDED = BASE + 11;
+
+ /** @hide */
+ public static final int UPDATE_SERVICE = BASE + 12;
+ /** @hide */
+ public static final int UPDATE_SERVICE_FAILED = BASE + 13;
+ /** @hide */
+ public static final int UPDATE_SERVICE_SUCCEEDED = BASE + 14;
+
+ /** @hide */
+ public static final int RESOLVE_SERVICE = BASE + 15;
+ /** @hide */
+ public static final int RESOLVE_SERVICE_FAILED = BASE + 16;
+ /** @hide */
+ public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 17;
+
+ /**
+ * Create a new Nsd instance. Applications use
+ * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
+ * {@link android.content.Context#NSD_SERVICE Context.NSD_SERVICE}.
+ * @param service the Binder interface
+ * @hide - hide this because it takes in a parameter of type INsdManager, which
+ * is a system private class.
+ */
+ public NsdManager(INsdManager service) {
+ mService = service;
+ }
+
+ /**
+ * Indicates that the operation failed due to an internal error.
+ */
+ public static final int ERROR = 0;
+
+ /**
+ * Indicates that the operation failed because service discovery is unsupported on the device.
+ */
+ public static final int UNSUPPORTED = 1;
+
+ /**
+ * Indicates that the operation failed because the framework is busy and
+ * unable to service the request
+ */
+ public static final int BUSY = 2;
+
+ /** Interface for callback invocation when framework channel is connected or lost */
+ public interface ChannelListener {
+ public void onChannelConnected(Channel c);
+ /**
+ * The channel to the framework has been disconnected.
+ * Application could try re-initializing using {@link #initialize}
+ */
+ public void onChannelDisconnected();
+ }
+
+ public interface ActionListener {
+
+ public void onFailure(int errorCode);
+
+ public void onSuccess();
+ }
+
+ public interface DnsSdDiscoveryListener {
+
+ public void onFailure(int errorCode);
+
+ public void onStarted(String registrationType);
+
+ public void onServiceFound(DnsSdServiceInfo serviceInfo);
+
+ public void onServiceLost(DnsSdServiceInfo serviceInfo);
+
+ }
+
+ public interface DnsSdRegisterListener {
+
+ public void onFailure(int errorCode);
+
+ public void onServiceRegistered(int registeredId, DnsSdServiceInfo serviceInfo);
+ }
+
+ public interface DnsSdUpdateRegistrationListener {
+
+ public void onFailure(int errorCode);
+
+ public void onServiceUpdated(int registeredId, DnsSdTxtRecord txtRecord);
+ }
+
+ public interface DnsSdResolveListener {
+
+ public void onFailure(int errorCode);
+
+ public void onServiceResolved(DnsSdServiceInfo serviceInfo);
+ }
+
+ /**
+ * A channel that connects the application to the NetworkService framework.
+ * Most service operations require a Channel as an argument. An instance of Channel is obtained
+ * by doing a call on {@link #initialize}
+ */
+ public static class Channel {
+ Channel(Looper looper, ChannelListener l) {
+ mAsyncChannel = new AsyncChannel();
+ mHandler = new ServiceHandler(looper);
+ mChannelListener = l;
+ }
+ private ChannelListener mChannelListener;
+ private DnsSdDiscoveryListener mDnsSdDiscoveryListener;
+ private ActionListener mDnsSdStopDiscoveryListener;
+ private DnsSdRegisterListener mDnsSdRegisterListener;
+ private DnsSdUpdateRegistrationListener mDnsSdUpdateListener;
+ private DnsSdResolveListener mDnsSdResolveListener;
+
+ AsyncChannel mAsyncChannel;
+ ServiceHandler mHandler;
+ class ServiceHandler extends Handler {
+ ServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ break;
+ case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
+ if (mChannelListener != null) {
+ mChannelListener.onChannelConnected(Channel.this);
+ }
+ break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ if (mChannelListener != null) {
+ mChannelListener.onChannelDisconnected();
+ mChannelListener = null;
+ }
+ break;
+ case DISCOVER_SERVICES_STARTED:
+ if (mDnsSdDiscoveryListener != null) {
+ mDnsSdDiscoveryListener.onStarted((String) message.obj);
+ }
+ break;
+ case DISCOVER_SERVICES_FAILED:
+ if (mDnsSdDiscoveryListener != null) {
+ mDnsSdDiscoveryListener.onFailure(message.arg1);
+ }
+ break;
+ case SERVICE_FOUND:
+ if (mDnsSdDiscoveryListener != null) {
+ mDnsSdDiscoveryListener.onServiceFound(
+ (DnsSdServiceInfo) message.obj);
+ }
+ break;
+ case SERVICE_LOST:
+ if (mDnsSdDiscoveryListener != null) {
+ mDnsSdDiscoveryListener.onServiceLost(
+ (DnsSdServiceInfo) message.obj);
+ }
+ break;
+ case STOP_DISCOVERY_FAILED:
+ if (mDnsSdStopDiscoveryListener != null) {
+ mDnsSdStopDiscoveryListener.onFailure(message.arg1);
+ }
+ break;
+ case STOP_DISCOVERY_SUCCEEDED:
+ if (mDnsSdStopDiscoveryListener != null) {
+ mDnsSdStopDiscoveryListener.onSuccess();
+ }
+ break;
+ case REGISTER_SERVICE_FAILED:
+ if (mDnsSdRegisterListener != null) {
+ mDnsSdRegisterListener.onFailure(message.arg1);
+ }
+ break;
+ case REGISTER_SERVICE_SUCCEEDED:
+ if (mDnsSdRegisterListener != null) {
+ mDnsSdRegisterListener.onServiceRegistered(message.arg1,
+ (DnsSdServiceInfo) message.obj);
+ }
+ break;
+ case UPDATE_SERVICE_FAILED:
+ if (mDnsSdUpdateListener != null) {
+ mDnsSdUpdateListener.onFailure(message.arg1);
+ }
+ break;
+ case UPDATE_SERVICE_SUCCEEDED:
+ if (mDnsSdUpdateListener != null) {
+ mDnsSdUpdateListener.onServiceUpdated(message.arg1,
+ (DnsSdTxtRecord) message.obj);
+ }
+ break;
+ case RESOLVE_SERVICE_FAILED:
+ if (mDnsSdResolveListener != null) {
+ mDnsSdResolveListener.onFailure(message.arg1);
+ }
+ break;
+ case RESOLVE_SERVICE_SUCCEEDED:
+ if (mDnsSdResolveListener != null) {
+ mDnsSdResolveListener.onServiceResolved(
+ (DnsSdServiceInfo) message.obj);
+ }
+ break;
+ default:
+ Log.d(TAG, "Ignored " + message);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers the application with the service discovery framework. This function
+ * must be the first to be called before any other operations are performed. No service
+ * discovery operations must be performed until the ChannelListener callback notifies
+ * that the channel is connected
+ *
+ * @param srcContext is the context of the source
+ * @param srcLooper is the Looper on which the callbacks are receivied
+ * @param listener for callback at loss of framework communication.
+ */
+ public void initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
+ Messenger messenger = getMessenger();
+ if (messenger == null) throw new RuntimeException("Failed to initialize");
+ if (listener == null) throw new IllegalArgumentException("ChannelListener cannot be null");
+
+ Channel c = new Channel(srcLooper, listener);
+ c.mAsyncChannel.connect(srcContext, c.mHandler, messenger);
+ }
+
+ /**
+ * Set the listener for service discovery. Can be null.
+ */
+ public void setDiscoveryListener(Channel c, DnsSdDiscoveryListener b) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mDnsSdDiscoveryListener = b;
+ }
+
+ /**
+ * Set the listener for stop service discovery. Can be null.
+ */
+ public void setStopDiscoveryListener(Channel c, ActionListener a) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mDnsSdStopDiscoveryListener = a;
+ }
+
+ /**
+ * Set the listener for service registration. Can be null.
+ */
+ public void setRegisterListener(Channel c, DnsSdRegisterListener b) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mDnsSdRegisterListener = b;
+ }
+
+ /**
+ * Set the listener for service registration. Can be null.
+ */
+ public void setUpdateRegistrationListener(Channel c, DnsSdUpdateRegistrationListener b) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mDnsSdUpdateListener = b;
+ }
+
+ /**
+ * Set the listener for service resolution. Can be null.
+ */
+ public void setResolveListener(Channel c, DnsSdResolveListener b) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mDnsSdResolveListener = b;
+ }
+
+ public void registerService(Channel c, DnsSdServiceInfo serviceInfo) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (serviceInfo == null) throw new IllegalArgumentException("Null serviceInfo");
+ c.mAsyncChannel.sendMessage(REGISTER_SERVICE, serviceInfo);
+ }
+
+ public void updateService(Channel c, int registeredId, DnsSdTxtRecord txtRecord) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mAsyncChannel.sendMessage(UPDATE_SERVICE, registeredId, 0, txtRecord);
+ }
+
+ public void discoverServices(Channel c, String serviceType) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (c.mDnsSdDiscoveryListener == null) throw new
+ IllegalStateException("Discovery listener needs to be set first");
+ DnsSdServiceInfo s = new DnsSdServiceInfo();
+ s.setServiceType(serviceType);
+ c.mAsyncChannel.sendMessage(DISCOVER_SERVICES, s);
+ }
+
+ public void stopServiceDiscovery(Channel c) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ c.mAsyncChannel.sendMessage(STOP_DISCOVERY);
+ }
+
+ public void resolveService(Channel c, DnsSdServiceInfo serviceInfo) {
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+ if (serviceInfo == null) throw new IllegalArgumentException("Null serviceInfo");
+ if (c.mDnsSdResolveListener == null) throw new
+ IllegalStateException("Resolve listener needs to be set first");
+ c.mAsyncChannel.sendMessage(RESOLVE_SERVICE, serviceInfo);
+ }
+
+ /**
+ * Get a reference to NetworkService handler. This is used to establish
+ * an AsyncChannel communication with the service
+ *
+ * @return Messenger pointing to the NetworkService handler
+ */
+ private Messenger getMessenger() {
+ try {
+ return mService.getMessenger();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/INdefPushCallback.aidl
index e60a5b0..4e79822 100644
--- a/core/java/android/nfc/INdefPushCallback.aidl
+++ b/core/java/android/nfc/INdefPushCallback.aidl
@@ -17,6 +17,7 @@
package android.nfc;
import android.nfc.NdefMessage;
+import android.net.Uri;
/**
* @hide
@@ -24,5 +25,7 @@
interface INdefPushCallback
{
NdefMessage createMessage();
+ Uri getUri();
+ String getMimeType();
void onNdefPushComplete();
}
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index 2223255..3ac1dcc 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -45,4 +45,5 @@
void resetTimeouts();
boolean canMakeReadOnly(int ndefType);
int getMaxTransceiveLength(int technology);
+ boolean getExtendedLengthApdusSupported();
}
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 2c73056..8335459 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.Application;
+import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
@@ -107,6 +108,8 @@
NdefMessage ndefMessage = null; // static NDEF message
NfcAdapter.CreateNdefMessageCallback ndefMessageCallback = null;
NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
+ Uri uri = null;
+ String mimeType = null;
public NfcActivityState(Activity activity) {
if (activity.getWindow().isDestroyed()) {
throw new IllegalStateException("activity is already destroyed");
@@ -121,12 +124,14 @@
ndefMessage = null;
ndefMessageCallback = null;
onNdefPushCompleteCallback = null;
+ uri = null;
+ mimeType = null;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder("[").append(" ");
s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
- s.append(onNdefPushCompleteCallback).append("]");
+ s.append(onNdefPushCompleteCallback).append(" ").append(uri).append("]");
return s.toString();
}
}
@@ -175,6 +180,19 @@
mDefaultEvent = new NfcEvent(mAdapter);
}
+ public void setNdefPushContentUri(Activity activity, String mimeType, Uri uri) {
+ boolean isResumed;
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = getActivityState(activity);
+ state.uri = uri;
+ state.mimeType = mimeType;
+ isResumed = state.resumed;
+ }
+ if (isResumed) {
+ requestNfcServiceCallback(true);
+ }
+ }
+
public void setNdefPushMessage(Activity activity, NdefMessage message) {
boolean isResumed;
synchronized (NfcActivityManager.this) {
@@ -249,6 +267,26 @@
/** Callback from NFC service, usually on binder thread */
@Override
+ public Uri getUri() {
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = findResumedActivityState();
+ if (state == null) return null;
+
+ return state.uri;
+ }
+ }
+ /** Callback from NFC service, usually on binder thread */
+ @Override
+ public String getMimeType() {
+ synchronized (NfcActivityManager.this) {
+ NfcActivityState state = findResumedActivityState();
+ if (state == null) return null;
+
+ return state.mimeType;
+ }
+ }
+ /** Callback from NFC service, usually on binder thread */
+ @Override
public void onNdefPushComplete() {
NfcAdapter.OnNdefPushCompleteCallback callback;
synchronized (NfcActivityManager.this) {
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index b7a7bd5..917751c 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -28,6 +28,7 @@
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
@@ -555,6 +556,18 @@
}
}
+ //TODO: Consider a callback alternative
+ //TOOD: See if we get rid of mimeType
+ //TODO: make sure NFC service has permission for URI
+ //TODO: javadoc
+ /** @hide */
+ public void setBeamPushUri(String mimeType, Uri uri, Activity activity) {
+ if (activity == null) {
+ throw new NullPointerException("activity cannot be null");
+ }
+ mNfcActivityManager.setNdefPushContentUri(activity, mimeType, uri);
+ }
+
/**
* Set a static {@link NdefMessage} to send using Android Beam (TM).
*
@@ -580,7 +593,18 @@
* and/or {@link #setNdefPushMessageCallback} is called with a null callback,
* then NDEF push will be completely disabled for the specified activity(s).
* This also disables any default NDEF message the Android OS would have
- * otherwise sent on your behalf.
+ * otherwise sent on your behalf for those activity(s).
+ *
+ * <p>If you want to prevent the Android OS from sending default NDEF
+ * messages completely (for all activities), you can include a
+ * <code><meta-data></code> element inside the <code><application></code>
+ * element of your AndroidManifest.xml file, like this:
+ * <pre>{@code
+ * <application ...>
+ * <meta-data android:name="android.nfc.disable_beam_default"
+ * android:value="true" />
+ * </application>
+ * }</pre>
*
* <p>The API allows for multiple activities to be specified at a time,
* but it is strongly recommended to just register one at a time,
@@ -664,7 +688,18 @@
* and/or {@link #setNdefPushMessageCallback} is called with a null callback,
* then NDEF push will be completely disabled for the specified activity(s).
* This also disables any default NDEF message the Android OS would have
- * otherwise sent on your behalf.
+ * otherwise sent on your behalf for those activity(s).
+ *
+ * <p>If you want to prevent the Android OS from sending default NDEF
+ * messages completely (for all activities), you can include a
+ * <code><meta-data></code> element inside the <code><application></code>
+ * element of your AndroidManifest.xml file, like this:
+ * <pre>{@code
+ * <application ...>
+ * <meta-data android:name="android.nfc.disable_beam_default"
+ * android:value="true" />
+ * </application>
+ * }</pre>
*
* <p>The API allows for multiple activities to be specified at a time,
* but it is strongly recommended to just register one at a time,
diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java
index 1859877..089b159 100644
--- a/core/java/android/nfc/tech/IsoDep.java
+++ b/core/java/android/nfc/tech/IsoDep.java
@@ -179,4 +179,27 @@
public int getMaxTransceiveLength() {
return getMaxTransceiveLengthInternal();
}
+
+ /**
+ * <p>Standard APDUs have a 1-byte length field, allowing a maximum of
+ * 255 payload bytes, which results in a maximum APDU length of 261 bytes.
+ *
+ * <p>Extended length APDUs have a 3-byte length field, allowing 65535
+ * payload bytes.
+ *
+ * <p>Some NFC adapters, like the one used in the Nexus S and the Galaxy Nexus
+ * do not support extended length APDUs. They are expected to be well-supported
+ * in the future though. Use this method to check for extended length APDU
+ * support.
+ *
+ * @return whether the NFC adapter on this device supports extended length APDUs.
+ */
+ public boolean isExtendedLengthApduSupported() {
+ try {
+ return mTag.getTagService().getExtendedLengthApdusSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ return false;
+ }
+ }
}
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index b816b11..4aa7fe2 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -16,9 +16,6 @@
package android.os;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.TimeUtils;
/**
@@ -368,13 +365,13 @@
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with represent to synchronous messages. Asynchronous messages are not subject to
- * the synchronization barriers introduced by {@link MessageQueue#acquireSyncBarrier()}.
+ * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @return True if the message is asynchronous.
*
* @see #setAsynchronous(boolean)
- * @see MessageQueue#acquireSyncBarrier()
- * @see MessageQueue#releaseSyncBarrier()
+ * @see MessageQueue#enqueueSyncBarrier(long)
+ * @see MessageQueue#removeSyncBarrier(int)
*
* @hide
*/
@@ -387,13 +384,13 @@
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with represent to synchronous messages. Asynchronous messages are not subject to
- * the synchronization barriers introduced by {@link MessageQueue#acquireSyncBarrier()}.
+ * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param async True if the message is asynchronous.
*
* @see #isAsynchronous()
- * @see MessageQueue#acquireSyncBarrier()
- * @see MessageQueue#releaseSyncBarrier()
+ * @see MessageQueue#enqueueSyncBarrier(long)
+ * @see MessageQueue#removeSyncBarrier(int)
*
* @hide
*/
@@ -506,7 +503,7 @@
Messenger.writeMessengerOrNullToParcel(replyTo, dest);
}
- private final void readFromParcel(Parcel source) {
+ private void readFromParcel(Parcel source) {
what = source.readInt();
arg1 = source.readInt();
arg2 = source.readInt();
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 74a376d..8df4339 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -497,27 +497,30 @@
* @see #onCreateView(ViewGroup)
*/
protected void onBindView(View view) {
- TextView textView = (TextView) view.findViewById(com.android.internal.R.id.title);
- if (textView != null) {
- textView.setText(getTitle());
- }
-
- textView = (TextView) view.findViewById(com.android.internal.R.id.summary);
- if (textView != null) {
- final CharSequence summary = getSummary();
- if (!TextUtils.isEmpty(summary)) {
- if (textView.getVisibility() != View.VISIBLE) {
- textView.setVisibility(View.VISIBLE);
- }
-
- textView.setText(getSummary());
+ final TextView titleView = (TextView) view.findViewById(
+ com.android.internal.R.id.title);
+ if (titleView != null) {
+ final CharSequence title = getTitle();
+ if (!TextUtils.isEmpty(title)) {
+ titleView.setText(title);
+ titleView.setVisibility(View.VISIBLE);
} else {
- if (textView.getVisibility() != View.GONE) {
- textView.setVisibility(View.GONE);
- }
+ titleView.setVisibility(View.GONE);
}
}
-
+
+ final TextView summaryView = (TextView) view.findViewById(
+ com.android.internal.R.id.summary);
+ if (summaryView != null) {
+ final CharSequence summary = getSummary();
+ if (!TextUtils.isEmpty(summary)) {
+ summaryView.setText(summary);
+ summaryView.setVisibility(View.VISIBLE);
+ } else {
+ summaryView.setVisibility(View.GONE);
+ }
+ }
+
ImageView imageView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
if (imageView != null) {
if (mIconResId != 0 || mIcon != null) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d74ccb8..830a85f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1506,13 +1506,22 @@
public static final String VOLUME_MASTER = "volume_master";
/**
- * Whether the notifications should use the ring volume (value of 1) or a separate
- * notification volume (value of 0). In most cases, users will have this enabled so the
- * notification and ringer volumes will be the same. However, power users can disable this
- * and use the separate notification volume control.
+ * Master volume mute (int 1 = mute, 0 = not muted).
+ *
+ * @hide
+ */
+ public static final String VOLUME_MASTER_MUTE = "volume_master_mute";
+
+ /**
+ * Whether the notifications should use the ring volume (value of 1) or
+ * a separate notification volume (value of 0). In most cases, users
+ * will have this enabled so the notification and ringer volumes will be
+ * the same. However, power users can disable this and use the separate
+ * notification volume control.
* <p>
- * Note: This is a one-off setting that will be removed in the future when there is profile
- * support. For this reason, it is kept hidden from the public APIs.
+ * Note: This is a one-off setting that will be removed in the future
+ * when there is profile support. For this reason, it is kept hidden
+ * from the public APIs.
*
* @hide
* @deprecated
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index d0c87c6..f7a7eb8 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -262,19 +262,8 @@
return append(String.valueOf(text));
}
- private int change(int start, int end, CharSequence tb, int tbstart, int tbend) {
- return change(true, start, end, tb, tbstart, tbend);
- }
-
- private int change(boolean notify, int start, int end,
- CharSequence tb, int tbstart, int tbend) {
+ private void change(int start, int end, CharSequence tb, int tbstart, int tbend) {
checkRange("replace", start, end);
- int ret = tbend - tbstart;
- TextWatcher[] recipients = null;
-
- if (notify) {
- recipients = sendTextWillChange(start, end - start, tbend - tbstart);
- }
for (int i = mSpanCount - 1; i >= 0; i--) {
if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) {
@@ -338,7 +327,7 @@
en = tbend;
if (getSpanStart(spans[i]) < 0) {
- setSpan(true, spans[i],
+ setSpan(false, spans[i],
st - tbstart + start,
en - tbstart + start,
sp.getSpanFlags(spans[i]));
@@ -346,51 +335,37 @@
}
}
- // no need for span fixup on pure insertion
- if (tbend > tbstart && end - start == 0) {
- if (notify) {
- sendTextChange(recipients, start, end - start, tbend - tbstart);
- sendTextHasChanged(recipients);
- }
+ if (end > start) {
+ // no need for span fixup on pure insertion
+ boolean atEnd = (mGapStart + mGapLength == mText.length);
- return ret;
- }
+ for (int i = mSpanCount - 1; i >= 0; i--) {
+ if (mSpanStarts[i] >= start &&
+ mSpanStarts[i] < mGapStart + mGapLength) {
+ int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT;
- boolean atend = (mGapStart + mGapLength == mText.length);
-
- for (int i = mSpanCount - 1; i >= 0; i--) {
- if (mSpanStarts[i] >= start &&
- mSpanStarts[i] < mGapStart + mGapLength) {
- int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT;
-
- if (flag == POINT || (flag == PARAGRAPH && atend))
+ if (flag == POINT || (flag == PARAGRAPH && atEnd))
mSpanStarts[i] = mGapStart + mGapLength;
else
mSpanStarts[i] = start;
- }
+ }
- if (mSpanEnds[i] >= start &&
- mSpanEnds[i] < mGapStart + mGapLength) {
- int flag = (mSpanFlags[i] & END_MASK);
+ if (mSpanEnds[i] >= start &&
+ mSpanEnds[i] < mGapStart + mGapLength) {
+ int flag = (mSpanFlags[i] & END_MASK);
- if (flag == POINT || (flag == PARAGRAPH && atend))
- mSpanEnds[i] = mGapStart + mGapLength;
- else
- mSpanEnds[i] = start;
- }
+ if (flag == POINT || (flag == PARAGRAPH && atEnd))
+ mSpanEnds[i] = mGapStart + mGapLength;
+ else
+ mSpanEnds[i] = start;
+ }
- // remove 0-length SPAN_EXCLUSIVE_EXCLUSIVE
- if (mSpanEnds[i] < mSpanStarts[i]) {
- removeSpan(i);
+ // remove 0-length SPAN_EXCLUSIVE_EXCLUSIVE
+ if (mSpanEnds[i] < mSpanStarts[i]) {
+ removeSpan(i);
+ }
}
}
-
- if (notify) {
- sendTextChange(recipients, start, end - start, tbend - tbstart);
- sendTextHasChanged(recipients);
- }
-
- return ret;
}
private void removeSpan(int i) {
@@ -425,8 +400,7 @@
CharSequence tb, int tbstart, int tbend) {
int filtercount = mFilters.length;
for (int i = 0; i < filtercount; i++) {
- CharSequence repl = mFilters[i].filter(tb, tbstart, tbend,
- this, start, end);
+ CharSequence repl = mFilters[i].filter(tb, tbstart, tbend, this, start, end);
if (repl != null) {
tb = repl;
@@ -435,11 +409,17 @@
}
}
- if (end == start && tbstart == tbend) {
+ final int origLen = end - start;
+ final int newLen = tbend - tbstart;
+
+ if (origLen == 0 && newLen == 0) {
return this;
}
- if (end == start || tbstart == tbend) {
+ TextWatcher[] textWatchers = getSpans(start, start + origLen, TextWatcher.class);
+ sendBeforeTextChanged(textWatchers, start, origLen, newLen);
+
+ if (origLen == 0 || newLen == 0) {
change(start, end, tb, tbstart, tbend);
} else {
int selstart = Selection.getSelectionStart(this);
@@ -450,11 +430,6 @@
checkRange("replace", start, end);
moveGapTo(end);
- TextWatcher[] recipients;
-
- int origlen = end - start;
-
- recipients = sendTextWillChange(start, origlen, tbend - tbstart);
if (mGapLength < 2)
resizeFor(length() + 1);
@@ -475,9 +450,9 @@
new Exception("mGapLength < 1").printStackTrace();
}
- int inserted = change(false, start + 1, start + 1, tb, tbstart, tbend);
- change(false, start, start + 1, "", 0, 0);
- change(false, start + inserted, start + inserted + origlen, "", 0, 0);
+ change(start + 1, start + 1, tb, tbstart, tbend);
+ change(start, start + 1, "", 0, 0);
+ change(start + newLen, start + newLen + origLen, "", 0, 0);
/*
* Special case to keep the cursor in the same position
@@ -490,7 +465,7 @@
if (selstart > start && selstart < end) {
long off = selstart - start;
- off = off * inserted / (end - start);
+ off = off * newLen / (end - start);
selstart = (int) off + start;
setSpan(false, Selection.SELECTION_START, selstart, selstart,
@@ -499,15 +474,16 @@
if (selend > start && selend < end) {
long off = selend - start;
- off = off * inserted / (end - start);
+ off = off * newLen / (end - start);
selend = (int) off + start;
setSpan(false, Selection.SELECTION_END, selend, selend, Spanned.SPAN_POINT_POINT);
}
- sendTextChange(recipients, start, origlen, inserted);
- sendTextHasChanged(recipients);
}
+ sendTextChanged(textWatchers, start, origLen, newLen);
+ sendAfterTextChanged(textWatchers);
+
return this;
}
@@ -872,30 +848,27 @@
return new String(buf);
}
- private TextWatcher[] sendTextWillChange(int start, int before, int after) {
- TextWatcher[] recip = getSpans(start, start + before, TextWatcher.class);
- int n = recip.length;
+ private void sendBeforeTextChanged(TextWatcher[] watchers, int start, int before, int after) {
+ int n = watchers.length;
for (int i = 0; i < n; i++) {
- recip[i].beforeTextChanged(this, start, before, after);
- }
-
- return recip;
- }
-
- private void sendTextChange(TextWatcher[] recip, int start, int before, int after) {
- int n = recip.length;
-
- for (int i = 0; i < n; i++) {
- recip[i].onTextChanged(this, start, before, after);
+ watchers[i].beforeTextChanged(this, start, before, after);
}
}
- private void sendTextHasChanged(TextWatcher[] recip) {
- int n = recip.length;
+ private void sendTextChanged(TextWatcher[] watchers, int start, int before, int after) {
+ int n = watchers.length;
for (int i = 0; i < n; i++) {
- recip[i].afterTextChanged(this);
+ watchers[i].onTextChanged(this, start, before, after);
+ }
+ }
+
+ private void sendAfterTextChanged(TextWatcher[] watchers) {
+ int n = watchers.length;
+
+ for (int i = 0; i < n; i++) {
+ watchers[i].afterTextChanged(this);
}
}
@@ -1037,8 +1010,7 @@
* Don't call this yourself -- exists for Canvas to use internally.
* {@hide}
*/
- public void drawText(Canvas c, int start, int end,
- float x, float y, Paint p) {
+ public void drawText(Canvas c, int start, int end, float x, float y, Paint p) {
checkRange("drawText", start, end);
if (end <= mGapStart) {
@@ -1059,8 +1031,7 @@
* Don't call this yourself -- exists for Canvas to use internally.
* {@hide}
*/
- public void drawTextRun(Canvas c, int start, int end,
- int contextStart, int contextEnd,
+ public void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd,
float x, float y, int flags, Paint p) {
checkRange("drawTextRun", start, end);
@@ -1262,6 +1233,7 @@
private int[] mSpanFlags;
private int mSpanCount;
+ // TODO These value are tightly related to the public SPAN_MARK/POINT values in {@link Spanned}
private static final int POINT = 2;
private static final int PARAGRAPH = 3;
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index e2aafa9..fba73fbd 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -16,6 +16,8 @@
package android.view;
+import android.graphics.Matrix;
+
/**
* A display lists records a series of graphics related operation and can replay
* them later. Display lists are usually built by recording operations on a
@@ -117,12 +119,26 @@
public abstract void setClipChildren(boolean clipChildren);
/**
- * Set the application scale on the DisplayList. This scale is incurred by applications that
- * are auto-scaled for compatibility reasons. By default, the value is 1 (unscaled).
+ * Set the static matrix on the DisplayList. This matrix exists if a custom ViewGroup
+ * overrides
+ * {@link ViewGroup#getChildStaticTransformation(View, android.view.animation.Transformation)}
+ * and also has {@link ViewGroup#setStaticTransformationsEnabled(boolean)} set to true.
+ * This matrix will be concatenated with any other matrices in the DisplayList to position
+ * the view appropriately.
*
- * @param scale The scaling factor
+ * @param matrix The matrix
*/
- public abstract void setApplicationScale(float scale);
+ public abstract void setStaticMatrix(Matrix matrix);
+
+ /**
+ * Set the Animation matrix on the DisplayList. This matrix exists if an Animation is
+ * currently playing on a View, and is set on the DisplayList during at draw() time. When
+ * the Animation finishes, the matrix should be cleared by sending <code>null</code>
+ * for the matrix parameter.
+ *
+ * @param matrix The matrix, null indicates that the matrix should be cleared.
+ */
+ public abstract void setAnimationMatrix(Matrix matrix);
/**
* Sets the alpha value for the DisplayList
@@ -133,6 +149,15 @@
public abstract void setAlpha(float alpha);
/**
+ * Sets whether the DisplayList renders content which overlaps. Non-overlapping rendering
+ * can use a fast path for alpha that avoids rendering to an offscreen buffer.
+ *
+ * @param hasOverlappingRendering
+ * @see android.view.View#hasOverlappingRendering()
+ */
+ public abstract void setHasOverlappingRendering(boolean hasOverlappingRendering);
+
+ /**
* Sets the translationX value for the DisplayList
*
* @param translationX The translationX value of the DisplayList
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 5b0433e..bedafc74 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -259,6 +259,13 @@
private static native int nCallDrawGLFunction(int renderer, int drawGLFunction);
+ @Override
+ public int invokeFunctors(Rect dirty) {
+ return nInvokeFunctors(mRenderer, dirty);
+ }
+
+ private static native int nInvokeFunctors(int renderer, Rect dirty);
+
///////////////////////////////////////////////////////////////////////////
// Memory
///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 9b4cf21..f3618eb 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -17,6 +17,7 @@
package android.view;
import android.graphics.Bitmap;
+import android.graphics.Matrix;
import java.util.ArrayList;
@@ -119,9 +120,18 @@
}
@Override
- public void setApplicationScale(float scale) {
+ public void setStaticMatrix(Matrix matrix) {
try {
- nSetApplicationScale(getNativeDisplayList(), scale);
+ nSetStaticMatrix(getNativeDisplayList(), matrix.native_instance);
+ } catch (IllegalStateException e) {
+ // invalid DisplayList okay: we'll set current values the next time we render to it
+ }
+ }
+
+ @Override
+ public void setAnimationMatrix(Matrix matrix) {
+ try {
+ nSetAnimationMatrix(getNativeDisplayList(), matrix.native_instance);
} catch (IllegalStateException e) {
// invalid DisplayList okay: we'll set current values the next time we render to it
}
@@ -137,6 +147,15 @@
}
@Override
+ public void setHasOverlappingRendering(boolean hasOverlappingRendering) {
+ try {
+ nSetHasOverlappingRendering(getNativeDisplayList(), hasOverlappingRendering);
+ } catch (IllegalStateException e) {
+ // invalid DisplayList okay: we'll set current values the next time we render to it
+ }
+ }
+
+ @Override
public void setTranslationX(float translationX) {
try {
nSetTranslationX(getNativeDisplayList(), translationX);
@@ -325,6 +344,8 @@
private static native void nSetClipChildren(int displayList, boolean clipChildren);
private static native void nSetApplicationScale(int displayList, float scale);
private static native void nSetAlpha(int displayList, float alpha);
+ private static native void nSetHasOverlappingRendering(int displayList,
+ boolean hasOverlappingRendering);
private static native void nSetTranslationX(int displayList, float translationX);
private static native void nSetTranslationY(int displayList, float translationY);
private static native void nSetRotation(int displayList, float rotation);
@@ -335,6 +356,8 @@
private static native void nSetTransformationInfo(int displayList, float alpha,
float translationX, float translationY, float rotation, float rotationX,
float rotationY, float scaleX, float scaleY);
+ private static native void nSetStaticMatrix(int displayList, int nativeMatrix);
+ private static native void nSetAnimationMatrix(int displayList, int animationMatrix);
///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 2636ea21..de8c62d 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -98,4 +98,16 @@
// Noop - this is done in the display list recorder subclass
return DisplayList.STATUS_DONE;
}
+
+ /**
+ * Invoke all the functors who requested to be invoked during the previous frame.
+ *
+ * @param dirty The region to redraw when the functors return {@link DisplayList#STATUS_DRAW}
+ *
+ * @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW} or
+ * {@link DisplayList#STATUS_INVOKE}
+ */
+ public int invokeFunctors(Rect dirty) {
+ return DisplayList.STATUS_DONE;
+ }
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index be640dc..b8c692a 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -87,7 +87,7 @@
/**
* System property used to enable or disable hardware rendering profiling.
* The default value of this property is assumed to be false.
- *
+ *
* When profiling is enabled, the adb shell dumpsys gfxinfo command will
* output extra information about the time taken to execute by the last
* frames.
@@ -99,6 +99,20 @@
static final String PROFILE_PROPERTY = "hwui.profile";
/**
+ * System property used to specify the number of frames to be used
+ * when doing hardware rendering profiling.
+ * The default value of this property is #PROFILE_MAX_FRAMES.
+ *
+ * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+ * output extra information about the time taken to execute by the last
+ * frames.
+ *
+ * Possible values:
+ * "60", to set the limit of frames to 60
+ */
+ static final String PROFILE_MAXFRAMES_PROPERTY = "hwui.profile.maxframes";
+
+ /**
* System property used to debug EGL configuration choice.
*
* Possible values:
@@ -259,7 +273,14 @@
* @param pw
*/
abstract void dumpGfxInfo(PrintWriter pw);
-
+
+ /**
+ * Outputs the total number of frames rendered (used for fps calculations)
+ *
+ * @return the number of frames rendered
+ */
+ abstract long getFrameCount();
+
/**
* Sets the directory to use as a persistent storage for hardware rendering
* resources.
@@ -495,7 +516,9 @@
static final int SURFACE_STATE_ERROR = 0;
static final int SURFACE_STATE_SUCCESS = 1;
static final int SURFACE_STATE_UPDATED = 2;
-
+
+ static final int FUNCTOR_PROCESS_DELAY = 2;
+
static EGL10 sEgl;
static EGLDisplay sEglDisplay;
static EGLConfig sEglConfig;
@@ -513,7 +536,7 @@
GL mGl;
HardwareCanvas mCanvas;
- int mFrameCount;
+ long mFrameCount;
Paint mDebugPaint;
static boolean sDirtyRegions;
@@ -542,7 +565,9 @@
private boolean mDestroyed;
private final Rect mRedrawClip = new Rect();
+
private final int[] mSurfaceSize = new int[2];
+ private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable();
GlRenderer(int glVersion, boolean translucent) {
mGlVersion = glVersion;
@@ -568,7 +593,13 @@
}
if (mProfileEnabled) {
- mProfileData = new float[PROFILE_MAX_FRAMES * PROFILE_FRAME_DATA_COUNT];
+ property = SystemProperties.get(PROFILE_MAXFRAMES_PROPERTY,
+ Integer.toString(PROFILE_MAX_FRAMES));
+ int maxProfileFrames = Integer.valueOf(property);
+ mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
+ for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
+ mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
+ }
} else {
mProfileData = null;
}
@@ -585,12 +616,22 @@
if (mProfileEnabled) {
pw.printf("\n\tDraw\tProcess\tExecute\n");
for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
+ if (mProfileData[i] < 0) {
+ break;
+ }
pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
mProfileData[i + 2]);
+ mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
}
+ mProfileCurrentFrame = mProfileData.length;
}
}
+ @Override
+ long getFrameCount() {
+ return mFrameCount;
+ }
+
/**
* Indicates whether this renderer instance can track and update dirty regions.
*/
@@ -945,6 +986,24 @@
void onPostDraw() {
}
+ class FunctorsRunnable implements Runnable {
+ View.AttachInfo attachInfo;
+
+ @Override
+ public void run() {
+ final HardwareRenderer renderer = attachInfo.mHardwareRenderer;
+ if (renderer == null || !renderer.isEnabled() || renderer != GlRenderer.this) {
+ return;
+ }
+
+ final int surfaceState = checkCurrent();
+ if (surfaceState != SURFACE_STATE_ERROR) {
+ int status = mCanvas.invokeFunctors(mRedrawClip);
+ handleFunctorStatus(attachInfo, status);
+ }
+ }
+ }
+
@Override
boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Rect dirty) {
@@ -1012,10 +1071,6 @@
Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- getDisplayList() took " +
total + "ms");
}
- if (View.USE_DISPLAY_LIST_PROPERTIES) {
- Log.d("DLProperties", "getDisplayList():\t" +
- mProfileData[mProfileCurrentFrame]);
- }
}
if (displayList != null) {
@@ -1039,15 +1094,7 @@
}
}
- if (status != DisplayList.STATUS_DONE) {
- if (mRedrawClip.isEmpty()) {
- attachInfo.mViewRootImpl.invalidate();
- } else {
- attachInfo.mViewRootImpl.invalidateChildInParent(
- null, mRedrawClip);
- mRedrawClip.setEmpty();
- }
- }
+ handleFunctorStatus(attachInfo, status);
} else {
// Shouldn't reach here
view.draw(canvas);
@@ -1056,13 +1103,13 @@
callbacks.onHardwarePostDraw(canvas);
canvas.restoreToCount(saveCount);
view.mRecreateDisplayList = false;
-
+ mFrameCount++;
if (mDebugDirtyRegions) {
if (mDebugPaint == null) {
mDebugPaint = new Paint();
mDebugPaint.setColor(0x7fff0000);
}
- if (dirty != null && (mFrameCount++ & 1) == 0) {
+ if (dirty != null && (mFrameCount & 1) == 0) {
canvas.drawRect(dirty, mDebugPaint);
}
}
@@ -1099,6 +1146,26 @@
return false;
}
+ private void handleFunctorStatus(View.AttachInfo attachInfo, int status) {
+ // If the draw flag is set, functors will be invoked while executing
+ // the tree of display lists
+ if ((status & DisplayList.STATUS_DRAW) != 0) {
+ if (mRedrawClip.isEmpty()) {
+ attachInfo.mViewRootImpl.invalidate();
+ } else {
+ attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip);
+ mRedrawClip.setEmpty();
+ }
+ }
+
+ if ((status & DisplayList.STATUS_INVOKE) != 0) {
+ attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
+ mFunctorsRunnable.attachInfo = attachInfo;
+ // delay the functor callback by a few ms so it isn't polled constantly
+ attachInfo.mHandler.postDelayed(mFunctorsRunnable, FUNCTOR_PROCESS_DELAY);
+ }
+ }
+
/**
* Ensures the current EGL context is the one we expect.
*
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 104ed6a..e4a4a75 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -591,8 +591,24 @@
/** Key code constant: Calculator special function key.
* Used to launch a calculator application. */
public static final int KEYCODE_CALCULATOR = 210;
+ /** Key code constant: Japanese full-width / half-width key. */
+ public static final int KEYCODE_ZENKAKU_HANKAKU = 211;
+ /** Key code constant: Japanese alphanumeric key. */
+ public static final int KEYCODE_EISU = 212;
+ /** Key code constant: Japanese non-conversion key. */
+ public static final int KEYCODE_MUHENKAN = 213;
+ /** Key code constant: Japanese conversion key. */
+ public static final int KEYCODE_HENKAN = 214;
+ /** Key code constant: Japanese katakana / hiragana key. */
+ public static final int KEYCODE_KATAKANA_HIRAGANA = 215;
+ /** Key code constant: Japanese Yen key. */
+ public static final int KEYCODE_YEN = 216;
+ /** Key code constant: Japanese Ro key. */
+ public static final int KEYCODE_RO = 217;
+ /** Key code constant: Japanese kana key. */
+ public static final int KEYCODE_KANA = 218;
- private static final int LAST_KEYCODE = KEYCODE_CALCULATOR;
+ private static final int LAST_KEYCODE = KEYCODE_KANA;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
@@ -825,6 +841,14 @@
names.append(KEYCODE_CALENDAR, "KEYCODE_CALENDAR");
names.append(KEYCODE_MUSIC, "KEYCODE_MUSIC");
names.append(KEYCODE_CALCULATOR, "KEYCODE_CALCULATOR");
+ names.append(KEYCODE_ZENKAKU_HANKAKU, "KEYCODE_ZENKAKU_HANKAKU");
+ names.append(KEYCODE_EISU, "KEYCODE_EISU");
+ names.append(KEYCODE_MUHENKAN, "KEYCODE_MUHENKAN");
+ names.append(KEYCODE_HENKAN, "KEYCODE_HENKAN");
+ names.append(KEYCODE_KATAKANA_HIRAGANA, "KEYCODE_KATAKANA_HIRAGANA");
+ names.append(KEYCODE_YEN, "KEYCODE_YEN");
+ names.append(KEYCODE_RO, "KEYCODE_RO");
+ names.append(KEYCODE_KANA, "KEYCODE_KANA");
};
// Symbolic names of all metakeys in bit order from least significant to most significant.
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 3c0ee12..26a5b26 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -583,7 +583,14 @@
Object[] args = mConstructorArgs;
args[1] = attrs;
- return constructor.newInstance(args);
+
+ final View view = constructor.newInstance(args);
+ if (view instanceof ViewStub) {
+ // always use ourselves when inflating ViewStub later
+ final ViewStub viewStub = (ViewStub) view;
+ viewStub.setLayoutInflater(this);
+ }
+ return view;
} catch (NoSuchMethodException e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6c964b0..5d7c8cd 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -71,6 +71,7 @@
import android.widget.ScrollBarDrawable;
import static android.os.Build.VERSION_CODES.*;
+import static java.lang.Math.max;
import com.android.internal.R;
import com.android.internal.util.Predicate;
@@ -126,7 +127,7 @@
* example, all views will let you set a listener to be notified when the view
* gains or loses focus. You can register such a listener using
* {@link #setOnFocusChangeListener(android.view.View.OnFocusChangeListener)}.
- * Other view subclasses offer more specialized listeners. For example, a Button
+ * Other view subclasses offer more specialized listeners. For example, a Button
* exposes a listener to notify clients when the button is clicked.</li>
* <li><strong>Set visibility:</strong> You can hide or show views using
* {@link #setVisibility(int)}.</li>
@@ -579,6 +580,7 @@
* @attr ref android.R.styleable#View_duplicateParentState
* @attr ref android.R.styleable#View_id
* @attr ref android.R.styleable#View_requiresFadingEdge
+ * @attr ref android.R.styleable#View_fadeScrollbars
* @attr ref android.R.styleable#View_fadingEdgeLength
* @attr ref android.R.styleable#View_filterTouchesWhenObscured
* @attr ref android.R.styleable#View_fitsSystemWindows
@@ -624,6 +626,7 @@
* @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack
* @attr ref android.R.styleable#View_soundEffectsEnabled
* @attr ref android.R.styleable#View_tag
+ * @attr ref android.R.styleable#View_textAlignment
* @attr ref android.R.styleable#View_transformPivotX
* @attr ref android.R.styleable#View_transformPivotY
* @attr ref android.R.styleable#View_translationX
@@ -765,7 +768,12 @@
*/
static final int FILTER_TOUCHES_WHEN_OBSCURED = 0x00000400;
- // note flag value 0x00000800 is now available for next flags...
+ /**
+ * Set for framework elements that use FITS_SYSTEM_WINDOWS, to indicate
+ * that they are optional and should be skipped if the window has
+ * requested system UI flags that ignore those insets for layout.
+ */
+ static final int OPTIONAL_FITS_SYSTEM_WINDOWS = 0x00000800;
/**
* <p>This view doesn't show fading edges.</p>
@@ -1459,7 +1467,7 @@
* apps.
* @hide
*/
- public static final boolean USE_DISPLAY_LIST_PROPERTIES = false;
+ public static final boolean USE_DISPLAY_LIST_PROPERTIES = true;
/**
* Map used to store views' tags.
@@ -1822,17 +1830,17 @@
public static final int TEXT_DIRECTION_LOCALE = 5;
/**
+ * Default text direction is inherited
+ */
+ protected static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
+
+ /**
* Bit shift to get the horizontal layout direction. (bits after LAYOUT_DIRECTION_RESOLVED)
* @hide
*/
static final int TEXT_DIRECTION_MASK_SHIFT = 6;
/**
- * Default text direction is inherited
- */
- protected static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
-
- /**
* Mask for use with private flags indicating bits used for text direction.
* @hide
*/
@@ -1877,6 +1885,113 @@
static final int TEXT_DIRECTION_RESOLVED_DEFAULT =
TEXT_DIRECTION_FIRST_STRONG << TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
+ /*
+ * Default text alignment. The text alignment of this View is inherited from its parent.
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_INHERIT = 0;
+
+ /**
+ * Default for the root view. The gravity determines the text alignment, ALIGN_NORMAL,
+ * ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s text direction.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_GRAVITY = 1;
+
+ /**
+ * Align to the start of the paragraph, e.g. ALIGN_NORMAL.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_TEXT_START = 2;
+
+ /**
+ * Align to the end of the paragraph, e.g. ALIGN_OPPOSITE.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_TEXT_END = 3;
+
+ /**
+ * Center the paragraph, e.g. ALIGN_CENTER.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_CENTER = 4;
+
+ /**
+ * Align to the start of the view, which is ALIGN_LEFT if the view’s resolved
+ * layoutDirection is LTR, and ALIGN_RIGHT otherwise.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_VIEW_START = 5;
+
+ /**
+ * Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved
+ * layoutDirection is LTR, and ALIGN_LEFT otherwise.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_VIEW_END = 6;
+
+ /**
+ * Default text alignment is inherited
+ */
+ protected static int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
+
+ /**
+ * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_MASK_SHIFT = 13;
+
+ /**
+ * Mask for use with private flags indicating bits used for text alignment.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_MASK = 0x00000007 << TEXT_ALIGNMENT_MASK_SHIFT;
+
+ /**
+ * Array of text direction flags for mapping attribute "textAlignment" to correct
+ * flag value.
+ * @hide
+ */
+ private static final int[] TEXT_ALIGNMENT_FLAGS = {
+ TEXT_ALIGNMENT_INHERIT << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_GRAVITY << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_TEXT_START << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_TEXT_END << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_CENTER << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_VIEW_START << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_VIEW_END << TEXT_ALIGNMENT_MASK_SHIFT
+ };
+
+ /**
+ * Indicates whether the view text alignment has been resolved.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_RESOLVED = 0x00000008 << TEXT_ALIGNMENT_MASK_SHIFT;
+
+ /**
+ * Bit shift to get the resolved text alignment.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT = 17;
+
+ /**
+ * Mask for use with private flags indicating bits used for text alignment.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_RESOLVED_MASK = 0x00000007 << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+
+ /**
+ * Indicates whether if the view text alignment has been resolved to gravity
+ */
+ public static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT =
+ TEXT_ALIGNMENT_GRAVITY << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+
/* End of masks for mPrivateFlags2 */
@@ -1909,43 +2024,124 @@
public static final int OVER_SCROLL_NEVER = 2;
/**
- * View has requested the system UI (status bar) to be visible (the default).
+ * Special constant for {@link #setSystemUiVisibility(int)}: View has
+ * requested the system UI (status bar) to be visible (the default).
*
* @see #setSystemUiVisibility(int)
*/
public static final int SYSTEM_UI_FLAG_VISIBLE = 0;
/**
- * View has requested the system UI to enter an unobtrusive "low profile" mode.
+ * Flag for {@link #setSystemUiVisibility(int)}: View has requested the
+ * system UI to enter an unobtrusive "low profile" mode.
*
- * This is for use in games, book readers, video players, or any other "immersive" application
- * where the usual system chrome is deemed too distracting.
+ * <p>This is for use in games, book readers, video players, or any other
+ * "immersive" application where the usual system chrome is deemed too distracting.
*
- * In low profile mode, the status bar and/or navigation icons may dim.
+ * <p>In low profile mode, the status bar and/or navigation icons may dim.
*
* @see #setSystemUiVisibility(int)
*/
public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001;
/**
- * View has requested that the system navigation be temporarily hidden.
+ * Flag for {@link #setSystemUiVisibility(int)}: View has requested that the
+ * system navigation be temporarily hidden.
*
- * This is an even less obtrusive state than that called for by
+ * <p>This is an even less obtrusive state than that called for by
* {@link #SYSTEM_UI_FLAG_LOW_PROFILE}; on devices that draw essential navigation controls
* (Home, Back, and the like) on screen, <code>SYSTEM_UI_FLAG_HIDE_NAVIGATION</code> will cause
* those to disappear. This is useful (in conjunction with the
- * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN FLAG_FULLSCREEN} and
+ * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN FLAG_FULLSCREEN} and
* {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN FLAG_LAYOUT_IN_SCREEN}
* window flags) for displaying content using every last pixel on the display.
*
- * There is a limitation: because navigation controls are so important, the least user
- * interaction will cause them to reappear immediately.
+ * <p>There is a limitation: because navigation controls are so important, the least user
+ * interaction will cause them to reappear immediately. When this happens, both
+ * this flag and {@link #SYSTEM_UI_FLAG_FULLSCREEN} will be cleared automatically,
+ * so that both elements reappear at the same time.
*
* @see #setSystemUiVisibility(int)
*/
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
/**
+ * Flag for {@link #setSystemUiVisibility(int)}: View has requested to go
+ * into the normal fullscreen mode so that its content can take over the screen
+ * while still allowing the user to interact with the application.
+ *
+ * <p>This has the same visual effect as
+ * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN
+ * WindowManager.LayoutParams.FLAG_FULLSCREEN},
+ * meaning that non-critical screen decorations (such as the status bar) will be
+ * hidden while the user is in the View's window, focusing the experience on
+ * that content. Unlike the window flag, if you are using ActionBar in
+ * overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY
+ * Window.FEATURE_ACTION_BAR_OVERLAY}, then enabling this flag will also
+ * hide the action bar.
+ *
+ * <p>This approach to going fullscreen is best used over the window flag when
+ * it is a transient state -- that is, the application does this at certain
+ * points in its user interaction where it wants to allow the user to focus
+ * on content, but not as a continuous state. For situations where the application
+ * would like to simply stay full screen the entire time (such as a game that
+ * wants to take over the screen), the
+ * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN window flag}
+ * is usually a better approach. The state set here will be removed by the system
+ * in various situations (such as the user moving to another application) like
+ * the other system UI states.
+ *
+ * <p>When using this flag, the application should provide some easy facility
+ * for the user to go out of it. A common example would be in an e-book
+ * reader, where tapping on the screen brings back whatever screen and UI
+ * decorations that had been hidden while the user was immersed in reading
+ * the book.
+ *
+ * @see #setSystemUiVisibility(int)
+ */
+ public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
+
+ /**
+ * Flag for {@link #setSystemUiVisibility(int)}: When using other layout
+ * flags, we would like a stable view of the content insets given to
+ * {@link #fitSystemWindows(Rect)}. This means that the insets seen there
+ * will always represent the worst case that the application can expect
+ * as a continue state. In practice this means with any of system bar,
+ * nav bar, and status bar shown, but not the space that would be needed
+ * for an input method.
+ *
+ * <p>If you are using ActionBar in
+ * overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY
+ * Window.FEATURE_ACTION_BAR_OVERLAY}, this flag will also impact the
+ * insets it adds to those given to the application.
+ */
+ public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100;
+
+ /**
+ * Flag for {@link #setSystemUiVisibility(int)}: View would like its window
+ * to be layed out as if it has requested
+ * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, even if it currently hasn't. This
+ * allows it to avoid artifacts when switching in and out of that mode, at
+ * the expense that some of its user interface may be covered by screen
+ * decorations when they are shown. You can perform layout of your inner
+ * UI elements to account for the navagation system UI through the
+ * {@link #fitSystemWindows(Rect)} method.
+ */
+ public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
+
+ /**
+ * Flag for {@link #setSystemUiVisibility(int)}: View would like its window
+ * to be layed out as if it has requested
+ * {@link #SYSTEM_UI_FLAG_FULLSCREEN}, even if it currently hasn't. This
+ * allows it to avoid artifacts when switching in and out of that mode, at
+ * the expense that some of its user interface may be covered by screen
+ * decorations when they are shown. You can perform layout of your inner
+ * UI elements to account for non-fullscreen system UI through the
+ * {@link #fitSystemWindows(Rect)} method.
+ */
+ public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
+
+ /**
* @deprecated Use {@link #SYSTEM_UI_FLAG_LOW_PROFILE} instead.
*/
public static final int STATUS_BAR_HIDDEN = SYSTEM_UI_FLAG_LOW_PROFILE;
@@ -2055,17 +2251,6 @@
/**
* @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility, etc. etc.
- *
- * This hides HOME and RECENT and is provided for compatibility with interim implementations.
- */
- @Deprecated
- public static final int STATUS_BAR_DISABLE_NAVIGATION =
- STATUS_BAR_DISABLE_HOME | STATUS_BAR_DISABLE_RECENT;
-
- /**
- * @hide
*/
public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x0000FFFF;
@@ -2076,7 +2261,15 @@
* @hide
*/
public static final int SYSTEM_UI_CLEARABLE_FLAGS =
- SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | SYSTEM_UI_FLAG_FULLSCREEN;
+
+ /**
+ * Flags that can impact the layout in relation to system UI.
+ */
+ public static final int SYSTEM_UI_LAYOUT_FLAGS =
+ SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
/**
* Find views that render the specified text.
@@ -2256,7 +2449,7 @@
*/
private int mPrevWidth = -1;
private int mPrevHeight = -1;
-
+
/**
* The degrees rotation around the vertical axis through the pivot point.
*/
@@ -2463,7 +2656,7 @@
*/
int mOldHeightMeasureSpec = Integer.MIN_VALUE;
- private Drawable mBGDrawable;
+ private Drawable mBackground;
private int mBackgroundResource;
private boolean mBackgroundSizeChanged;
@@ -2537,7 +2730,7 @@
/**
* Set to true when drawing cache is enabled and cannot be created.
- *
+ *
* @hide
*/
public boolean mCachingFailed;
@@ -2747,19 +2940,6 @@
private boolean mSendingHoverAccessibilityEvents;
/**
- * Delegate for injecting accessiblity functionality.
- */
- AccessibilityDelegate mAccessibilityDelegate;
-
- /**
- * Consistency verifier for debugging purposes.
- * @hide
- */
- protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
- InputEventConsistencyVerifier.isInstrumentationEnabled() ?
- new InputEventConsistencyVerifier(this, 0) : null;
-
- /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -2771,7 +2951,8 @@
mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
// Set layout and text direction defaults
mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << LAYOUT_DIRECTION_MASK_SHIFT) |
- (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT);
+ (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT) |
+ (TEXT_ALIGNMENT_DEFAULT << TEXT_ALIGNMENT_MASK_SHIFT);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
mUserPaddingStart = -1;
@@ -2780,6 +2961,19 @@
}
/**
+ * Delegate for injecting accessiblity functionality.
+ */
+ AccessibilityDelegate mAccessibilityDelegate;
+
+ /**
+ * Consistency verifier for debugging purposes.
+ * @hide
+ */
+ protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+ InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+ new InputEventConsistencyVerifier(this, 0) : null;
+
+ /**
* Constructor that is called when inflating a view from XML. This is called
* when a view is being constructed from an XML file, supplying attributes
* that were specified in the XML file. This version uses a default style of
@@ -3139,6 +3333,13 @@
mPrivateFlags2 |= TEXT_DIRECTION_FLAGS[textDirection];
}
break;
+ case R.styleable.View_textAlignment:
+ // Clear any text alignment flag already set
+ mPrivateFlags2 &= ~TEXT_ALIGNMENT_MASK;
+ // Set the text alignment flag depending on the value of the attribute
+ final int textAlignment = a.getInt(attr, TEXT_ALIGNMENT_DEFAULT);
+ mPrivateFlags2 |= TEXT_ALIGNMENT_FLAGS[textAlignment];
+ break;
}
}
@@ -3147,7 +3348,7 @@
setOverScrollMode(overScrollMode);
if (background != null) {
- setBackgroundDrawable(background);
+ setBackground(background);
}
// Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved
@@ -3411,6 +3612,11 @@
}
}
+ private ScrollabilityCache getScrollCache() {
+ initScrollCache();
+ return mScrollCache;
+ }
+
/**
* Set the position of the vertical scroll bar. Should be one of
* {@link #SCROLLBAR_POSITION_DEFAULT}, {@link #SCROLLBAR_POSITION_LEFT} or
@@ -3826,8 +4032,9 @@
* <p>
* <strong>Note:</strong> When a View clears focus the framework is trying
* to give focus to the first focusable View from the top. Hence, if this
- * View is the first from the top that can take focus, then its focus will
- * not be cleared nor will the focus change callback be invoked.
+ * View is the first from the top that can take focus, then all callbacks
+ * related to clearing focus will be invoked after wich the framework will
+ * give focus to this view.
* </p>
*/
public void clearFocus() {
@@ -3844,25 +4051,22 @@
onFocusChanged(false, 0, null);
refreshDrawableState();
+
+ ensureInputFocusOnFirstFocusable();
}
}
- /**
- * Called to clear the focus of a view that is about to be removed.
- * Doesn't call clearChildFocus, which prevents this view from taking
- * focus again before it has been removed from the parent
- */
- void clearFocusForRemoval() {
- if ((mPrivateFlags & FOCUSED) != 0) {
- mPrivateFlags &= ~FOCUSED;
-
- onFocusChanged(false, 0, null);
- refreshDrawableState();
-
- // The view cleared focus and invoked the callbacks, so now is the
- // time to give focus to the the first focusable from the top to
- // ensure that the gain focus is announced after clear focus.
- getRootView().requestFocus(FOCUS_FORWARD);
+ void ensureInputFocusOnFirstFocusable() {
+ View root = getRootView();
+ if (root != null) {
+ // Find the first focusble from the top.
+ View next = root.focusSearch(FOCUS_FORWARD);
+ if (next != null) {
+ // Giving focus to the found focusable will not
+ // perform a search since we found a view that is
+ // guaranteed to be able to take focus.
+ next.requestFocus(FOCUS_FORWARD);
+ }
}
}
@@ -4479,11 +4683,26 @@
}
/**
+ * Indicates whether this view is one of the set of scrollable containers in
+ * its window.
+ *
+ * @return whether this view is one of the set of scrollable containers in
+ * its window
+ *
+ * @attr ref android.R.styleable#View_isScrollContainer
+ */
+ public boolean isScrollContainer() {
+ return (mPrivateFlags & SCROLL_CONTAINER_ADDED) != 0;
+ }
+
+ /**
* Change whether this view is one of the set of scrollable containers in
* its window. This will be used to determine whether the window can
* resize or must pan when a soft input area is open -- scrollable
* containers allow the window to use resize mode since the container
* will appropriately shrink.
+ *
+ * @attr ref android.R.styleable#View_isScrollContainer
*/
public void setScrollContainer(boolean isScrollContainer) {
if (isScrollContainer) {
@@ -4692,21 +4911,54 @@
}
/**
- * Apply the insets for system windows to this view, if the FITS_SYSTEM_WINDOWS flag
- * is set
+ * Called by the view hierarchy when the content insets for a window have
+ * changed, to allow it to adjust its content to fit within those windows.
+ * The content insets tell you the space that the status bar, input method,
+ * and other system windows infringe on the application's window.
*
- * @param insets Insets for system windows
+ * <p>You do not normally need to deal with this function, since the default
+ * window decoration given to applications takes care of applying it to the
+ * content of the window. If you use {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}
+ * or {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION} this will not be the case,
+ * and your content can be placed under those system elements. You can then
+ * use this method within your view hierarchy if you have parts of your UI
+ * which you would like to ensure are not being covered.
*
- * @return True if this view applied the insets, false otherwise
+ * <p>The default implementation of this method simply applies the content
+ * inset's to the view's padding. This can be enabled through
+ * {@link #setFitsSystemWindows(boolean)}. Alternatively, you can override
+ * the method and handle the insets however you would like. Note that the
+ * insets provided by the framework are always relative to the far edges
+ * of the window, not accounting for the location of the called view within
+ * that window. (In fact when this method is called you do not yet know
+ * where the layout will place the view, as it is done before layout happens.)
+ *
+ * <p>Note: unlike many View methods, there is no dispatch phase to this
+ * call. If you are overriding it in a ViewGroup and want to allow the
+ * call to continue to your children, you must be sure to call the super
+ * implementation.
+ *
+ * @param insets Current content insets of the window. Prior to
+ * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} you must not modify
+ * the insets or else you and Android will be unhappy.
+ *
+ * @return Return true if this view applied the insets and it should not
+ * continue propagating further down the hierarchy, false otherwise.
*/
protected boolean fitSystemWindows(Rect insets) {
if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
- mPaddingLeft = insets.left;
- mPaddingTop = insets.top;
- mPaddingRight = insets.right;
- mPaddingBottom = insets.bottom;
- requestLayout();
- return true;
+ mUserPaddingStart = -1;
+ mUserPaddingEnd = -1;
+ mUserPaddingRelative = false;
+ if ((mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) == 0
+ || mAttachInfo == null
+ || (mAttachInfo.mSystemUiVisibility & SYSTEM_UI_LAYOUT_FLAGS) == 0) {
+ internalSetPadding(insets.left, insets.top, insets.right, insets.bottom);
+ return true;
+ } else {
+ internalSetPadding(0, 0, 0, 0);
+ return false;
+ }
}
return false;
}
@@ -4742,6 +4994,23 @@
}
/**
+ * Ask that a new dispatch of {@link #fitSystemWindows(Rect)} be performed.
+ */
+ public void requestFitSystemWindows() {
+ if (mParent != null) {
+ mParent.requestFitSystemWindows();
+ }
+ }
+
+ /**
+ * For use by PhoneWindow to make its own system window fitting optional.
+ * @hide
+ */
+ public void makeOptionalFitsSystemWindows() {
+ setFlags(OPTIONAL_FITS_SYSTEM_WINDOWS, OPTIONAL_FITS_SYSTEM_WINDOWS);
+ }
+
+ /**
* Returns the visibility status for this view.
*
* @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
@@ -4765,7 +5034,7 @@
@RemotableViewMethod
public void setVisibility(int visibility) {
setFlags(visibility, VISIBILITY_MASK);
- if (mBGDrawable != null) mBGDrawable.setVisible(visibility == VISIBLE, false);
+ if (mBackground != null) mBackground.setVisible(visibility == VISIBLE, false);
}
/**
@@ -5146,7 +5415,7 @@
* {@link #setPressed(boolean)} is explicitly called, only clickable views can enter
* the pressed state.
*
- * @see #setPressed(boolean)
+ * @see #setPressed(boolean)
* @see #isClickable()
* @see #setClickable(boolean)
*
@@ -5832,7 +6101,7 @@
/**
* Dispatch a hover event.
* <p>
- * Do not call this method directly.
+ * Do not call this method directly.
* Call {@link #dispatchGenericMotionEvent(MotionEvent)} instead.
* </p>
*
@@ -6019,7 +6288,7 @@
*
* @param visibility The new visibility of the window.
*
- * @see #onWindowVisibilityChanged(int)
+ * @see #onWindowVisibilityChanged(int)
*/
public void dispatchWindowVisibilityChanged(int visibility) {
onWindowVisibilityChanged(visibility);
@@ -6095,7 +6364,7 @@
*
* @param newConfig The new resource configuration.
*
- * @see #onConfigurationChanged(android.content.res.Configuration)
+ * @see #onConfigurationChanged(android.content.res.Configuration)
*/
public void dispatchConfigurationChanged(Configuration newConfig) {
onConfigurationChanged(newConfig);
@@ -6118,19 +6387,19 @@
* Private function to aggregate all per-view attributes in to the view
* root.
*/
- void dispatchCollectViewAttributes(int visibility) {
- performCollectViewAttributes(visibility);
+ void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
+ performCollectViewAttributes(attachInfo, visibility);
}
- void performCollectViewAttributes(int visibility) {
- if ((visibility & VISIBILITY_MASK) == VISIBLE && mAttachInfo != null) {
+ void performCollectViewAttributes(AttachInfo attachInfo, int visibility) {
+ if ((visibility & VISIBILITY_MASK) == VISIBLE) {
if ((mViewFlags & KEEP_SCREEN_ON) == KEEP_SCREEN_ON) {
- mAttachInfo.mKeepScreenOn = true;
+ attachInfo.mKeepScreenOn = true;
}
- mAttachInfo.mSystemUiVisibility |= mSystemUiVisibility;
+ attachInfo.mSystemUiVisibility |= mSystemUiVisibility;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnSystemUiVisibilityChangeListener != null) {
- mAttachInfo.mHasSystemUiListeners = true;
+ attachInfo.mHasSystemUiListeners = true;
}
}
}
@@ -6963,7 +7232,7 @@
if ((changed & DRAW_MASK) != 0) {
if ((mViewFlags & WILL_NOT_DRAW) != 0) {
- if (mBGDrawable != null) {
+ if (mBackground != null) {
mPrivateFlags &= ~SKIP_DRAW;
mPrivateFlags |= ONLY_DRAWS_BACKGROUND;
} else {
@@ -7351,39 +7620,39 @@
* views are drawn) from the camera to this view. The camera's distance
* affects 3D transformations, for instance rotations around the X and Y
* axis. If the rotationX or rotationY properties are changed and this view is
- * large (more than half the size of the screen), it is recommended to always
+ * large (more than half the size of the screen), it is recommended to always
* use a camera distance that's greater than the height (X axis rotation) or
* the width (Y axis rotation) of this view.</p>
- *
+ *
* <p>The distance of the camera from the view plane can have an affect on the
* perspective distortion of the view when it is rotated around the x or y axis.
* For example, a large distance will result in a large viewing angle, and there
* will not be much perspective distortion of the view as it rotates. A short
- * distance may cause much more perspective distortion upon rotation, and can
+ * distance may cause much more perspective distortion upon rotation, and can
* also result in some drawing artifacts if the rotated view ends up partially
* behind the camera (which is why the recommendation is to use a distance at
* least as far as the size of the view, if the view is to be rotated.)</p>
- *
+ *
* <p>The distance is expressed in "depth pixels." The default distance depends
* on the screen density. For instance, on a medium density display, the
* default distance is 1280. On a high density display, the default distance
* is 1920.</p>
- *
+ *
* <p>If you want to specify a distance that leads to visually consistent
* results across various densities, use the following formula:</p>
* <pre>
* float scale = context.getResources().getDisplayMetrics().density;
* view.setCameraDistance(distance * scale);
* </pre>
- *
+ *
* <p>The density scale factor of a high density display is 1.5,
* and 1920 = 1280 * 1.5.</p>
- *
+ *
* @param distance The distance in "depth pixels", if negative the opposite
* value is used
- *
- * @see #setRotationX(float)
- * @see #setRotationY(float)
+ *
+ * @see #setRotationX(float)
+ * @see #setRotationY(float)
*/
public void setCameraDistance(float distance) {
invalidateViewProperty(true, false);
@@ -7408,10 +7677,10 @@
/**
* The degrees that the view is rotated around the pivot point.
*
- * @see #setRotation(float)
+ * @see #setRotation(float)
* @see #getPivotX()
* @see #getPivotY()
- *
+ *
* @return The degrees of rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
@@ -7424,12 +7693,12 @@
* result in clockwise rotation.
*
* @param rotation The degrees of rotation.
- *
- * @see #getRotation()
+ *
+ * @see #getRotation()
* @see #getPivotX()
* @see #getPivotY()
- * @see #setRotationX(float)
- * @see #setRotationY(float)
+ * @see #setRotationX(float)
+ * @see #setRotationY(float)
*
* @attr ref android.R.styleable#View_rotation
*/
@@ -7453,8 +7722,8 @@
*
* @see #getPivotX()
* @see #getPivotY()
- * @see #setRotationY(float)
- *
+ * @see #setRotationY(float)
+ *
* @return The degrees of Y rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
@@ -7466,18 +7735,18 @@
* Sets the degrees that the view is rotated around the vertical axis through the pivot point.
* Increasing values result in counter-clockwise rotation from the viewpoint of looking
* down the y axis.
- *
+ *
* When rotating large views, it is recommended to adjust the camera distance
* accordingly. Refer to {@link #setCameraDistance(float)} for more information.
*
* @param rotationY The degrees of Y rotation.
- *
- * @see #getRotationY()
+ *
+ * @see #getRotationY()
* @see #getPivotX()
* @see #getPivotY()
* @see #setRotation(float)
- * @see #setRotationX(float)
- * @see #setCameraDistance(float)
+ * @see #setRotationX(float)
+ * @see #setCameraDistance(float)
*
* @attr ref android.R.styleable#View_rotationY
*/
@@ -7500,8 +7769,8 @@
*
* @see #getPivotX()
* @see #getPivotY()
- * @see #setRotationX(float)
- *
+ * @see #setRotationX(float)
+ *
* @return The degrees of X rotation.
*/
@ViewDebug.ExportedProperty(category = "drawing")
@@ -7513,18 +7782,18 @@
* Sets the degrees that the view is rotated around the horizontal axis through the pivot point.
* Increasing values result in clockwise rotation from the viewpoint of looking down the
* x axis.
- *
+ *
* When rotating large views, it is recommended to adjust the camera distance
* accordingly. Refer to {@link #setCameraDistance(float)} for more information.
*
* @param rotationX The degrees of X rotation.
- *
- * @see #getRotationX()
+ *
+ * @see #getRotationX()
* @see #getPivotX()
* @see #getPivotY()
* @see #setRotation(float)
- * @see #setRotationY(float)
- * @see #setCameraDistance(float)
+ * @see #setRotationY(float)
+ * @see #setCameraDistance(float)
*
* @attr ref android.R.styleable#View_rotationX
*/
@@ -7629,6 +7898,8 @@
* @see #getScaleY()
* @see #getPivotY()
* @return The x location of the pivot point.
+ *
+ * @attr ref android.R.styleable#View_transformPivotX
*/
@ViewDebug.ExportedProperty(category = "drawing")
public float getPivotX() {
@@ -7674,6 +7945,8 @@
* @see #getScaleY()
* @see #getPivotY()
* @return The y location of the pivot point.
+ *
+ * @attr ref android.R.styleable#View_transformPivotY
*/
@ViewDebug.ExportedProperty(category = "drawing")
public float getPivotY() {
@@ -7722,6 +7995,23 @@
}
/**
+ * Returns whether this View has content which overlaps. This function, intended to be
+ * overridden by specific View types, is an optimization when alpha is set on a view. If
+ * rendering overlaps in a view with alpha < 1, that view is drawn to an offscreen buffer
+ * and then composited it into place, which can be expensive. If the view has no overlapping
+ * rendering, the view can draw each primitive with the appropriate alpha value directly.
+ * An example of overlapping rendering is a TextView with a background image, such as a
+ * Button. An example of non-overlapping rendering is a TextView with no background, or
+ * an ImageView with only the foreground image. The default implementation returns true;
+ * subclasses should override if they have cases which can be optimized.
+ *
+ * @return true if the content in this view might overlap, false otherwise.
+ */
+ public boolean hasOverlappingRendering() {
+ return true;
+ }
+
+ /**
* <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is
* completely transparent and 1 means the view is completely opaque.</p>
*
@@ -8872,7 +9162,7 @@
// - Background is opaque
// - Doesn't have scrollbars or scrollbars are inside overlay
- if (mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE) {
+ if (mBackground != null && mBackground.getOpacity() == PixelFormat.OPAQUE) {
mPrivateFlags |= OPAQUE_BACKGROUND;
} else {
mPrivateFlags &= ~OPAQUE_BACKGROUND;
@@ -8920,7 +9210,7 @@
/**
* <p>Causes the Runnable to be added to the message queue.
* The runnable will be run on the user interface thread.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -8944,7 +9234,7 @@
* <p>Causes the Runnable to be added to the message queue, to be run
* after the specified amount of time elapses.
* The runnable will be run on the user interface thread.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -9018,7 +9308,7 @@
/**
* <p>Removes the specified Runnable from the message queue.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -9050,7 +9340,7 @@
*
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
- *
+ *
* @see #invalidate()
*/
public void postInvalidate() {
@@ -9060,7 +9350,7 @@
/**
* <p>Cause an invalidate of the specified area to happen on a subsequent cycle
* through the event loop. Use this to invalidate the View from a non-UI thread.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -9079,7 +9369,7 @@
/**
* <p>Cause an invalidate to happen on a subsequent cycle through the event
* loop. Waits for the specified amount of time.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -9098,7 +9388,7 @@
/**
* <p>Cause an invalidate of the specified area to happen on a subsequent cycle
* through the event loop. Waits for the specified amount of time.</p>
- *
+ *
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
@@ -9208,6 +9498,7 @@
* otherwise
*
* @see #setHorizontalFadingEdgeEnabled(boolean)
+ *
* @attr ref android.R.styleable#View_requiresFadingEdge
*/
public boolean isHorizontalFadingEdgeEnabled() {
@@ -9223,6 +9514,7 @@
* horizontally
*
* @see #isHorizontalFadingEdgeEnabled()
+ *
* @attr ref android.R.styleable#View_requiresFadingEdge
*/
public void setHorizontalFadingEdgeEnabled(boolean horizontalFadingEdgeEnabled) {
@@ -9243,6 +9535,7 @@
* otherwise
*
* @see #setVerticalFadingEdgeEnabled(boolean)
+ *
* @attr ref android.R.styleable#View_requiresFadingEdge
*/
public boolean isVerticalFadingEdgeEnabled() {
@@ -9258,6 +9551,7 @@
* vertically
*
* @see #isVerticalFadingEdgeEnabled()
+ *
* @attr ref android.R.styleable#View_requiresFadingEdge
*/
public void setVerticalFadingEdgeEnabled(boolean verticalFadingEdgeEnabled) {
@@ -9400,6 +9694,7 @@
*
* @param fadeScrollbars wheter to enable fading
*
+ * @attr ref android.R.styleable#View_fadeScrollbars
*/
public void setScrollbarFadingEnabled(boolean fadeScrollbars) {
initScrollCache();
@@ -9417,12 +9712,86 @@
* Returns true if scrollbars will fade when this view is not scrolling
*
* @return true if scrollbar fading is enabled
+ *
+ * @attr ref android.R.styleable#View_fadeScrollbars
*/
public boolean isScrollbarFadingEnabled() {
return mScrollCache != null && mScrollCache.fadeScrollBars;
}
/**
+ *
+ * Returns the delay before scrollbars fade.
+ *
+ * @return the delay before scrollbars fade
+ *
+ * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
+ */
+ public int getScrollBarDefaultDelayBeforeFade() {
+ return mScrollCache == null ? ViewConfiguration.getScrollDefaultDelay() :
+ mScrollCache.scrollBarDefaultDelayBeforeFade;
+ }
+
+ /**
+ * Define the delay before scrollbars fade.
+ *
+ * @param scrollBarDefaultDelayBeforeFade - the delay before scrollbars fade
+ *
+ * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade
+ */
+ public void setScrollBarDefaultDelayBeforeFade(int scrollBarDefaultDelayBeforeFade) {
+ getScrollCache().scrollBarDefaultDelayBeforeFade = scrollBarDefaultDelayBeforeFade;
+ }
+
+ /**
+ *
+ * Returns the scrollbar fade duration.
+ *
+ * @return the scrollbar fade duration
+ *
+ * @attr ref android.R.styleable#View_scrollbarFadeDuration
+ */
+ public int getScrollBarFadeDuration() {
+ return mScrollCache == null ? ViewConfiguration.getScrollBarFadeDuration() :
+ mScrollCache.scrollBarFadeDuration;
+ }
+
+ /**
+ * Define the scrollbar fade duration.
+ *
+ * @param scrollBarFadeDuration - the scrollbar fade duration
+ *
+ * @attr ref android.R.styleable#View_scrollbarFadeDuration
+ */
+ public void setScrollBarFadeDuration(int scrollBarFadeDuration) {
+ getScrollCache().scrollBarFadeDuration = scrollBarFadeDuration;
+ }
+
+ /**
+ *
+ * Returns the scrollbar size.
+ *
+ * @return the scrollbar size
+ *
+ * @attr ref android.R.styleable#View_scrollbarSize
+ */
+ public int getScrollBarSize() {
+ return mScrollCache == null ? ViewConfiguration.getScrollBarSize() :
+ mScrollCache.scrollBarSize;
+ }
+
+ /**
+ * Define the scrollbar size.
+ *
+ * @param scrollBarSize - the scrollbar size
+ *
+ * @attr ref android.R.styleable#View_scrollbarSize
+ */
+ public void setScrollBarSize(int scrollBarSize) {
+ getScrollCache().scrollBarSize = scrollBarSize;
+ }
+
+ /**
* <p>Specify the style of the scrollbars. The scrollbars can be overlaid or
* inset. When inset, they add to the padding of the view. And the scrollbars
* can be drawn inside the padding area or on the edge of the view. For example,
@@ -9438,6 +9807,8 @@
* @see #SCROLLBARS_INSIDE_INSET
* @see #SCROLLBARS_OUTSIDE_OVERLAY
* @see #SCROLLBARS_OUTSIDE_INSET
+ *
+ * @attr ref android.R.styleable#View_scrollbarStyle
*/
public void setScrollBarStyle(int style) {
if (style != (mViewFlags & SCROLLBARS_STYLE_MASK)) {
@@ -9454,6 +9825,8 @@
* @see #SCROLLBARS_INSIDE_INSET
* @see #SCROLLBARS_OUTSIDE_OVERLAY
* @see #SCROLLBARS_OUTSIDE_INSET
+ *
+ * @attr ref android.R.styleable#View_scrollbarStyle
*/
@ViewDebug.ExportedProperty(mapping = {
@ViewDebug.IntToString(from = SCROLLBARS_INSIDE_OVERLAY, to = "INSIDE_OVERLAY"),
@@ -9839,6 +10212,7 @@
resolveLayoutDirection();
resolvePadding();
resolveTextDirection();
+ resolveTextAlignment();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
@@ -9865,6 +10239,13 @@
}
/**
+ * Return true if the application tag in the AndroidManifest has set "supportRtl" to true
+ */
+ private boolean hasRtlSupport() {
+ return mContext.getApplicationInfo().hasRtlSupport();
+ }
+
+ /**
* Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing
* that the parent directionality can and will be resolved before its children.
* Will call {@link View#onResolvedLayoutDirectionChanged} when resolution is done.
@@ -9873,30 +10254,32 @@
// Clear any previous layout direction resolution
mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED_MASK;
- // Set resolved depending on layout direction
- switch (getLayoutDirection()) {
- case LAYOUT_DIRECTION_INHERIT:
- // If this is root view, no need to look at parent's layout dir.
- if (canResolveLayoutDirection()) {
- ViewGroup viewGroup = ((ViewGroup) mParent);
+ if (hasRtlSupport()) {
+ // Set resolved depending on layout direction
+ switch (getLayoutDirection()) {
+ case LAYOUT_DIRECTION_INHERIT:
+ // If this is root view, no need to look at parent's layout dir.
+ if (canResolveLayoutDirection()) {
+ ViewGroup viewGroup = ((ViewGroup) mParent);
- if (viewGroup.getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+ if (viewGroup.getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+ mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
+ }
+ } else {
+ // Nothing to do, LTR by default
+ }
+ break;
+ case LAYOUT_DIRECTION_RTL:
+ mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
+ break;
+ case LAYOUT_DIRECTION_LOCALE:
+ if(isLayoutDirectionRtl(Locale.getDefault())) {
mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
}
- } else {
+ break;
+ default:
// Nothing to do, LTR by default
- }
- break;
- case LAYOUT_DIRECTION_RTL:
- mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
- break;
- case LAYOUT_DIRECTION_LOCALE:
- if(isLayoutDirectionRtl(Locale.getDefault())) {
- mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
- }
- break;
- default:
- // Nothing to do, LTR by default
+ }
}
// Set to resolved
@@ -10059,6 +10442,7 @@
mCurrentAnimation = null;
resetResolvedLayoutDirection();
+ resetResolvedTextAlignment();
}
/**
@@ -10126,7 +10510,7 @@
mAttachInfo.mScrollContainers.add(this);
mPrivateFlags |= SCROLL_CONTAINER_ADDED;
}
- performCollectViewAttributes(visibility);
+ performCollectViewAttributes(mAttachInfo, visibility);
onAttachedToWindow();
ListenerInfo li = mListenerInfo;
@@ -10189,9 +10573,9 @@
*
* @param container The SparseArray in which to save the view's state.
*
- * @see #restoreHierarchyState(android.util.SparseArray)
- * @see #dispatchSaveInstanceState(android.util.SparseArray)
- * @see #onSaveInstanceState()
+ * @see #restoreHierarchyState(android.util.SparseArray)
+ * @see #dispatchSaveInstanceState(android.util.SparseArray)
+ * @see #onSaveInstanceState()
*/
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
@@ -10204,9 +10588,9 @@
*
* @param container The SparseArray in which to save the view's state.
*
- * @see #dispatchRestoreInstanceState(android.util.SparseArray)
- * @see #saveHierarchyState(android.util.SparseArray)
- * @see #onSaveInstanceState()
+ * @see #dispatchRestoreInstanceState(android.util.SparseArray)
+ * @see #saveHierarchyState(android.util.SparseArray)
+ * @see #onSaveInstanceState()
*/
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
@@ -10240,9 +10624,9 @@
* @return Returns a Parcelable object containing the view's current dynamic
* state, or null if there is nothing interesting to save. The
* default implementation returns null.
- * @see #onRestoreInstanceState(android.os.Parcelable)
- * @see #saveHierarchyState(android.util.SparseArray)
- * @see #dispatchSaveInstanceState(android.util.SparseArray)
+ * @see #onRestoreInstanceState(android.os.Parcelable)
+ * @see #saveHierarchyState(android.util.SparseArray)
+ * @see #dispatchSaveInstanceState(android.util.SparseArray)
* @see #setSaveEnabled(boolean)
*/
protected Parcelable onSaveInstanceState() {
@@ -10255,9 +10639,9 @@
*
* @param container The SparseArray which holds previously frozen states.
*
- * @see #saveHierarchyState(android.util.SparseArray)
- * @see #dispatchRestoreInstanceState(android.util.SparseArray)
- * @see #onRestoreInstanceState(android.os.Parcelable)
+ * @see #saveHierarchyState(android.util.SparseArray)
+ * @see #dispatchRestoreInstanceState(android.util.SparseArray)
+ * @see #onRestoreInstanceState(android.os.Parcelable)
*/
public void restoreHierarchyState(SparseArray<Parcelable> container) {
dispatchRestoreInstanceState(container);
@@ -10271,9 +10655,9 @@
*
* @param container The SparseArray which holds previously saved state.
*
- * @see #dispatchSaveInstanceState(android.util.SparseArray)
- * @see #restoreHierarchyState(android.util.SparseArray)
- * @see #onRestoreInstanceState(android.os.Parcelable)
+ * @see #dispatchSaveInstanceState(android.util.SparseArray)
+ * @see #restoreHierarchyState(android.util.SparseArray)
+ * @see #onRestoreInstanceState(android.os.Parcelable)
*/
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID) {
@@ -10299,9 +10683,9 @@
* @param state The frozen state that had previously been returned by
* {@link #onSaveInstanceState}.
*
- * @see #onSaveInstanceState()
- * @see #restoreHierarchyState(android.util.SparseArray)
- * @see #dispatchRestoreInstanceState(android.util.SparseArray)
+ * @see #onSaveInstanceState()
+ * @see #restoreHierarchyState(android.util.SparseArray)
+ * @see #dispatchRestoreInstanceState(android.util.SparseArray)
*/
protected void onRestoreInstanceState(Parcelable state) {
mPrivateFlags |= SAVE_STATE_CALLED;
@@ -10455,7 +10839,7 @@
* {@link #LAYER_TYPE_HARDWARE}
*
* @see #setLayerType(int, android.graphics.Paint)
- * @see #buildLayer()
+ * @see #buildLayer()
* @see #LAYER_TYPE_NONE
* @see #LAYER_TYPE_SOFTWARE
* @see #LAYER_TYPE_HARDWARE
@@ -10468,14 +10852,14 @@
* Forces this view's layer to be created and this view to be rendered
* into its layer. If this view's layer type is set to {@link #LAYER_TYPE_NONE},
* invoking this method will have no effect.
- *
+ *
* This method can for instance be used to render a view into its layer before
* starting an animation. If this view is complex, rendering into the layer
* before starting the animation will avoid skipping frames.
- *
+ *
* @throws IllegalStateException If this view is not attached to a window
- *
- * @see #setLayerType(int, android.graphics.Paint)
+ *
+ * @see #setLayerType(int, android.graphics.Paint)
*/
public void buildLayer() {
if (mLayerType == LAYER_TYPE_NONE) return;
@@ -10497,7 +10881,7 @@
break;
}
}
-
+
// Make sure the HardwareRenderer.validate() was invoked before calling this method
void flushLayer() {
if (mLayerType == LAYER_TYPE_HARDWARE && mHardwareLayer != null) {
@@ -10516,7 +10900,7 @@
!mAttachInfo.mHardwareRenderer.isEnabled()) {
return null;
}
-
+
if (!mAttachInfo.mHardwareRenderer.validate()) return null;
final int width = mRight - mLeft;
@@ -10550,10 +10934,10 @@
/**
* Destroys this View's hardware layer if possible.
- *
+ *
* @return True if the layer was destroyed, false otherwise.
- *
- * @see #setLayerType(int, android.graphics.Paint)
+ *
+ * @see #setLayerType(int, android.graphics.Paint)
* @see #LAYER_TYPE_HARDWARE
*/
boolean destroyLayer(boolean valid) {
@@ -10577,11 +10961,11 @@
* Destroys all hardware rendering resources. This method is invoked
* when the system needs to reclaim resources. Upon execution of this
* method, you should free any OpenGL resources created by the view.
- *
+ *
* Note: you <strong>must</strong> call
* <code>super.destroyHardwareResources()</code> when overriding
* this method.
- *
+ *
* @hide
*/
protected void destroyHardwareResources() {
@@ -11292,17 +11676,17 @@
if (offsetRequired) top += getTopPaddingOffset();
return top;
}
-
+
/**
* @hide
* @param offsetRequired
*/
protected int getFadeHeight(boolean offsetRequired) {
int padding = mPaddingTop;
- if (offsetRequired) padding += getTopPaddingOffset();
+ if (offsetRequired) padding += getTopPaddingOffset();
return mBottom - mTop - mPaddingBottom - padding;
}
-
+
/**
* <p>Indicates whether this view is attached to a hardware accelerated
* window or not.</p>
@@ -11392,16 +11776,39 @@
void setDisplayListProperties(DisplayList displayList) {
if (USE_DISPLAY_LIST_PROPERTIES && displayList != null) {
displayList.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
+ displayList.setHasOverlappingRendering(hasOverlappingRendering());
if (mParent instanceof ViewGroup) {
displayList.setClipChildren(
(((ViewGroup)mParent).mGroupFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0);
}
- if (mAttachInfo != null && mAttachInfo.mScalingRequired &&
- mAttachInfo.mApplicationScale != 1.0f) {
- displayList.setApplicationScale(1f / mAttachInfo.mApplicationScale);
+ float alpha = 1;
+ if (mParent instanceof ViewGroup && (((ViewGroup) mParent).mGroupFlags &
+ ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
+ ViewGroup parentVG = (ViewGroup) mParent;
+ final boolean hasTransform =
+ parentVG.getChildStaticTransformation(this, parentVG.mChildTransformation);
+ if (hasTransform) {
+ Transformation transform = parentVG.mChildTransformation;
+ final int transformType = parentVG.mChildTransformation.getTransformationType();
+ if (transformType != Transformation.TYPE_IDENTITY) {
+ if ((transformType & Transformation.TYPE_ALPHA) != 0) {
+ alpha = transform.getAlpha();
+ }
+ if ((transformType & Transformation.TYPE_MATRIX) != 0) {
+ displayList.setStaticMatrix(transform.getMatrix());
+ }
+ }
+ }
}
if (mTransformationInfo != null) {
- displayList.setTransformationInfo(mTransformationInfo.mAlpha,
+ alpha *= mTransformationInfo.mAlpha;
+ if (alpha < 1) {
+ final int multipliedAlpha = (int) (255 * alpha);
+ if (onSetAlpha(multipliedAlpha)) {
+ alpha = 1;
+ }
+ }
+ displayList.setTransformationInfo(alpha,
mTransformationInfo.mTranslationX, mTransformationInfo.mTranslationY,
mTransformationInfo.mRotation, mTransformationInfo.mRotationX,
mTransformationInfo.mRotationY, mTransformationInfo.mScaleX,
@@ -11415,6 +11822,8 @@
displayList.setPivotX(getPivotX());
displayList.setPivotY(getPivotY());
}
+ } else if (alpha < 1) {
+ displayList.setAlpha(alpha);
}
}
}
@@ -11447,6 +11856,7 @@
if ((flags & ViewGroup.FLAG_CHILDREN_DRAWN_WITH_CACHE) != 0 ||
(flags & ViewGroup.FLAG_ALWAYS_DRAWN_WITH_CACHE) != 0) {
caching = true;
+ // Auto-scaled apps are not hw-accelerated, no need to set scaling flag on DisplayList
if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
} else {
caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated;
@@ -11457,7 +11867,8 @@
more = drawAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
transformToApply = parent.mChildTransformation;
- } else if ((flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
+ } else if (!useDisplayListProperties &&
+ (flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
final boolean hasTransform =
parent.getChildStaticTransformation(this, parent.mChildTransformation);
if (hasTransform) {
@@ -11525,6 +11936,17 @@
}
}
useDisplayListProperties &= hasDisplayList;
+ if (useDisplayListProperties) {
+ displayList = getDisplayList();
+ if (!displayList.isValid()) {
+ // Uncommon, but possible. If a view is removed from the hierarchy during the call
+ // to getDisplayList(), the display list will be marked invalid and we should not
+ // try to use it again.
+ displayList = null;
+ hasDisplayList = false;
+ useDisplayListProperties = false;
+ }
+ }
final boolean hasNoCache = cache == null || hasDisplayList;
final boolean offsetForScroll = cache == null && !hasDisplayList &&
@@ -11542,6 +11964,7 @@
}
if (scalingRequired) {
if (useDisplayListProperties) {
+ // TODO: Might not need this if we put everything inside the DL
restoreTo = canvas.save();
}
// mAttachInfo cannot be null, otherwise scalingRequired == false
@@ -11551,7 +11974,7 @@
}
float alpha = useDisplayListProperties ? 1 : getAlpha();
- if (transformToApply != null || alpha < 1.0f || !hasIdentityMatrix()) {
+ if (transformToApply != null || alpha < 1 || !hasIdentityMatrix()) {
if (transformToApply != null || !childHasIdentityMatrix) {
int transX = 0;
int transY = 0;
@@ -11563,16 +11986,20 @@
if (transformToApply != null) {
if (concatMatrix) {
- // Undo the scroll translation, apply the transformation matrix,
- // then redo the scroll translate to get the correct result.
- canvas.translate(-transX, -transY);
- canvas.concat(transformToApply.getMatrix());
- canvas.translate(transX, transY);
+ if (useDisplayListProperties) {
+ displayList.setAnimationMatrix(transformToApply.getMatrix());
+ } else {
+ // Undo the scroll translation, apply the transformation matrix,
+ // then redo the scroll translate to get the correct result.
+ canvas.translate(-transX, -transY);
+ canvas.concat(transformToApply.getMatrix());
+ canvas.translate(transX, transY);
+ }
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
float transformAlpha = transformToApply.getAlpha();
- if (transformAlpha < 1.0f) {
+ if (transformAlpha < 1) {
alpha *= transformToApply.getAlpha();
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
@@ -11585,7 +12012,7 @@
}
}
- if (alpha < 1.0f) {
+ if (alpha < 1) {
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
if (hasNoCache) {
final int multipliedAlpha = (int) (255 * alpha);
@@ -11595,7 +12022,9 @@
layerType != LAYER_TYPE_NONE) {
layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
}
- if (layerType == LAYER_TYPE_NONE) {
+ if (useDisplayListProperties) {
+ displayList.setAlpha(alpha * getAlpha());
+ } else if (layerType == LAYER_TYPE_NONE) {
final int scrollX = hasDisplayList ? 0 : sx;
final int scrollY = hasDisplayList ? 0 : sy;
canvas.saveLayerAlpha(scrollX, scrollY, scrollX + mRight - mLeft,
@@ -11625,7 +12054,7 @@
}
}
- if (hasDisplayList) {
+ if (!useDisplayListProperties && hasDisplayList) {
displayList = getDisplayList();
if (!displayList.isValid()) {
// Uncommon, but possible. If a view is removed from the hierarchy during the call
@@ -11682,7 +12111,7 @@
cachePaint.setDither(false);
parent.mCachePaint = cachePaint;
}
- if (alpha < 1.0f) {
+ if (alpha < 1) {
cachePaint.setAlpha((int) (alpha * 255));
parent.mGroupFlags |= ViewGroup.FLAG_ALPHA_LOWER_THAN_ONE;
} else if ((flags & ViewGroup.FLAG_ALPHA_LOWER_THAN_ONE) != 0) {
@@ -11758,7 +12187,7 @@
int saveCount;
if (!dirtyOpaque) {
- final Drawable background = mBGDrawable;
+ final Drawable background = mBackground;
if (background != null) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
@@ -11832,7 +12261,7 @@
}
final ScrollabilityCache scrollabilityCache = mScrollCache;
- final float fadeHeight = scrollabilityCache.fadingEdgeLength;
+ final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
// clip the fade length if top and bottom fades overlap
@@ -11939,8 +12368,8 @@
* optimize the drawing of the fading edges. If you do return a non-zero color, the alpha
* should be set to 0xFF.
*
- * @see #setVerticalFadingEdgeEnabled(boolean)
- * @see #setHorizontalFadingEdgeEnabled(boolean)
+ * @see #setVerticalFadingEdgeEnabled(boolean)
+ * @see #setHorizontalFadingEdgeEnabled(boolean)
*
* @return The known solid color background for this view, or 0 if the color may vary
*/
@@ -12289,7 +12718,7 @@
* @param who the Drawable to query
*/
public int getResolvedLayoutDirection(Drawable who) {
- return (who == mBGDrawable) ? getResolvedLayoutDirection() : LAYOUT_DIRECTION_DEFAULT;
+ return (who == mBackground) ? getResolvedLayoutDirection() : LAYOUT_DIRECTION_DEFAULT;
}
/**
@@ -12308,11 +12737,11 @@
* @return boolean If true than the Drawable is being displayed in the
* view; else false and it is not allowed to animate.
*
- * @see #unscheduleDrawable(android.graphics.drawable.Drawable)
- * @see #drawableStateChanged()
+ * @see #unscheduleDrawable(android.graphics.drawable.Drawable)
+ * @see #drawableStateChanged()
*/
protected boolean verifyDrawable(Drawable who) {
- return who == mBGDrawable;
+ return who == mBackground;
}
/**
@@ -12322,10 +12751,10 @@
* <p>Be sure to call through to the superclass when overriding this
* function.
*
- * @see Drawable#setState(int[])
+ * @see Drawable#setState(int[])
*/
protected void drawableStateChanged() {
- Drawable d = mBGDrawable;
+ Drawable d = mBackground;
if (d != null && d.isStateful()) {
d.setState(getDrawableState());
}
@@ -12355,9 +12784,9 @@
*
* @return The current drawable state
*
- * @see Drawable#setState(int[])
- * @see #drawableStateChanged()
- * @see #onCreateDrawableState(int)
+ * @see Drawable#setState(int[])
+ * @see #drawableStateChanged()
+ * @see #onCreateDrawableState(int)
*/
public final int[] getDrawableState() {
if ((mDrawableState != null) && ((mPrivateFlags & DRAWABLE_STATE_DIRTY) == 0)) {
@@ -12382,7 +12811,7 @@
* @return Returns an array holding the current {@link Drawable} state of
* the view.
*
- * @see #mergeDrawableStates(int[], int[])
+ * @see #mergeDrawableStates(int[], int[])
*/
protected int[] onCreateDrawableState(int extraSpace) {
if ((mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE &&
@@ -12458,7 +12887,7 @@
* @return As a convenience, the <var>baseState</var> array you originally
* passed into the function is returned.
*
- * @see #onCreateDrawableState(int)
+ * @see #onCreateDrawableState(int)
*/
protected static int[] mergeDrawableStates(int[] baseState, int[] additionalState) {
final int N = baseState.length;
@@ -12475,8 +12904,8 @@
* on all Drawable objects associated with this view.
*/
public void jumpDrawablesToCurrentState() {
- if (mBGDrawable != null) {
- mBGDrawable.jumpToCurrentState();
+ if (mBackground != null) {
+ mBackground.jumpToCurrentState();
}
}
@@ -12486,10 +12915,10 @@
*/
@RemotableViewMethod
public void setBackgroundColor(int color) {
- if (mBGDrawable instanceof ColorDrawable) {
- ((ColorDrawable) mBGDrawable).setColor(color);
+ if (mBackground instanceof ColorDrawable) {
+ ((ColorDrawable) mBackground).setColor(color);
} else {
- setBackgroundDrawable(new ColorDrawable(color));
+ setBackground(new ColorDrawable(color));
}
}
@@ -12497,6 +12926,7 @@
* Set the background to a given resource. The resource should refer to
* a Drawable object or 0 to remove the background.
* @param resid The identifier of the resource.
+ *
* @attr ref android.R.styleable#View_background
*/
@RemotableViewMethod
@@ -12509,7 +12939,7 @@
if (resid != 0) {
d = mResources.getDrawable(resid);
}
- setBackgroundDrawable(d);
+ setBackground(d);
mBackgroundResource = resid;
}
@@ -12521,11 +12951,19 @@
* touched. If setting the padding is desired, please use
* {@link #setPadding(int, int, int, int)}.
*
- * @param d The Drawable to use as the background, or null to remove the
+ * @param background The Drawable to use as the background, or null to remove the
* background
*/
- public void setBackgroundDrawable(Drawable d) {
- if (d == mBGDrawable) {
+ public void setBackground(Drawable background) {
+ setBackgroundDrawable(background);
+ }
+
+ /**
+ * @deprecated use {@link #setBackground(Drawable)} instead
+ */
+ @Deprecated
+ public void setBackgroundDrawable(Drawable background) {
+ if (background == mBackground) {
return;
}
@@ -12537,19 +12975,19 @@
* Regardless of whether we're setting a new background or not, we want
* to clear the previous drawable.
*/
- if (mBGDrawable != null) {
- mBGDrawable.setCallback(null);
- unscheduleDrawable(mBGDrawable);
+ if (mBackground != null) {
+ mBackground.setCallback(null);
+ unscheduleDrawable(mBackground);
}
- if (d != null) {
+ if (background != null) {
Rect padding = sThreadLocal.get();
if (padding == null) {
padding = new Rect();
sThreadLocal.set(padding);
}
- if (d.getPadding(padding)) {
- switch (d.getResolvedLayoutDirectionSelf()) {
+ if (background.getPadding(padding)) {
+ switch (background.getResolvedLayoutDirectionSelf()) {
case LAYOUT_DIRECTION_RTL:
setPadding(padding.right, padding.top, padding.left, padding.bottom);
break;
@@ -12561,17 +12999,17 @@
// Compare the minimum sizes of the old Drawable and the new. If there isn't an old or
// if it has a different minimum size, we should layout again
- if (mBGDrawable == null || mBGDrawable.getMinimumHeight() != d.getMinimumHeight() ||
- mBGDrawable.getMinimumWidth() != d.getMinimumWidth()) {
+ if (mBackground == null || mBackground.getMinimumHeight() != background.getMinimumHeight() ||
+ mBackground.getMinimumWidth() != background.getMinimumWidth()) {
requestLayout = true;
}
- d.setCallback(this);
- if (d.isStateful()) {
- d.setState(getDrawableState());
+ background.setCallback(this);
+ if (background.isStateful()) {
+ background.setState(getDrawableState());
}
- d.setVisible(getVisibility() == VISIBLE, false);
- mBGDrawable = d;
+ background.setVisible(getVisibility() == VISIBLE, false);
+ mBackground = background;
if ((mPrivateFlags & SKIP_DRAW) != 0) {
mPrivateFlags &= ~SKIP_DRAW;
@@ -12580,7 +13018,7 @@
}
} else {
/* Remove the background */
- mBGDrawable = null;
+ mBackground = null;
if ((mPrivateFlags & ONLY_DRAWS_BACKGROUND) != 0) {
/*
@@ -12616,10 +13054,15 @@
/**
* Gets the background drawable
+ *
* @return The drawable used as the background for this view, if any.
+ *
+ * @see #setBackground(Drawable)
+ *
+ * @attr ref android.R.styleable#View_background
*/
public Drawable getBackground() {
- return mBGDrawable;
+ return mBackground;
}
/**
@@ -13160,8 +13603,8 @@
* number.
*
* @see #NO_ID
- * @see #getId()
- * @see #findViewById(int)
+ * @see #getId()
+ * @see #findViewById(int)
*
* @param id a number used to identify the view
*
@@ -13200,8 +13643,8 @@
* @return a positive integer used to identify the view or {@link #NO_ID}
* if the view has no ID
*
- * @see #setId(int)
- * @see #findViewById(int)
+ * @see #setId(int)
+ * @see #findViewById(int)
* @attr ref android.R.styleable#View_id
*/
@ViewDebug.CapturedViewProperty
@@ -13481,13 +13924,12 @@
mPrivateFlags |= FORCE_LAYOUT;
mPrivateFlags |= INVALIDATED;
- if (mParent != null) {
- if (mLayoutParams != null) {
- mLayoutParams.onResolveLayoutDirection(getResolvedLayoutDirection());
- }
- if (!mParent.isLayoutRequested()) {
- mParent.requestLayout();
- }
+ if (mLayoutParams != null) {
+ mLayoutParams.onResolveLayoutDirection(getResolvedLayoutDirection());
+ }
+
+ if (mParent != null && !mParent.isLayoutRequested()) {
+ mParent.requestLayout();
}
}
@@ -13714,16 +14156,8 @@
* @return The suggested minimum height of the view.
*/
protected int getSuggestedMinimumHeight() {
- int suggestedMinHeight = mMinHeight;
+ return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
- if (mBGDrawable != null) {
- final int bgMinHeight = mBGDrawable.getMinimumHeight();
- if (suggestedMinHeight < bgMinHeight) {
- suggestedMinHeight = bgMinHeight;
- }
- }
-
- return suggestedMinHeight;
}
/**
@@ -13738,16 +14172,20 @@
* @return The suggested minimum width of the view.
*/
protected int getSuggestedMinimumWidth() {
- int suggestedMinWidth = mMinWidth;
+ return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
+ }
- if (mBGDrawable != null) {
- final int bgMinWidth = mBGDrawable.getMinimumWidth();
- if (suggestedMinWidth < bgMinWidth) {
- suggestedMinWidth = bgMinWidth;
- }
- }
-
- return suggestedMinWidth;
+ /**
+ * Returns the minimum height of the view.
+ *
+ * @return the minimum height the view will try to be.
+ *
+ * @see #setMinimumHeight(int)
+ *
+ * @attr ref android.R.styleable#View_minHeight
+ */
+ public int getMinimumHeight() {
+ return mMinHeight;
}
/**
@@ -13756,9 +14194,27 @@
* constrains it with less available height).
*
* @param minHeight The minimum height the view will try to be.
+ *
+ * @see #getMinimumHeight()
+ *
+ * @attr ref android.R.styleable#View_minHeight
*/
public void setMinimumHeight(int minHeight) {
mMinHeight = minHeight;
+ requestLayout();
+ }
+
+ /**
+ * Returns the minimum width of the view.
+ *
+ * @return the minimum width the view will try to be.
+ *
+ * @see #setMinimumWidth(int)
+ *
+ * @attr ref android.R.styleable#View_minWidth
+ */
+ public int getMinimumWidth() {
+ return mMinWidth;
}
/**
@@ -13767,9 +14223,15 @@
* constrains it with less available width).
*
* @param minWidth The minimum width the view will try to be.
+ *
+ * @see #getMinimumWidth()
+ *
+ * @attr ref android.R.styleable#View_minWidth
*/
public void setMinimumWidth(int minWidth) {
mMinWidth = minWidth;
+ requestLayout();
+
}
/**
@@ -13888,11 +14350,11 @@
getLocationInWindow(location);
region.op(location[0], location[1], location[0] + mRight - mLeft,
location[1] + mBottom - mTop, Region.Op.DIFFERENCE);
- } else if ((pflags & ONLY_DRAWS_BACKGROUND) != 0 && mBGDrawable != null) {
+ } else if ((pflags & ONLY_DRAWS_BACKGROUND) != 0 && mBackground != null) {
// The ONLY_DRAWS_BACKGROUND flag IS set and the background drawable
// exists, so we remove the background drawable's non-transparent
// parts from this transparent region.
- applyDrawableToTransparentRegion(mBGDrawable, region);
+ applyDrawableToTransparentRegion(mBackground, region);
}
}
return true;
@@ -13982,6 +14444,35 @@
}
/**
+ * Returns the current system UI visibility that is currently set for
+ * the entire window. This is the combination of the
+ * {@link #setSystemUiVisibility(int)} values supplied by all of the
+ * views in the window.
+ */
+ public int getWindowSystemUiVisibility() {
+ return mAttachInfo != null ? mAttachInfo.mSystemUiVisibility : 0;
+ }
+
+ /**
+ * Override to find out when the window's requested system UI visibility
+ * has changed, that is the value returned by {@link #getWindowSystemUiVisibility()}.
+ * This is different from the callbacks recieved through
+ * {@link #setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener)}
+ * in that this is only telling you about the local request of the window,
+ * not the actual values applied by the system.
+ */
+ public void onWindowSystemUiVisibilityChanged(int visible) {
+ }
+
+ /**
+ * Dispatch callbacks to {@link #onWindowSystemUiVisibilityChanged(int)} down
+ * the view hierarchy.
+ */
+ public void dispatchWindowSystemUiVisiblityChanged(int visible) {
+ onWindowSystemUiVisibilityChanged(visible);
+ }
+
+ /**
* Set a listener to receive callbacks when the visibility of the system bar changes.
* @param l The {@link OnSystemUiVisibilityChangeListener} to receive callbacks.
*/
@@ -14534,7 +15025,7 @@
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
- * {@link #TEXT_DIRECTION_LOCALE},
+ * {@link #TEXT_DIRECTION_LOCALE}
*/
@ViewDebug.ExportedProperty(category = "text", mapping = {
@ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
@@ -14558,7 +15049,7 @@
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
- * {@link #TEXT_DIRECTION_LOCALE},
+ * {@link #TEXT_DIRECTION_LOCALE}
*/
public void setTextDirection(int textDirection) {
if (getTextDirection() != textDirection) {
@@ -14567,6 +15058,7 @@
resetResolvedTextDirection();
// Set the new text direction
mPrivateFlags2 |= ((textDirection << TEXT_DIRECTION_MASK_SHIFT) & TEXT_DIRECTION_MASK);
+ // Refresh
requestLayout();
invalidate(true);
}
@@ -14586,7 +15078,7 @@
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
- * {@link #TEXT_DIRECTION_LOCALE},
+ * {@link #TEXT_DIRECTION_LOCALE}
*/
public int getResolvedTextDirection() {
// The text direction will be resolved only if needed
@@ -14604,44 +15096,49 @@
// Reset any previous text direction resolution
mPrivateFlags2 &= ~(TEXT_DIRECTION_RESOLVED | TEXT_DIRECTION_RESOLVED_MASK);
- // Set resolved text direction flag depending on text direction flag
- final int textDirection = getTextDirection();
- switch(textDirection) {
- case TEXT_DIRECTION_INHERIT:
- if (canResolveTextDirection()) {
- ViewGroup viewGroup = ((ViewGroup) mParent);
+ if (hasRtlSupport()) {
+ // Set resolved text direction flag depending on text direction flag
+ final int textDirection = getTextDirection();
+ switch(textDirection) {
+ case TEXT_DIRECTION_INHERIT:
+ if (canResolveTextDirection()) {
+ ViewGroup viewGroup = ((ViewGroup) mParent);
- // Set current resolved direction to the same value as the parent's one
- final int parentResolvedDirection = viewGroup.getResolvedTextDirection();
- switch (parentResolvedDirection) {
- case TEXT_DIRECTION_FIRST_STRONG:
- case TEXT_DIRECTION_ANY_RTL:
- case TEXT_DIRECTION_LTR:
- case TEXT_DIRECTION_RTL:
- case TEXT_DIRECTION_LOCALE:
- mPrivateFlags2 |=
- (parentResolvedDirection << TEXT_DIRECTION_RESOLVED_MASK_SHIFT);
- break;
- default:
- // Default resolved direction is "first strong" heuristic
- mPrivateFlags2 |= TEXT_DIRECTION_RESOLVED_DEFAULT;
+ // Set current resolved direction to the same value as the parent's one
+ final int parentResolvedDirection = viewGroup.getResolvedTextDirection();
+ switch (parentResolvedDirection) {
+ case TEXT_DIRECTION_FIRST_STRONG:
+ case TEXT_DIRECTION_ANY_RTL:
+ case TEXT_DIRECTION_LTR:
+ case TEXT_DIRECTION_RTL:
+ case TEXT_DIRECTION_LOCALE:
+ mPrivateFlags2 |=
+ (parentResolvedDirection << TEXT_DIRECTION_RESOLVED_MASK_SHIFT);
+ break;
+ default:
+ // Default resolved direction is "first strong" heuristic
+ mPrivateFlags2 |= TEXT_DIRECTION_RESOLVED_DEFAULT;
+ }
+ } else {
+ // We cannot do the resolution if there is no parent, so use the default one
+ mPrivateFlags2 |= TEXT_DIRECTION_RESOLVED_DEFAULT;
}
- } else {
- // We cannot do the resolution if there is no parent, so use the default one
+ break;
+ case TEXT_DIRECTION_FIRST_STRONG:
+ case TEXT_DIRECTION_ANY_RTL:
+ case TEXT_DIRECTION_LTR:
+ case TEXT_DIRECTION_RTL:
+ case TEXT_DIRECTION_LOCALE:
+ // Resolved direction is the same as text direction
+ mPrivateFlags2 |= (textDirection << TEXT_DIRECTION_RESOLVED_MASK_SHIFT);
+ break;
+ default:
+ // Default resolved direction is "first strong" heuristic
mPrivateFlags2 |= TEXT_DIRECTION_RESOLVED_DEFAULT;
- }
- break;
- case TEXT_DIRECTION_FIRST_STRONG:
- case TEXT_DIRECTION_ANY_RTL:
- case TEXT_DIRECTION_LTR:
- case TEXT_DIRECTION_RTL:
- case TEXT_DIRECTION_LOCALE:
- // Resolved direction is the same as text direction
- mPrivateFlags2 |= (textDirection << TEXT_DIRECTION_RESOLVED_MASK_SHIFT);
- break;
- default:
- // Default resolved direction is "first strong" heuristic
- mPrivateFlags2 |= TEXT_DIRECTION_RESOLVED_DEFAULT;
+ }
+ } else {
+ // Default resolved direction is "first strong" heuristic
+ mPrivateFlags2 |= TEXT_DIRECTION_RESOLVED_DEFAULT;
}
// Set to resolved
@@ -14690,6 +15187,199 @@
public void onResolvedTextDirectionReset() {
}
+ /**
+ * Return the value specifying the text alignment or policy that was set with
+ * {@link #setTextAlignment(int)}.
+ *
+ * @return the defined text alignment. It can be one of:
+ *
+ * {@link #TEXT_ALIGNMENT_INHERIT},
+ * {@link #TEXT_ALIGNMENT_GRAVITY},
+ * {@link #TEXT_ALIGNMENT_CENTER},
+ * {@link #TEXT_ALIGNMENT_TEXT_START},
+ * {@link #TEXT_ALIGNMENT_TEXT_END},
+ * {@link #TEXT_ALIGNMENT_VIEW_START},
+ * {@link #TEXT_ALIGNMENT_VIEW_END}
+ */
+ @ViewDebug.ExportedProperty(category = "text", mapping = {
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_GRAVITY, to = "GRAVITY"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_START, to = "TEXT_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_END, to = "TEXT_END"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_CENTER, to = "CENTER"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
+ })
+ public int getTextAlignment() {
+ return (mPrivateFlags2 & TEXT_ALIGNMENT_MASK) >> TEXT_ALIGNMENT_MASK_SHIFT;
+ }
+
+ /**
+ * Set the text alignment.
+ *
+ * @param textAlignment The text alignment to set. Should be one of
+ *
+ * {@link #TEXT_ALIGNMENT_INHERIT},
+ * {@link #TEXT_ALIGNMENT_GRAVITY},
+ * {@link #TEXT_ALIGNMENT_CENTER},
+ * {@link #TEXT_ALIGNMENT_TEXT_START},
+ * {@link #TEXT_ALIGNMENT_TEXT_END},
+ * {@link #TEXT_ALIGNMENT_VIEW_START},
+ * {@link #TEXT_ALIGNMENT_VIEW_END}
+ *
+ * @attr ref android.R.styleable#View_textAlignment
+ */
+ public void setTextAlignment(int textAlignment) {
+ if (textAlignment != getTextAlignment()) {
+ // Reset the current and resolved text alignment
+ mPrivateFlags2 &= ~TEXT_ALIGNMENT_MASK;
+ resetResolvedTextAlignment();
+ // Set the new text alignment
+ mPrivateFlags2 |= ((textAlignment << TEXT_ALIGNMENT_MASK_SHIFT) & TEXT_ALIGNMENT_MASK);
+ // Refresh
+ requestLayout();
+ invalidate(true);
+ }
+ }
+
+ /**
+ * Return the resolved text alignment.
+ *
+ * The resolved text alignment. This needs resolution if the value is
+ * TEXT_ALIGNMENT_INHERIT. The resolution matches {@link #setTextAlignment(int)} if it is
+ * not TEXT_ALIGNMENT_INHERIT, otherwise resolution proceeds up the parent chain of the view.
+ *
+ * @return the resolved text alignment. Returns one of:
+ *
+ * {@link #TEXT_ALIGNMENT_GRAVITY},
+ * {@link #TEXT_ALIGNMENT_CENTER},
+ * {@link #TEXT_ALIGNMENT_TEXT_START},
+ * {@link #TEXT_ALIGNMENT_TEXT_END},
+ * {@link #TEXT_ALIGNMENT_VIEW_START},
+ * {@link #TEXT_ALIGNMENT_VIEW_END}
+ */
+ @ViewDebug.ExportedProperty(category = "text", mapping = {
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_GRAVITY, to = "GRAVITY"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_START, to = "TEXT_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_END, to = "TEXT_END"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_CENTER, to = "CENTER"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
+ })
+ public int getResolvedTextAlignment() {
+ // If text alignment is not resolved, then resolve it
+ if ((mPrivateFlags2 & TEXT_ALIGNMENT_RESOLVED) != TEXT_ALIGNMENT_RESOLVED) {
+ resolveTextAlignment();
+ }
+ return (mPrivateFlags2 & TEXT_ALIGNMENT_RESOLVED_MASK) >> TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+ }
+
+ /**
+ * Resolve the text alignment. Will call {@link View#onResolvedTextAlignmentChanged} when
+ * resolution is done.
+ */
+ public void resolveTextAlignment() {
+ // Reset any previous text alignment resolution
+ mPrivateFlags2 &= ~(TEXT_ALIGNMENT_RESOLVED | TEXT_ALIGNMENT_RESOLVED_MASK);
+
+ if (hasRtlSupport()) {
+ // Set resolved text alignment flag depending on text alignment flag
+ final int textAlignment = getTextAlignment();
+ switch (textAlignment) {
+ case TEXT_ALIGNMENT_INHERIT:
+ // Check if we can resolve the text alignment
+ if (canResolveLayoutDirection() && mParent instanceof View) {
+ View view = (View) mParent;
+
+ final int parentResolvedTextAlignment = view.getResolvedTextAlignment();
+ switch (parentResolvedTextAlignment) {
+ case TEXT_ALIGNMENT_GRAVITY:
+ case TEXT_ALIGNMENT_TEXT_START:
+ case TEXT_ALIGNMENT_TEXT_END:
+ case TEXT_ALIGNMENT_CENTER:
+ case TEXT_ALIGNMENT_VIEW_START:
+ case TEXT_ALIGNMENT_VIEW_END:
+ // Resolved text alignment is the same as the parent resolved
+ // text alignment
+ mPrivateFlags2 |=
+ (parentResolvedTextAlignment << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT);
+ break;
+ default:
+ // Use default resolved text alignment
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+ }
+ else {
+ // We cannot do the resolution if there is no parent so use the default
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+ break;
+ case TEXT_ALIGNMENT_GRAVITY:
+ case TEXT_ALIGNMENT_TEXT_START:
+ case TEXT_ALIGNMENT_TEXT_END:
+ case TEXT_ALIGNMENT_CENTER:
+ case TEXT_ALIGNMENT_VIEW_START:
+ case TEXT_ALIGNMENT_VIEW_END:
+ // Resolved text alignment is the same as text alignment
+ mPrivateFlags2 |= (textAlignment << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT);
+ break;
+ default:
+ // Use default resolved text alignment
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+ } else {
+ // Use default resolved text alignment
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+
+ // Set the resolved
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED;
+ onResolvedTextAlignmentChanged();
+ }
+
+ /**
+ * Check if text alignment resolution can be done.
+ *
+ * @return true if text alignment resolution can be done otherwise return false.
+ */
+ public boolean canResolveTextAlignment() {
+ switch (getTextAlignment()) {
+ case TEXT_DIRECTION_INHERIT:
+ return (mParent != null);
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Called when text alignment has been resolved. Subclasses that care about text alignment
+ * resolution should override this method.
+ *
+ * The default implementation does nothing.
+ */
+ public void onResolvedTextAlignmentChanged() {
+ }
+
+ /**
+ * Reset resolved text alignment. Text alignment can be resolved with a call to
+ * getResolvedTextAlignment(). Will call {@link View#onResolvedTextAlignmentReset} when
+ * reset is done.
+ */
+ public void resetResolvedTextAlignment() {
+ // Reset any previous text alignment resolution
+ mPrivateFlags2 &= ~(TEXT_ALIGNMENT_RESOLVED | TEXT_ALIGNMENT_RESOLVED_MASK);
+ onResolvedTextAlignmentReset();
+ }
+
+ /**
+ * Called when text alignment is reset. Subclasses that care about text alignment reset should
+ * override this method and do a reset of the text alignment of their children. The default
+ * implementation does nothing.
+ */
+ public void onResolvedTextAlignmentReset() {
+ }
+
//
// Properties
//
@@ -15182,7 +15872,7 @@
* visibility. This reports <strong>global</strong> changes to the system UI
* state, not just what the application is requesting.
*
- * @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener)
+ * @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener)
*/
public interface OnSystemUiVisibilityChangeListener {
/**
@@ -15696,6 +16386,12 @@
* classes i.e. classes in package android.view, that would like their
* applications to be backwards compatible.
* </p>
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about making applications accessible, read the
+ * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
+ * developer guide.</p>
+ * </div>
* <p>
* A scenario in which a developer would like to use an accessibility delegate
* is overriding a method introduced in a later API version then the minimal API
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 20183ee..b9924c7 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -31,14 +31,14 @@
public class ViewConfiguration {
/**
* Expected bit depth of the display panel.
- *
+ *
* @hide
*/
public static final float PANEL_BIT_DEPTH = 24;
/**
* Minimum alpha required for a view to draw.
- *
+ *
* @hide
*/
public static final float ALPHA_THRESHOLD = 0.5f / PANEL_BIT_DEPTH;
@@ -72,8 +72,8 @@
* Defines the duration in milliseconds of the pressed state in child
* components.
*/
- private static final int PRESSED_STATE_DURATION = 125;
-
+ private static final int PRESSED_STATE_DURATION = 64;
+
/**
* Defines the default duration in milliseconds before a press turns into
* a long press
@@ -91,18 +91,18 @@
* lock screen, etc).
*/
private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 500;
-
+
/**
- * Defines the duration in milliseconds we will wait to see if a touch event
+ * Defines the duration in milliseconds we will wait to see if a touch event
* is a tap or a scroll. If the user does not move within this interval, it is
- * considered to be a tap.
+ * considered to be a tap.
*/
private static final int TAP_TIMEOUT = 180;
-
+
/**
- * Defines the duration in milliseconds we will wait to see if a touch event
+ * Defines the duration in milliseconds we will wait to see if a touch event
* is a jump tap. If the user does not complete the jump tap within this interval, it is
- * considered to be a tap.
+ * considered to be a tap.
*/
private static final int JUMP_TAP_TIMEOUT = 500;
@@ -128,7 +128,7 @@
private static final int HOVER_TAP_SLOP = 20;
/**
- * Defines the duration in milliseconds we want to display zoom controls in response
+ * Defines the duration in milliseconds we want to display zoom controls in response
* to a user panning within an application.
*/
private static final int ZOOM_CONTROLS_TIMEOUT = 3000;
@@ -137,7 +137,7 @@
* Inset in dips to look for touchable content when the user touches the edge of the screen
*/
private static final int EDGE_SLOP = 12;
-
+
/**
* Distance a touch can wander before we think the user is scrolling in dips.
* Note that this value defined here is only used as a fallback by legacy/misbehaving
@@ -150,7 +150,7 @@
* the characteristics of the touch panel and firmware.
*/
private static final int TOUCH_SLOP = 8;
-
+
/**
* Distance the first touch can wander before we stop considering this event a double tap
* (in dips)
@@ -170,12 +170,12 @@
* config_viewConfigurationTouchSlop * 2 when provided with a Context.
*/
private static final int PAGING_TOUCH_SLOP = TOUCH_SLOP * 2;
-
+
/**
* Distance in dips between the first touch and second touch to still be considered a double tap
*/
private static final int DOUBLE_TAP_SLOP = 100;
-
+
/**
* Distance in dips a touch needs to be outside of a window's bounds for it to
* count as outside for purposes of dismissing the window.
@@ -186,7 +186,7 @@
* Minimum velocity to initiate a fling, as measured in dips per second
*/
private static final int MINIMUM_FLING_VELOCITY = 50;
-
+
/**
* Maximum velocity to initiate a fling, as measured in dips per second
*/
@@ -281,7 +281,7 @@
*
* @param context The application context used to initialize this view configuration.
*
- * @see #get(android.content.Context)
+ * @see #get(android.content.Context)
* @see android.util.DisplayMetrics
*/
private ViewConfiguration(Context context) {
@@ -383,7 +383,7 @@
public static int getScrollDefaultDelay() {
return SCROLL_BAR_DEFAULT_DELAY;
}
-
+
/**
* @return the length of the fading edges in dips
*
@@ -435,7 +435,7 @@
/**
* @return the duration in milliseconds we will wait to see if a touch event
* is a tap or a scroll. If the user does not move within this interval, it is
- * considered to be a tap.
+ * considered to be a tap.
*/
public static int getTapTimeout() {
return TAP_TIMEOUT;
@@ -444,12 +444,12 @@
/**
* @return the duration in milliseconds we will wait to see if a touch event
* is a jump tap. If the user does not move within this interval, it is
- * considered to be a tap.
+ * considered to be a tap.
*/
public static int getJumpTapTimeout() {
return JUMP_TAP_TIMEOUT;
}
-
+
/**
* @return the duration in milliseconds between the first tap's up event and
* the second tap's down event for an interaction to be considered a
@@ -514,7 +514,7 @@
public int getScaledTouchSlop() {
return mTouchSlop;
}
-
+
/**
* @return Distance in pixels the first touch can wander before we do not consider this a
* potential double tap event
@@ -543,7 +543,7 @@
public static int getDoubleTapSlop() {
return DOUBLE_TAP_SLOP;
}
-
+
/**
* @return Distance in pixels between the first touch and second touch to still be
* considered a double tap
@@ -595,7 +595,7 @@
public int getScaledWindowTouchSlop() {
return mWindowTouchSlop;
}
-
+
/**
* @return Minimum velocity to initiate a fling, as measured in dips per second.
*
@@ -629,7 +629,7 @@
public int getScaledMaximumFlingVelocity() {
return mMaximumFlingVelocity;
}
-
+
/**
* The maximum drawing cache size expressed in bytes.
*
@@ -671,7 +671,7 @@
/**
* The amount of time that the zoom controls should be
* displayed on the screen expressed in milliseconds.
- *
+ *
* @return the time the zoom controls should be visible expressed
* in milliseconds.
*/
@@ -692,7 +692,7 @@
/**
* The amount of friction applied to scrolls and flings.
- *
+ *
* @return A scalar dimensionless value representing the coefficient of
* friction.
*/
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 30d6ec7..121b544 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -918,7 +918,20 @@
}
}
}
-
+
+ /**
+ * @hide
+ */
+ @Override
+ public void makeOptionalFitsSystemWindows() {
+ super.makeOptionalFitsSystemWindows();
+ final int count = mChildrenCount;
+ final View[] children = mChildren;
+ for (int i = 0; i < count; i++) {
+ children[i].makeOptionalFitsSystemWindows();
+ }
+ }
+
/**
* {@inheritDoc}
*/
@@ -1017,13 +1030,16 @@
}
@Override
- void dispatchCollectViewAttributes(int visibility) {
- visibility |= mViewFlags&VISIBILITY_MASK;
- super.dispatchCollectViewAttributes(visibility);
- final int count = mChildrenCount;
- final View[] children = mChildren;
- for (int i = 0; i < count; i++) {
- children[i].dispatchCollectViewAttributes(visibility);
+ void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
+ if ((visibility & VISIBILITY_MASK) == VISIBLE) {
+ super.dispatchCollectViewAttributes(attachInfo, visibility);
+ final int count = mChildrenCount;
+ final View[] children = mChildren;
+ for (int i = 0; i < count; i++) {
+ final View child = children[i];
+ child.dispatchCollectViewAttributes(attachInfo,
+ visibility | (child.mViewFlags&VISIBILITY_MASK));
+ }
}
}
@@ -1239,6 +1255,18 @@
}
@Override
+ public void dispatchWindowSystemUiVisiblityChanged(int visible) {
+ super.dispatchWindowSystemUiVisiblityChanged(visible);
+
+ final int count = mChildrenCount;
+ final View[] children = mChildren;
+ for (int i=0; i <count; i++) {
+ final View child = children[i];
+ child.dispatchWindowSystemUiVisiblityChanged(visible);
+ }
+ }
+
+ @Override
public void dispatchSystemUiVisibilityChanged(int visible) {
super.dispatchSystemUiVisibilityChanged(visible);
@@ -2244,12 +2272,12 @@
super.dispatchAttachedToWindow(info, visibility);
mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
- visibility |= mViewFlags & VISIBILITY_MASK;
-
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
- children[i].dispatchAttachedToWindow(info, visibility);
+ final View child = children[i];
+ child.dispatchAttachedToWindow(info,
+ visibility | (child.mViewFlags&VISIBILITY_MASK));
}
}
@@ -3347,7 +3375,7 @@
boolean clearChildFocus = false;
if (view == mFocused) {
- view.clearFocusForRemoval();
+ view.unFocus();
clearChildFocus = true;
}
@@ -3370,6 +3398,7 @@
if (clearChildFocus) {
clearChildFocus(view);
+ ensureInputFocusOnFirstFocusable();
}
}
@@ -3422,7 +3451,7 @@
}
if (view == focused) {
- view.clearFocusForRemoval();
+ view.unFocus();
clearChildFocus = view;
}
@@ -3446,6 +3475,7 @@
if (clearChildFocus != null) {
clearChildFocus(clearChildFocus);
+ ensureInputFocusOnFirstFocusable();
}
}
@@ -3491,7 +3521,7 @@
}
if (view == focused) {
- view.clearFocusForRemoval();
+ view.unFocus();
clearChildFocus = view;
}
@@ -3514,6 +3544,7 @@
if (clearChildFocus != null) {
clearChildFocus(clearChildFocus);
+ ensureInputFocusOnFirstFocusable();
}
}
@@ -5028,6 +5059,18 @@
}
}
+ @Override
+ public void onResolvedTextAlignmentReset() {
+ // Take care of resetting the children resolution too
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getTextAlignment() == TEXT_ALIGNMENT_INHERIT) {
+ child.resetResolvedTextAlignment();
+ }
+ }
+ }
+
/**
* Return true if the pressed state should be delayed for children or descendants of this
* ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 8395f1b..75e9151 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -271,4 +271,10 @@
* @hide
*/
public void childHasTransientStateChanged(View child, boolean hasTransientState);
+
+ /**
+ * Ask that a new dispatch of {@link View#fitSystemWindows(Rect)
+ * View.fitSystemWindows(Rect)} be performed.
+ */
+ public void requestFitSystemWindows();
}
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 623b567..3626aba 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -834,40 +834,49 @@
*/
private void setValue(int propertyConstant, float value) {
final View.TransformationInfo info = mView.mTransformationInfo;
+ DisplayList displayList = View.USE_DISPLAY_LIST_PROPERTIES ? mView.mDisplayList : null;
switch (propertyConstant) {
case TRANSLATION_X:
info.mTranslationX = value;
+ if (displayList != null) displayList.setTranslationX(value);
break;
case TRANSLATION_Y:
info.mTranslationY = value;
+ if (displayList != null) displayList.setTranslationY(value);
break;
case ROTATION:
info.mRotation = value;
+ if (displayList != null) displayList.setRotation(value);
break;
case ROTATION_X:
info.mRotationX = value;
+ if (displayList != null) displayList.setRotationX(value);
break;
case ROTATION_Y:
info.mRotationY = value;
+ if (displayList != null) displayList.setRotationY(value);
break;
case SCALE_X:
info.mScaleX = value;
+ if (displayList != null) displayList.setScaleX(value);
break;
case SCALE_Y:
info.mScaleY = value;
+ if (displayList != null) displayList.setScaleY(value);
break;
case X:
info.mTranslationX = value - mView.mLeft;
+ if (displayList != null) displayList.setTranslationX(value - mView.mLeft);
break;
case Y:
info.mTranslationY = value - mView.mTop;
+ if (displayList != null) displayList.setTranslationY(value - mView.mTop);
break;
case ALPHA:
info.mAlpha = value;
+ if (displayList != null) displayList.setAlpha(value);
break;
}
- // TODO: optimize to set only the properties that have changed
- mView.setDisplayListProperties();
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4d589d7..899fb32 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -223,6 +223,7 @@
long mLastTraversalFinishedTimeNanos;
long mLastDrawFinishedTimeNanos;
boolean mWillDrawSoon;
+ boolean mFitSystemWindowsRequested;
boolean mLayoutRequested;
boolean mFirst;
boolean mReportNextDraw;
@@ -230,6 +231,7 @@
boolean mNewSurfaceNeeded;
boolean mHasHadWindowFocus;
boolean mLastWasImTarget;
+ int mLastSystemUiVisibility;
// Pool of queued input events.
private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
@@ -263,6 +265,8 @@
final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
= new ViewTreeObserver.InternalInsetsInfo();
+ final Rect mFitSystemWindowsInsets = new Rect();
+
final Configuration mLastConfiguration = new Configuration();
final Configuration mPendingConfiguration = new Configuration();
@@ -539,6 +543,8 @@
}
try {
mOrigWindowType = mWindowAttributes.type;
+ mAttachInfo.mRecomputeGlobalAttributes = true;
+ collectViewAttributes();
res = sWindowSession.add(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets,
mInputChannel);
@@ -786,6 +792,15 @@
/**
* {@inheritDoc}
*/
+ public void requestFitSystemWindows() {
+ checkThread();
+ mFitSystemWindowsRequested = true;
+ scheduleTraversals();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public void requestLayout() {
checkThread();
mLayoutRequested = true;
@@ -974,6 +989,100 @@
}
}
+ private boolean collectViewAttributes() {
+ final View.AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo.mRecomputeGlobalAttributes) {
+ //Log.i(TAG, "Computing view hierarchy attributes!");
+ attachInfo.mRecomputeGlobalAttributes = false;
+ boolean oldScreenOn = attachInfo.mKeepScreenOn;
+ int oldVis = attachInfo.mSystemUiVisibility;
+ boolean oldHasSystemUiListeners = attachInfo.mHasSystemUiListeners;
+ attachInfo.mKeepScreenOn = false;
+ attachInfo.mSystemUiVisibility = 0;
+ attachInfo.mHasSystemUiListeners = false;
+ mView.dispatchCollectViewAttributes(attachInfo, 0);
+ if (attachInfo.mKeepScreenOn != oldScreenOn
+ || attachInfo.mSystemUiVisibility != oldVis
+ || attachInfo.mHasSystemUiListeners != oldHasSystemUiListeners) {
+ WindowManager.LayoutParams params = mWindowAttributes;
+ if (attachInfo.mKeepScreenOn) {
+ params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+ }
+ params.subtreeSystemUiVisibility = attachInfo.mSystemUiVisibility;
+ params.hasSystemUiListeners = attachInfo.mHasSystemUiListeners;
+ mView.dispatchWindowSystemUiVisiblityChanged(attachInfo.mSystemUiVisibility);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
+ final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
+ int childWidthMeasureSpec;
+ int childHeightMeasureSpec;
+ boolean windowSizeMayChange = false;
+
+ if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,
+ "Measuring " + host + " in display " + desiredWindowWidth
+ + "x" + desiredWindowHeight + "...");
+
+ boolean goodMeasure = false;
+ if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ // On large screens, we don't want to allow dialogs to just
+ // stretch to fill the entire width of the screen to display
+ // one line of text. First try doing the layout at a smaller
+ // size to see if it will fit.
+ final DisplayMetrics packageMetrics = res.getDisplayMetrics();
+ res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
+ int baseSize = 0;
+ if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
+ baseSize = (int)mTmpValue.getDimension(packageMetrics);
+ }
+ if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
+ if (baseSize != 0 && desiredWindowWidth > baseSize) {
+ childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
+ childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
+ host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
+ if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
+ goodMeasure = true;
+ } else {
+ // Didn't fit in that size... try expanding a bit.
+ baseSize = (baseSize+desiredWindowWidth)/2;
+ if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
+ + baseSize);
+ childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
+ host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
+ if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
+ if (DEBUG_DIALOG) Log.v(TAG, "Good!");
+ goodMeasure = true;
+ }
+ }
+ }
+ }
+
+ if (!goodMeasure) {
+ childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
+ childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
+ host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
+ windowSizeMayChange = true;
+ }
+ }
+
+ if (DBG) {
+ System.out.println("======================================");
+ System.out.println("performTraversals -- after measure");
+ host.debug();
+ }
+
+ return windowSizeMayChange;
+ }
+
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
@@ -995,8 +1104,6 @@
int desiredWindowWidth;
int desiredWindowHeight;
- int childWidthMeasureSpec;
- int childHeightMeasureSpec;
final View.AttachInfo attachInfo = mAttachInfo;
@@ -1057,15 +1164,14 @@
attachInfo.mHasWindowFocus = false;
attachInfo.mWindowVisibility = viewVisibility;
attachInfo.mRecomputeGlobalAttributes = false;
- attachInfo.mKeepScreenOn = false;
- attachInfo.mSystemUiVisibility = 0;
viewVisibilityChanged = false;
mLastConfiguration.setTo(host.getResources().getConfiguration());
+ mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
host.dispatchAttachedToWindow(attachInfo, 0);
+ mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
+ host.fitSystemWindows(mFitSystemWindowsInsets);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
- host.fitSystemWindows(mAttachInfo.mContentInsets);
-
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
@@ -1093,7 +1199,8 @@
boolean insetsChanged = false;
- if (mLayoutRequested && !mStopped) {
+ boolean layoutRequested = mLayoutRequested && !mStopped;
+ if (layoutRequested) {
// Execute enqueued actions on every layout in case a view that was detached
// enqueued an action after being detached
getRunQueue().executeActions(attachInfo.mHandler);
@@ -1134,79 +1241,12 @@
}
// Ask host how big it wants to be
- if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,
- "Measuring " + host + " in display " + desiredWindowWidth
- + "x" + desiredWindowHeight + "...");
-
- boolean goodMeasure = false;
- if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
- // On large screens, we don't want to allow dialogs to just
- // stretch to fill the entire width of the screen to display
- // one line of text. First try doing the layout at a smaller
- // size to see if it will fit.
- final DisplayMetrics packageMetrics = res.getDisplayMetrics();
- res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
- int baseSize = 0;
- if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
- baseSize = (int)mTmpValue.getDimension(packageMetrics);
- }
- if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
- if (baseSize != 0 && desiredWindowWidth > baseSize) {
- childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
- childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
- host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
- + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
- if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
- goodMeasure = true;
- } else {
- // Didn't fit in that size... try expanding a bit.
- baseSize = (baseSize+desiredWindowWidth)/2;
- if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
- + baseSize);
- childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
- host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
- + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
- if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
- if (DEBUG_DIALOG) Log.v(TAG, "Good!");
- goodMeasure = true;
- }
- }
- }
- }
-
- if (!goodMeasure) {
- childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
- childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
- host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
- windowSizeMayChange = true;
- }
- }
-
- if (DBG) {
- System.out.println("======================================");
- System.out.println("performTraversals -- after measure");
- host.debug();
- }
+ windowSizeMayChange |= measureHierarchy(host, lp, res,
+ desiredWindowWidth, desiredWindowHeight);
}
- if (attachInfo.mRecomputeGlobalAttributes && host.mAttachInfo != null) {
- //Log.i(TAG, "Computing view hierarchy attributes!");
- attachInfo.mRecomputeGlobalAttributes = false;
- boolean oldScreenOn = attachInfo.mKeepScreenOn;
- int oldVis = attachInfo.mSystemUiVisibility;
- boolean oldHasSystemUiListeners = attachInfo.mHasSystemUiListeners;
- attachInfo.mKeepScreenOn = false;
- attachInfo.mSystemUiVisibility = 0;
- attachInfo.mHasSystemUiListeners = false;
- host.dispatchCollectViewAttributes(0);
- if (attachInfo.mKeepScreenOn != oldScreenOn
- || attachInfo.mSystemUiVisibility != oldVis
- || attachInfo.mHasSystemUiListeners != oldHasSystemUiListeners) {
- params = lp;
- }
+ if (collectViewAttributes()) {
+ params = lp;
}
if (attachInfo.mForceReportNewAttributes) {
attachInfo.mForceReportNewAttributes = false;
@@ -1245,7 +1285,28 @@
}
}
- boolean windowShouldResize = mLayoutRequested && windowSizeMayChange
+ if (mFitSystemWindowsRequested) {
+ mFitSystemWindowsRequested = false;
+ mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
+ host.fitSystemWindows(mFitSystemWindowsInsets);
+ if (mLayoutRequested) {
+ // Short-circuit catching a new layout request here, so
+ // we don't need to go through two layout passes when things
+ // change due to fitting system windows, which can happen a lot.
+ windowSizeMayChange |= measureHierarchy(host, lp,
+ mView.getContext().getResources(),
+ desiredWindowWidth, desiredWindowHeight);
+ }
+ }
+
+ if (layoutRequested) {
+ // Clear this now, so that if anything requests a layout in the
+ // rest of this function we will catch it and re-run a full
+ // layout pass.
+ mLayoutRequested = false;
+ }
+
+ boolean windowShouldResize = layoutRequested && windowSizeMayChange
&& ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
|| (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.width() < desiredWindowWidth && frame.width() != mWidth)
@@ -1285,15 +1346,6 @@
boolean hadSurface = mSurface.isValid();
try {
- int fl = 0;
- if (params != null) {
- fl = params.flags;
- if (attachInfo.mKeepScreenOn) {
- params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
- }
- params.subtreeSystemUiVisibility = attachInfo.mSystemUiVisibility;
- params.hasSystemUiListeners = attachInfo.mHasSystemUiListeners;
- }
if (DEBUG_LAYOUT) {
Log.i(TAG, "host=w:" + host.getMeasuredWidth() + ", h:" +
host.getMeasuredHeight() + ", params=" + params);
@@ -1302,10 +1354,6 @@
final int surfaceGenerationId = mSurface.getGenerationId();
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
- if (params != null) {
- params.flags = fl;
- }
-
if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
+ " content=" + mPendingContentInsets.toShortString()
+ " visible=" + mPendingVisibleInsets.toShortString()
@@ -1323,7 +1371,9 @@
visibleInsetsChanged = !mPendingVisibleInsets.equals(
mAttachInfo.mVisibleInsets);
if (contentInsetsChanged) {
- if (mWidth > 0 && mHeight > 0 &&
+ if (mWidth > 0 && mHeight > 0 && lp != null &&
+ ((lp.systemUiVisibility|lp.subtreeSystemUiVisibility)
+ & View.SYSTEM_UI_LAYOUT_FLAGS) == 0 &&
mSurface != null && mSurface.isValid() &&
!mAttachInfo.mTurnOffWindowResizeAnim &&
mAttachInfo.mHardwareRenderer != null &&
@@ -1390,10 +1440,16 @@
}
}
mAttachInfo.mContentInsets.set(mPendingContentInsets);
- host.fitSystemWindows(mAttachInfo.mContentInsets);
if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
+ mAttachInfo.mContentInsets);
}
+ if (contentInsetsChanged || mLastSystemUiVisibility !=
+ mAttachInfo.mSystemUiVisibility || mFitSystemWindowsRequested) {
+ mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
+ mFitSystemWindowsRequested = false;
+ mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
+ host.fitSystemWindows(mFitSystemWindowsInsets);
+ }
if (visibleInsetsChanged) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
@@ -1547,8 +1603,8 @@
(relayoutResult&WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
- childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
- childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
+ int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
+ int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed! mWidth="
+ mWidth + " measuredWidth=" + host.getMeasuredWidth()
@@ -1586,12 +1642,12 @@
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
- mLayoutRequested = true;
+ layoutRequested = true;
}
}
}
- final boolean didLayout = mLayoutRequested && !mStopped;
+ final boolean didLayout = layoutRequested && !mStopped;
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
@@ -1983,9 +2039,10 @@
scrollToRectOrFocus(null, false);
- if (mAttachInfo.mViewScrollChanged) {
- mAttachInfo.mViewScrollChanged = false;
- mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
+ final AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo.mViewScrollChanged) {
+ attachInfo.mViewScrollChanged = false;
+ attachInfo.mTreeObserver.dispatchOnScrollChanged();
}
int yoff;
@@ -2000,8 +2057,8 @@
fullRedrawNeeded = true;
}
- final float appScale = mAttachInfo.mApplicationScale;
- final boolean scalingRequired = mAttachInfo.mScalingRequired;
+ final float appScale = attachInfo.mApplicationScale;
+ final boolean scalingRequired = attachInfo.mScalingRequired;
int resizeAlpha = 0;
if (mResizeBuffer != null) {
@@ -2030,7 +2087,7 @@
}
if (fullRedrawNeeded) {
- mAttachInfo.mIgnoreDirtyState = true;
+ attachInfo.mIgnoreDirtyState = true;
dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
}
@@ -2043,8 +2100,10 @@
appScale + ", width=" + mWidth + ", height=" + mHeight);
}
+ attachInfo.mTreeObserver.dispatchOnDraw();
+
if (!dirty.isEmpty() || mIsAnimating) {
- if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
+ if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
// Draw with hardware renderer.
mIsAnimating = false;
mHardwareYOffset = yoff;
@@ -2055,147 +2114,12 @@
mPreviousDirty.set(dirty);
dirty.setEmpty();
- if (mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this,
+ if (attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,
animating ? null : mCurrentDirty)) {
mPreviousDirty.set(0, 0, mWidth, mHeight);
}
- } else {
- // Draw with software renderer.
- Canvas canvas;
- try {
- int left = dirty.left;
- int top = dirty.top;
- int right = dirty.right;
- int bottom = dirty.bottom;
-
- final long lockCanvasStartTime;
- if (ViewDebug.DEBUG_LATENCY) {
- lockCanvasStartTime = System.nanoTime();
- }
-
- canvas = mSurface.lockCanvas(dirty);
-
- if (ViewDebug.DEBUG_LATENCY) {
- long now = System.nanoTime();
- Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- lockCanvas() took "
- + ((now - lockCanvasStartTime) * 0.000001f) + "ms");
- }
-
- if (left != dirty.left || top != dirty.top || right != dirty.right ||
- bottom != dirty.bottom) {
- mAttachInfo.mIgnoreDirtyState = true;
- }
-
- // TODO: Do this in native
- canvas.setDensity(mDensity);
- } catch (Surface.OutOfResourcesException e) {
- Log.e(TAG, "OutOfResourcesException locking surface", e);
- try {
- if (!sWindowSession.outOfMemory(mWindow)) {
- Slog.w(TAG, "No processes killed for memory; killing self");
- Process.killProcess(Process.myPid());
- }
- } catch (RemoteException ex) {
- }
- mLayoutRequested = true; // ask wm for a new surface next time.
- return;
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "IllegalArgumentException locking surface", e);
- // Don't assume this is due to out of memory, it could be
- // something else, and if it is something else then we could
- // kill stuff (or ourself) for no reason.
- mLayoutRequested = true; // ask wm for a new surface next time.
- return;
- }
-
- try {
- if (DEBUG_ORIENTATION || DEBUG_DRAW) {
- Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
- + canvas.getWidth() + ", h=" + canvas.getHeight());
- //canvas.drawARGB(255, 255, 0, 0);
- }
-
- long startTime = 0L;
- if (ViewDebug.DEBUG_PROFILE_DRAWING) {
- startTime = SystemClock.elapsedRealtime();
- }
-
- // If this bitmap's format includes an alpha channel, we
- // need to clear it before drawing so that the child will
- // properly re-composite its drawing on a transparent
- // background. This automatically respects the clip/dirty region
- // or
- // If we are applying an offset, we need to clear the area
- // where the offset doesn't appear to avoid having garbage
- // left in the blank areas.
- if (!canvas.isOpaque() || yoff != 0) {
- canvas.drawColor(0, PorterDuff.Mode.CLEAR);
- }
-
- dirty.setEmpty();
- mIsAnimating = false;
- mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
- mView.mPrivateFlags |= View.DRAWN;
-
- if (DEBUG_DRAW) {
- Context cxt = mView.getContext();
- Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
- ", metrics=" + cxt.getResources().getDisplayMetrics() +
- ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
- }
- try {
- canvas.translate(0, -yoff);
- if (mTranslator != null) {
- mTranslator.translateCanvas(canvas);
- }
- canvas.setScreenDensity(scalingRequired
- ? DisplayMetrics.DENSITY_DEVICE : 0);
- mAttachInfo.mSetIgnoreDirtyState = false;
-
- final long drawStartTime;
- if (ViewDebug.DEBUG_LATENCY) {
- drawStartTime = System.nanoTime();
- }
-
- mView.draw(canvas);
-
- if (ViewDebug.DEBUG_LATENCY) {
- long now = System.nanoTime();
- Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- draw() took "
- + ((now - drawStartTime) * 0.000001f) + "ms");
- }
- } finally {
- if (!mAttachInfo.mSetIgnoreDirtyState) {
- // Only clear the flag if it was not set during the mView.draw() call
- mAttachInfo.mIgnoreDirtyState = false;
- }
- }
-
- if (false && ViewDebug.consistencyCheckEnabled) {
- mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
- }
-
- if (ViewDebug.DEBUG_PROFILE_DRAWING) {
- EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
- }
- } finally {
- final long unlockCanvasAndPostStartTime;
- if (ViewDebug.DEBUG_LATENCY) {
- unlockCanvasAndPostStartTime = System.nanoTime();
- }
-
- surface.unlockCanvasAndPost(canvas);
-
- if (ViewDebug.DEBUG_LATENCY) {
- long now = System.nanoTime();
- Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- unlockCanvasAndPost() took "
- + ((now - unlockCanvasAndPostStartTime) * 0.000001f) + "ms");
- }
-
- if (LOCAL_LOGV) {
- Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
- }
- }
+ } else if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {
+ return;
}
}
@@ -2205,6 +2129,151 @@
}
}
+ /**
+ * @return true if drawing was succesfull, false if an error occurred
+ */
+ private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff,
+ boolean scalingRequired, Rect dirty) {
+
+ // Draw with software renderer.
+ Canvas canvas;
+ try {
+ int left = dirty.left;
+ int top = dirty.top;
+ int right = dirty.right;
+ int bottom = dirty.bottom;
+
+ final long lockCanvasStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ lockCanvasStartTime = System.nanoTime();
+ }
+
+ canvas = mSurface.lockCanvas(dirty);
+
+ if (ViewDebug.DEBUG_LATENCY) {
+ long now = System.nanoTime();
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- lockCanvas() took "
+ + ((now - lockCanvasStartTime) * 0.000001f) + "ms");
+ }
+
+ if (left != dirty.left || top != dirty.top || right != dirty.right ||
+ bottom != dirty.bottom) {
+ attachInfo.mIgnoreDirtyState = true;
+ }
+
+ // TODO: Do this in native
+ canvas.setDensity(mDensity);
+ } catch (Surface.OutOfResourcesException e) {
+ Log.e(TAG, "OutOfResourcesException locking surface", e);
+ try {
+ if (!sWindowSession.outOfMemory(mWindow)) {
+ Slog.w(TAG, "No processes killed for memory; killing self");
+ Process.killProcess(Process.myPid());
+ }
+ } catch (RemoteException ex) {
+ }
+ mLayoutRequested = true; // ask wm for a new surface next time.
+ return false;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "IllegalArgumentException locking surface", e);
+ // Don't assume this is due to out of memory, it could be
+ // something else, and if it is something else then we could
+ // kill stuff (or ourself) for no reason.
+ mLayoutRequested = true; // ask wm for a new surface next time.
+ return false;
+ }
+
+ try {
+ if (DEBUG_ORIENTATION || DEBUG_DRAW) {
+ Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
+ + canvas.getWidth() + ", h=" + canvas.getHeight());
+ //canvas.drawARGB(255, 255, 0, 0);
+ }
+
+ long startTime = 0L;
+ if (ViewDebug.DEBUG_PROFILE_DRAWING) {
+ startTime = SystemClock.elapsedRealtime();
+ }
+
+ // If this bitmap's format includes an alpha channel, we
+ // need to clear it before drawing so that the child will
+ // properly re-composite its drawing on a transparent
+ // background. This automatically respects the clip/dirty region
+ // or
+ // If we are applying an offset, we need to clear the area
+ // where the offset doesn't appear to avoid having garbage
+ // left in the blank areas.
+ if (!canvas.isOpaque() || yoff != 0) {
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+ }
+
+ dirty.setEmpty();
+ mIsAnimating = false;
+ attachInfo.mDrawingTime = SystemClock.uptimeMillis();
+ mView.mPrivateFlags |= View.DRAWN;
+
+ if (DEBUG_DRAW) {
+ Context cxt = mView.getContext();
+ Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
+ ", metrics=" + cxt.getResources().getDisplayMetrics() +
+ ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
+ }
+ try {
+ canvas.translate(0, -yoff);
+ if (mTranslator != null) {
+ mTranslator.translateCanvas(canvas);
+ }
+ canvas.setScreenDensity(scalingRequired
+ ? DisplayMetrics.DENSITY_DEVICE : 0);
+ attachInfo.mSetIgnoreDirtyState = false;
+
+ final long drawStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ drawStartTime = System.nanoTime();
+ }
+
+ mView.draw(canvas);
+
+ if (ViewDebug.DEBUG_LATENCY) {
+ long now = System.nanoTime();
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- draw() took "
+ + ((now - drawStartTime) * 0.000001f) + "ms");
+ }
+ } finally {
+ if (!attachInfo.mSetIgnoreDirtyState) {
+ // Only clear the flag if it was not set during the mView.draw() call
+ attachInfo.mIgnoreDirtyState = false;
+ }
+ }
+
+ if (false && ViewDebug.consistencyCheckEnabled) {
+ mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
+ }
+
+ if (ViewDebug.DEBUG_PROFILE_DRAWING) {
+ EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
+ }
+ } finally {
+ final long unlockCanvasAndPostStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ unlockCanvasAndPostStartTime = System.nanoTime();
+ }
+
+ surface.unlockCanvasAndPost(canvas);
+
+ if (ViewDebug.DEBUG_LATENCY) {
+ long now = System.nanoTime();
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- unlockCanvasAndPost() took "
+ + ((now - unlockCanvasAndPostStartTime) * 0.000001f) + "ms");
+ }
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
+ }
+ }
+ return true;
+ }
+
void invalidateDisplayLists() {
final ArrayList<DisplayList> displayLists = mDisplayLists;
final int count = displayLists.size();
@@ -3579,9 +3648,6 @@
if (mView == null) return;
if (args.localChanges != 0) {
if (mAttachInfo != null) {
- mAttachInfo.mSystemUiVisibility =
- (mAttachInfo.mSystemUiVisibility & ~args.localChanges) |
- (args.localValue & args.localChanges);
mAttachInfo.mRecomputeGlobalAttributes = true;
}
mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges);
@@ -3777,30 +3843,33 @@
if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mAdded) {
- mAdded = false;
dispatchDetachedFromWindow();
}
if (mAdded && !mFirst) {
destroyHardwareRenderer();
- int viewVisibility = mView.getVisibility();
- boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
- if (mWindowAttributesChanged || viewVisibilityChanged) {
- // If layout params have been changed, first give them
- // to the window manager to make sure it has the correct
- // animation info.
- try {
- if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
- & WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) {
- sWindowSession.finishDrawing(mWindow);
+ if (mView != null) {
+ int viewVisibility = mView.getVisibility();
+ boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
+ if (mWindowAttributesChanged || viewVisibilityChanged) {
+ // If layout params have been changed, first give them
+ // to the window manager to make sure it has the correct
+ // animation info.
+ try {
+ if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
+ & WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0) {
+ sWindowSession.finishDrawing(mWindow);
+ }
+ } catch (RemoteException e) {
}
- } catch (RemoteException e) {
}
+
+ mSurface.release();
}
-
- mSurface.release();
}
+
+ mAdded = false;
}
}
diff --git a/core/java/android/view/ViewStub.java b/core/java/android/view/ViewStub.java
index d5e9af4..69a26c2 100644
--- a/core/java/android/view/ViewStub.java
+++ b/core/java/android/view/ViewStub.java
@@ -20,6 +20,7 @@
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.util.AttributeSet;
+import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -66,12 +67,14 @@
* @attr ref android.R.styleable#ViewStub_inflatedId
* @attr ref android.R.styleable#ViewStub_layout
*/
+@RemoteView
public final class ViewStub extends View {
private int mLayoutResource = 0;
private int mInflatedId;
private WeakReference<View> mInflatedViewRef;
+ private LayoutInflater mInflater;
private OnInflateListener mInflateListener;
public ViewStub(Context context) {
@@ -140,6 +143,7 @@
* @see #getInflatedId()
* @attr ref android.R.styleable#ViewStub_inflatedId
*/
+ @android.view.RemotableViewMethod
public void setInflatedId(int inflatedId) {
mInflatedId = inflatedId;
}
@@ -172,10 +176,26 @@
* @see #inflate()
* @attr ref android.R.styleable#ViewStub_layout
*/
+ @android.view.RemotableViewMethod
public void setLayoutResource(int layoutResource) {
mLayoutResource = layoutResource;
}
+ /**
+ * Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null}
+ * to use the default.
+ */
+ public void setLayoutInflater(LayoutInflater inflater) {
+ mInflater = inflater;
+ }
+
+ /**
+ * Get current {@link LayoutInflater} used in {@link #inflate()}.
+ */
+ public LayoutInflater getLayoutInflater() {
+ return mInflater;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(0, 0);
@@ -199,6 +219,7 @@
* @see #inflate()
*/
@Override
+ @android.view.RemotableViewMethod
public void setVisibility(int visibility) {
if (mInflatedViewRef != null) {
View view = mInflatedViewRef.get();
@@ -228,7 +249,12 @@
if (viewParent != null && viewParent instanceof ViewGroup) {
if (mLayoutResource != 0) {
final ViewGroup parent = (ViewGroup) viewParent;
- final LayoutInflater factory = LayoutInflater.from(mContext);
+ final LayoutInflater factory;
+ if (mInflater != null) {
+ factory = mInflater;
+ } else {
+ factory = LayoutInflater.from(mContext);
+ }
final View view = factory.inflate(mLayoutResource, parent,
false);
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 7fd3389..1c5d436 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -38,6 +38,7 @@
private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners;
private ArrayList<OnPreDrawListener> mOnPreDrawListeners;
+ private ArrayList<OnDrawListener> mOnDrawListeners;
private boolean mAlive = true;
@@ -90,6 +91,27 @@
}
/**
+ * Interface definition for a callback to be invoked when the view tree is about to be drawn.
+ */
+ public interface OnDrawListener {
+ /**
+ * <p>Callback method to be invoked when the view tree is about to be drawn. At this point,
+ * views cannot be modified in any way.</p>
+ *
+ * <p>Unlike with {@link OnPreDrawListener}, this method cannot be used to cancel the
+ * current drawing pass.</p>
+ *
+ * <p>An {@link OnDrawListener} listener <strong>cannot be added or removed</strong>
+ * from this method.</p>
+ *
+ * @see android.view.View#onMeasure
+ * @see android.view.View#onLayout
+ * @see android.view.View#onDraw
+ */
+ public void onDraw();
+ }
+
+ /**
* Interface definition for a callback to be invoked when the touch mode changes.
*/
public interface OnTouchModeChangeListener {
@@ -171,11 +193,7 @@
public void setTouchableInsets(int val) {
mTouchableInsets = val;
}
-
- public int getTouchableInsets() {
- return mTouchableInsets;
- }
-
+
int mTouchableInsets;
void reset() {
@@ -184,29 +202,28 @@
touchableRegion.setEmpty();
mTouchableInsets = TOUCHABLE_INSETS_FRAME;
}
-
+
+ @Override
+ public int hashCode() {
+ int result = contentInsets != null ? contentInsets.hashCode() : 0;
+ result = 31 * result + (visibleInsets != null ? visibleInsets.hashCode() : 0);
+ result = 31 * result + (touchableRegion != null ? touchableRegion.hashCode() : 0);
+ result = 31 * result + mTouchableInsets;
+ return result;
+ }
+
@Override
public boolean equals(Object o) {
- try {
- if (o == null) {
- return false;
- }
- InternalInsetsInfo other = (InternalInsetsInfo)o;
- if (mTouchableInsets != other.mTouchableInsets) {
- return false;
- }
- if (!contentInsets.equals(other.contentInsets)) {
- return false;
- }
- if (!visibleInsets.equals(other.visibleInsets)) {
- return false;
- }
- return touchableRegion.equals(other.touchableRegion);
- } catch (ClassCastException e) {
- return false;
- }
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ InternalInsetsInfo other = (InternalInsetsInfo)o;
+ return mTouchableInsets == other.mTouchableInsets &&
+ contentInsets.equals(other.contentInsets) &&
+ visibleInsets.equals(other.visibleInsets) &&
+ touchableRegion.equals(other.touchableRegion);
}
-
+
void set(InternalInsetsInfo other) {
contentInsets.set(other.contentInsets);
visibleInsets.set(other.visibleInsets);
@@ -420,6 +437,44 @@
}
/**
+ * <p>Register a callback to be invoked when the view tree is about to be drawn.</p>
+ * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
+ * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
+ *
+ * @param listener The callback to add
+ *
+ * @throws IllegalStateException If {@link #isAlive()} returns false
+ */
+ public void addOnDrawListener(OnDrawListener listener) {
+ checkIsAlive();
+
+ if (mOnDrawListeners == null) {
+ mOnDrawListeners = new ArrayList<OnDrawListener>();
+ }
+
+ mOnDrawListeners.add(listener);
+ }
+
+ /**
+ * <p>Remove a previously installed pre-draw callback.</p>
+ * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
+ * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
+ *
+ * @param victim The callback to remove
+ *
+ * @throws IllegalStateException If {@link #isAlive()} returns false
+ *
+ * @see #addOnDrawListener(OnDrawListener)
+ */
+ public void removeOnDrawListener(OnDrawListener victim) {
+ checkIsAlive();
+ if (mOnDrawListeners == null) {
+ return;
+ }
+ mOnDrawListeners.remove(victim);
+ }
+
+ /**
* Register a callback to be invoked when a view has been scrolled.
*
* @param listener The callback to add
@@ -601,6 +656,7 @@
*
* @return True if the current draw should be canceled and resceduled, false otherwise.
*/
+ @SuppressWarnings("unchecked")
public final boolean dispatchOnPreDraw() {
// NOTE: we *must* clone the listener list to perform the dispatching.
// The clone is a safe guard against listeners that
@@ -619,6 +675,19 @@
}
/**
+ * Notifies registered listeners that the drawing pass is about to start.
+ */
+ public final void dispatchOnDraw() {
+ if (mOnDrawListeners != null) {
+ final ArrayList<OnDrawListener> listeners = mOnDrawListeners;
+ int numListeners = listeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ listeners.get(i).onDraw();
+ }
+ }
+ }
+
+ /**
* Notifies registered listeners that the touch mode has changed.
*
* @param inTouchMode True if the touch mode is now enabled, false otherwise.
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index a99ac03..b0e90db 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -75,6 +75,16 @@
* over how the Action Bar is displayed, such as letting application content scroll beneath
* an Action Bar with a transparent background or otherwise displaying a transparent/translucent
* Action Bar over application content.
+ *
+ * <p>This mode is especially useful with {@link View#SYSTEM_UI_FLAG_FULLSCREEN
+ * View.SYSTEM_UI_FLAG_FULLSCREEN}, which allows you to seamlessly hide the
+ * action bar in conjunction with other screen decorations.
+ *
+ * <p>As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, when an
+ * ActionBar is in this mode it will adjust the insets provided to
+ * {@link View#fitSystemWindows(android.graphics.Rect) View.fitSystemWindows(Rect)}
+ * to include the content covered by the action bar, so you can do layout within
+ * that space.
*/
public static final int FEATURE_ACTION_BAR_OVERLAY = 9;
/**
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index f2ee9f9..a45a87e 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -510,8 +510,13 @@
String name = root.getClass().getName() + '@' +
Integer.toHexString(hashCode());
- pw.printf(" %s: %d views, %.2f kB (display lists)\n",
+ pw.printf(" %s: %d views, %.2f kB (display lists)",
name, info[0], info[1] / 1024.0f);
+ HardwareRenderer renderer = root.getView().mAttachInfo.mHardwareRenderer;
+ if (renderer != null) {
+ pw.printf(", %d frames rendered", renderer.getFrameCount());
+ }
+ pw.printf("\n");
viewsCount += info[0];
displayListsSize += info[1];
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 58844fc..0998c80 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -59,6 +59,12 @@
* by this class. For each event type there is a corresponding constant defined
* in this class. Follows a specification of the event types and their associated properties:
* </p>
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about creating and processing AccessibilityEvents, read the
+ * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
+ * developer guide.</p>
+ * </div>
* <p>
* <b>VIEW TYPES</b></br>
* </p>
diff --git a/core/java/android/view/accessibility/AccessibilityEventSource.java b/core/java/android/view/accessibility/AccessibilityEventSource.java
index f11880b..525ba9e 100644
--- a/core/java/android/view/accessibility/AccessibilityEventSource.java
+++ b/core/java/android/view/accessibility/AccessibilityEventSource.java
@@ -18,6 +18,13 @@
/**
* This interface is implemented by classes source of {@link AccessibilityEvent}s.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about making applications accessible, read the
+ * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
+ * developer guide.</p>
+ * </div>
*/
public interface AccessibilityEventSource {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 03c6211..f616dca 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -43,6 +43,12 @@
* details about how to obtain a handle to window content as a tree of accessibility
* node info as well as familiarizing with the security model.
* </p>
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about making applications accessible, read the
+ * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
+ * developer guide.</p>
+ * </div>
*
* @see android.accessibilityservice.AccessibilityService
* @see AccessibilityEvent
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index bc6074f..d25b3db 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -41,6 +41,13 @@
* event types. For detailed information please refer to {@link AccessibilityEvent}.
* </p>
*
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about creating and processing AccessibilityRecords, read the
+ * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
+ * developer guide.</p>
+ * </div>
+ *
* @see AccessibilityEvent
* @see AccessibilityManager
* @see android.accessibilityservice.AccessibilityService
diff --git a/core/java/android/view/accessibility/package.html b/core/java/android/view/accessibility/package.html
index 4afafd3..c2da0ae 100644
--- a/core/java/android/view/accessibility/package.html
+++ b/core/java/android/view/accessibility/package.html
@@ -35,5 +35,12 @@
changes etc. Parties interested in handling accessibility events implement and register an
accessibility service which extends {@link android.accessibilityservice.AccessibilityService}.
</p>
+<div class="special reference">
+<h3>Developer Guides</h3>
+<p>For more information about making applications accessible, read the
+<a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
+developer guide.</p>
+</div>
+
</body>
</html>
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
index 94b46fc..aa3d8d3 100644
--- a/core/java/android/webkit/WebSettingsClassic.java
+++ b/core/java/android/webkit/WebSettingsClassic.java
@@ -33,7 +33,7 @@
*/
public class WebSettingsClassic extends WebSettings {
// TODO: Keep this up to date
- private static final String PREVIOUS_VERSION = "4.0.3";
+ private static final String PREVIOUS_VERSION = "4.0.4";
// WebView associated with this WebSettings.
private WebViewClassic mWebView;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 422b48d..9492e38 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1913,13 +1913,6 @@
}
@Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- // Not using short-circuit OR: provider does suppress base-class call.
- return mProvider.getViewDelegate().drawChild(canvas, child, drawingTime) |
- super.drawChild(canvas, child, drawingTime);
- }
-
- @Override
protected void onDraw(Canvas canvas) {
mProvider.getViewDelegate().onDraw(canvas);
}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 04fa07a..4c118ac 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1204,10 +1204,8 @@
static final int SHOW_FULLSCREEN = 120;
static final int HIDE_FULLSCREEN = 121;
static final int REPLACE_BASE_CONTENT = 123;
- static final int FORM_DID_BLUR = 124;
static final int UPDATE_MATCH_COUNT = 126;
static final int CENTER_FIT_RECT = 127;
- static final int REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID = 128;
static final int SET_SCROLLBAR_MODES = 129;
static final int SELECTION_STRING_CHANGED = 130;
static final int HIT_TEST_RESULT = 131;
@@ -1274,7 +1272,6 @@
"HIDE_FULLSCREEN", // = 121;
"DOM_FOCUS_CHANGED", // = 122;
"REPLACE_BASE_CONTENT", // = 123;
- "FORM_DID_BLUR", // = 124;
"RETURN_LABEL", // = 125;
"UPDATE_MATCH_COUNT", // = 126;
"CENTER_FIT_RECT", // = 127;
@@ -2014,7 +2011,7 @@
if (mWebView instanceof TitleBarDelegate) {
return ((TitleBarDelegate) mWebView).getTitleHeight();
}
- return mTitleBar != null ? mTitleBar.getHeight() : 0;
+ return 0;
}
/**
@@ -2946,50 +2943,6 @@
}
/**
- * A title bar which is embedded in this WebView, and scrolls along with it
- * vertically, but not horizontally.
- */
- private View mTitleBar;
-
- /**
- * the title bar rendering gravity
- */
- private int mTitleGravity;
-
- /**
- * Add or remove a title bar to be embedded into the WebView, and scroll
- * along with it vertically, while remaining in view horizontally. Pass
- * null to remove the title bar from the WebView, and return to drawing
- * the WebView normally without translating to account for the title bar.
- */
- public void setEmbeddedTitleBar(View v) {
- if (mWebView instanceof TitleBarDelegate) {
- ((TitleBarDelegate) mWebView).onSetEmbeddedTitleBar(v);
- }
- if (mTitleBar == v) return;
- if (mTitleBar != null) {
- mWebView.removeView(mTitleBar);
- }
- if (null != v) {
- mWebView.addView(v, new AbsoluteLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
- }
- mTitleBar = v;
- }
-
- /**
- * Set where to render the embedded title bar
- * NO_GRAVITY at the top of the page
- * TOP at the top of the screen
- */
- public void setTitleBarGravity(int gravity) {
- mTitleGravity = gravity;
- // force refresh
- invalidate();
- }
-
- /**
* Given a distance in view space, convert it to content space. Note: this
* does not reflect translation, just scaling, so this should not be called
* with coordinates, but should be called for dimensions like width or
@@ -4187,7 +4140,7 @@
// animate the title bar off screen slowly enough that the user can see
// it.
if (cx == 0 && cy == 1 && getScrollX() == 0 && getScrollY() == 0
- && mTitleBar != null) {
+ && getTitleHeight() > 0) {
// FIXME: 100 should be defined somewhere as our max progress.
if (getProgress() < 100) {
// Wait to scroll the title bar off screen until the page has
@@ -4404,24 +4357,6 @@
}
}
- @Override
- public boolean drawChild(Canvas canvas, View child, long drawingTime) {
- if (child == mTitleBar) {
- // When drawing the title bar, move it horizontally to always show
- // at the top of the WebView.
- mTitleBar.offsetLeftAndRight(getScrollX() - mTitleBar.getLeft());
- int newTop = 0;
- if (mTitleGravity == Gravity.NO_GRAVITY) {
- newTop = Math.min(0, getScrollY());
- } else if (mTitleGravity == Gravity.TOP) {
- newTop = getScrollY();
- }
- mTitleBar.setBottom(newTop + mTitleBar.getHeight());
- mTitleBar.setTop(newTop);
- }
- return false; // We never call invalidate(), so unconditionally returning false.
- }
-
private void drawContent(Canvas canvas) {
if (mDrawHistory) {
canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
@@ -4586,9 +4521,8 @@
.getUseWebViewBackgroundForOverscrollBackground()) {
drawOverScrollBackground(canvas);
}
- if (mTitleBar != null) {
- canvas.translate(0, getTitleHeight());
- }
+
+ canvas.translate(0, getTitleHeight());
drawContent(canvas);
canvas.restoreToCount(saveCount);
@@ -8288,16 +8222,10 @@
}
}
break;
- case REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID:
- displaySoftKeyboard(true);
- // fall through to UPDATE_TEXT_SELECTION_MSG_ID
case UPDATE_TEXT_SELECTION_MSG_ID:
updateTextSelectionFromMessage(msg.arg1, msg.arg2,
(WebViewCore.TextSelectionData) msg.obj);
break;
- case FORM_DID_BLUR:
- // TODO: Figure out if this is needed for something (b/6111763)
- break;
case TAKE_FOCUS:
int direction = msg.arg1;
View focusSearch = mWebView.focusSearch(direction);
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 3eba6d7..b4ebc09 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -325,17 +325,6 @@
}
/**
- * Called by JNI. Send a message to the UI thread to hide the soft keyboard
- * if the node pointed to by nodePointer is still in focus.
- * @param nodePointer The node which just blurred.
- */
- private void formDidBlur(int nodePointer) {
- if (mWebViewClassic == null) return;
- Message.obtain(mWebViewClassic.mPrivateHandler, WebViewClassic.FORM_DID_BLUR,
- nodePointer, 0).sendToTarget();
- }
-
- /**
* Called by JNI when the focus node changed.
*/
private void focusNodeChanged(int nodePointer, WebKitHitTest hitTest) {
@@ -2830,7 +2819,7 @@
Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.INIT_EDIT_FIELD, initData).sendToTarget();
Message.obtain(mWebViewClassic.mPrivateHandler,
- WebViewClassic.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID,
+ WebViewClassic.UPDATE_TEXT_SELECTION_MSG_ID,
initData.mFieldPointer, 0,
new TextSelectionData(start, end, selectionPtr))
.sendToTarget();
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index f049198..74a215c 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -287,8 +287,6 @@
public void onWindowVisibilityChanged(int visibility);
- public boolean drawChild(Canvas canvas, View child, long drawingTime);
-
public void onDraw(Canvas canvas);
public void setLayoutParams(LayoutParams layoutParams);
diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java
index 0370049..9c9eb4b 100644
--- a/core/java/android/widget/Chronometer.java
+++ b/core/java/android/widget/Chronometer.java
@@ -25,6 +25,7 @@
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
@@ -247,6 +248,7 @@
}
}
setText(text);
+ Slog.v("Chronometer", "updateText: sec=" + seconds + " mFormat=" + mFormat + " text=" + text);
}
private void updateRunning() {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 880dc34..cbff58c 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1609,7 +1609,6 @@
private boolean mCancelled;
public void run() {
- Log.d("GILLES", "blinking !!!");
if (mCancelled) {
return;
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index b1a75e1..91e2e497 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -201,7 +201,7 @@
@Override
protected boolean onSetAlpha(int alpha) {
- if (getBackground() == null) {
+ if (!USE_DISPLAY_LIST_PROPERTIES && getBackground() == null) {
int scale = alpha + (alpha >> 7);
if (mViewAlphaScale != scale) {
mViewAlphaScale = scale;
@@ -214,6 +214,15 @@
}
@Override
+ public boolean hasOverlappingRendering() {
+ if (!USE_DISPLAY_LIST_PROPERTIES) {
+ return super.hasOverlappingRendering();
+ } else {
+ return (getBackground() != null);
+ }
+ }
+
+ @Override
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
super.onPopulateAccessibilityEvent(event);
CharSequence contentDescription = getContentDescription();
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 4e56cd6..d897a39 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -112,10 +112,10 @@
private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800;
/**
- * The duration of scrolling to the next/previous value while changing the
- * current value by one, i.e. increment or decrement.
+ * The duration of scrolling to the next/previous value while snapping to
+ * a given position.
*/
- private static final int CHANGE_CURRENT_BY_ONE_SCROLL_DURATION = 300;
+ private static final int SNAP_SCROLL_DURATION = 300;
/**
* The strength of fading in the top and bottom while drawing the selector.
@@ -140,7 +140,7 @@
/**
* Coefficient for adjusting touch scroll distance.
*/
- private static final float TOUCH_SCROLL_DECELERATION_COEFFICIENT = 2.5f;
+ private static final float TOUCH_SCROLL_DECELERATION_COEFFICIENT = 2.0f;
/**
* The resource id for the default layout.
@@ -152,7 +152,7 @@
*/
private static final char[] DIGIT_CHARACTERS = new char[] {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
- };
+ };
/**
* Constant for unspecified size.
@@ -838,7 +838,13 @@
if (absDeltaMoveY > mMinFlingDistance) {
fling(initialVelocity);
} else {
- changeValueByOne(deltaMove < 0);
+ final int normalizedDeltaMove =
+ (int) (absDeltaMoveY / TOUCH_SCROLL_DECELERATION_COEFFICIENT);
+ if (normalizedDeltaMove < mSelectorElementHeight) {
+ snapToNextValue(deltaMove < 0);
+ } else {
+ snapToClosestValue();
+ }
}
onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
} else {
@@ -1509,11 +1515,9 @@
}
mPreviousScrollerY = 0;
if (increment) {
- mFlingScroller.startScroll(0, 0, 0, -mSelectorElementHeight,
- CHANGE_CURRENT_BY_ONE_SCROLL_DURATION);
+ mFlingScroller.startScroll(0, 0, 0, -mSelectorElementHeight, SNAP_SCROLL_DURATION);
} else {
- mFlingScroller.startScroll(0, 0, 0, mSelectorElementHeight,
- CHANGE_CURRENT_BY_ONE_SCROLL_DURATION);
+ mFlingScroller.startScroll(0, 0, 0, mSelectorElementHeight, SNAP_SCROLL_DURATION);
}
invalidate();
} else {
@@ -1902,6 +1906,42 @@
return false;
}
+ private void snapToNextValue(boolean increment) {
+ int deltaY = mCurrentScrollOffset - mInitialScrollOffset;
+ int amountToScroll = 0;
+ if (deltaY != 0) {
+ mPreviousScrollerY = 0;
+ if (deltaY > 0) {
+ if (increment) {
+ amountToScroll = - deltaY;
+ } else {
+ amountToScroll = mSelectorElementHeight - deltaY;
+ }
+ } else {
+ if (increment) {
+ amountToScroll = - mSelectorElementHeight - deltaY;
+ } else {
+ amountToScroll = - deltaY;
+ }
+ }
+ mFlingScroller.startScroll(0, 0, 0, amountToScroll, SNAP_SCROLL_DURATION);
+ invalidate();
+ }
+ }
+
+ private void snapToClosestValue() {
+ // adjust to the closest value
+ int deltaY = mInitialScrollOffset - mCurrentScrollOffset;
+ if (deltaY != 0) {
+ mPreviousScrollerY = 0;
+ if (Math.abs(deltaY) > mSelectorElementHeight / 2) {
+ deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight;
+ }
+ mFlingScroller.startScroll(0, 0, 0, deltaY, SNAP_SCROLL_DURATION);
+ invalidate();
+ }
+ }
+
/**
* Command for setting the input text selection.
*/
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 586fdf4..8067435 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -477,8 +477,11 @@
private static final String TAG = "FixedSizeRemoteViewsCache";
// The meta data related to all the RemoteViews, ie. count, is stable, etc.
- private RemoteViewsMetaData mMetaData;
- private RemoteViewsMetaData mTemporaryMetaData;
+ // The meta data objects are made final so that they can be locked on independently
+ // of the FixedSizeRemoteViewsCache. If we ever lock on both meta data objects, it is in
+ // the order mTemporaryMetaData followed by mMetaData.
+ private final RemoteViewsMetaData mMetaData;
+ private final RemoteViewsMetaData mTemporaryMetaData;
// The cache/mapping of position to RemoteViewsMetaData. This set is guaranteed to be
// greater than or equal to the set of RemoteViews.
@@ -939,6 +942,10 @@
* which wouldn't otherwise be possible.
*/
public void setVisibleRangeHint(int lowerBound, int upperBound) {
+ if (lowerBound < 0 || upperBound < 0) {
+ throw new RuntimeException("Attempted to set invalid range: lowerBound="+lowerBound +
+ "," + "upperBound="+upperBound);
+ }
mVisibleWindowLowerBound = lowerBound;
mVisibleWindowUpperBound = upperBound;
}
@@ -1072,12 +1079,20 @@
// Re-request the new metadata (only after the notification to the factory)
updateTemporaryMetaData();
+ int newCount;
+ synchronized(mCache.getTemporaryMetaData()) {
+ newCount = mCache.getTemporaryMetaData().count;
+ }
// Pre-load (our best guess of) the views which are currently visible in the AdapterView.
// This mitigates flashing and flickering of loading views when a widget notifies that
// its data has changed.
for (int i = mVisibleWindowLowerBound; i <= mVisibleWindowUpperBound; i++) {
- updateRemoteViews(i, false, false);
+ // Because temporary meta data is only ever modified from this thread (ie.
+ // mWorkerThread), it is safe to assume that count is a valid representation.
+ if (i < newCount) {
+ updateRemoteViews(i, false, false);
+ }
}
// Propagate the notification back to the base adapter
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2a81f08..9867e47 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4268,7 +4268,8 @@
protected boolean onSetAlpha(int alpha) {
// Alpha is supported if and only if the drawing can be done in one pass.
// TODO text with spans with a background color currently do not respect this alpha.
- if (getBackground() == null) {
+ if (!USE_DISPLAY_LIST_PROPERTIES &&
+ (getBackground() != null || mText instanceof Spannable || hasSelection())) {
if (mCurrentAlpha != alpha) {
mCurrentAlpha = alpha;
final Drawables dr = mDrawables;
@@ -4292,6 +4293,15 @@
return false;
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ if (!USE_DISPLAY_LIST_PROPERTIES) {
+ return super.hasOverlappingRendering();
+ } else {
+ return (getBackground() != null || mText instanceof Spannable || hasSelection());
+ }
+ }
+
/**
* When a TextView is used to display a useful piece of information to the user (such as a
* contact's address), it should be made selectable, so that the user can select and copy this
@@ -5330,24 +5340,63 @@
physicalWidth, false);
}
+ @Override
+ public void onResolvedLayoutDirectionReset() {
+ if (mLayoutAlignment != null) {
+ int resolvedTextAlignment = getResolvedTextAlignment();
+ if (resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_START ||
+ resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_END) {
+ mLayoutAlignment = null;
+ }
+ }
+ }
+
private Layout.Alignment getLayoutAlignment() {
if (mLayoutAlignment == null) {
- switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
- case Gravity.START:
+ int textAlign = getResolvedTextAlignment();
+ switch (textAlign) {
+ case TEXT_ALIGNMENT_GRAVITY:
+ switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.START:
+ mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
+ break;
+ case Gravity.END:
+ mLayoutAlignment = Layout.Alignment.ALIGN_OPPOSITE;
+ break;
+ case Gravity.LEFT:
+ mLayoutAlignment = Layout.Alignment.ALIGN_LEFT;
+ break;
+ case Gravity.RIGHT:
+ mLayoutAlignment = Layout.Alignment.ALIGN_RIGHT;
+ break;
+ case Gravity.CENTER_HORIZONTAL:
+ mLayoutAlignment = Layout.Alignment.ALIGN_CENTER;
+ break;
+ default:
+ mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
+ break;
+ }
+ break;
+ case TEXT_ALIGNMENT_TEXT_START:
mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
break;
- case Gravity.END:
+ case TEXT_ALIGNMENT_TEXT_END:
mLayoutAlignment = Layout.Alignment.ALIGN_OPPOSITE;
break;
- case Gravity.LEFT:
- mLayoutAlignment = Layout.Alignment.ALIGN_LEFT;
- break;
- case Gravity.RIGHT:
- mLayoutAlignment = Layout.Alignment.ALIGN_RIGHT;
- break;
- case Gravity.CENTER_HORIZONTAL:
+ case TEXT_ALIGNMENT_CENTER:
mLayoutAlignment = Layout.Alignment.ALIGN_CENTER;
break;
+ case TEXT_ALIGNMENT_VIEW_START:
+ mLayoutAlignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+ Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;
+ break;
+ case TEXT_ALIGNMENT_VIEW_END:
+ mLayoutAlignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+ Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT;
+ break;
+ case TEXT_ALIGNMENT_INHERIT:
+ // This should never happen as we have already resolved the text alignment
+ // but better safe than sorry so we just fall through
default:
mLayoutAlignment = Layout.Alignment.ALIGN_NORMAL;
break;
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index f3486bd..3115eff 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -21,6 +21,7 @@
import com.android.internal.view.menu.SubMenuBuilder;
import com.android.internal.widget.ActionBarContainer;
import com.android.internal.widget.ActionBarContextView;
+import com.android.internal.widget.ActionBarOverlayLayout;
import com.android.internal.widget.ActionBarView;
import com.android.internal.widget.ScrollingTabContainerView;
@@ -47,6 +48,7 @@
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.view.Window;
import android.view.accessibility.AccessibilityEvent;
import android.widget.SpinnerAdapter;
@@ -69,7 +71,9 @@
private Activity mActivity;
private Dialog mDialog;
+ private ActionBarOverlayLayout mOverlayLayout;
private ActionBarContainer mContainerView;
+ private ViewGroup mTopVisibilityView;
private ActionBarView mActionView;
private ActionBarContextView mContextView;
private ActionBarContainer mSplitView;
@@ -100,6 +104,8 @@
final Handler mHandler = new Handler();
Runnable mTabSelector;
+ private int mCurWindowVisibility = View.VISIBLE;
+
private Animator mCurrentShowAnim;
private Animator mCurrentModeAnim;
private boolean mShowHideAnimationEnabled;
@@ -110,12 +116,12 @@
public void onAnimationEnd(Animator animation) {
if (mContentView != null) {
mContentView.setTranslationY(0);
- mContainerView.setTranslationY(0);
+ mTopVisibilityView.setTranslationY(0);
}
if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
mSplitView.setVisibility(View.GONE);
}
- mContainerView.setVisibility(View.GONE);
+ mTopVisibilityView.setVisibility(View.GONE);
mContainerView.setTransitioning(false);
mCurrentShowAnim = null;
completeDeferredDestroyActionMode();
@@ -126,7 +132,7 @@
@Override
public void onAnimationEnd(Animator animation) {
mCurrentShowAnim = null;
- mContainerView.requestLayout();
+ mTopVisibilityView.requestLayout();
}
};
@@ -147,11 +153,21 @@
private void init(View decor) {
mContext = decor.getContext();
+ mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById(
+ com.android.internal.R.id.action_bar_overlay_layout);
+ if (mOverlayLayout != null) {
+ mOverlayLayout.setActionBar(this);
+ }
mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
mContextView = (ActionBarContextView) decor.findViewById(
com.android.internal.R.id.action_context_bar);
mContainerView = (ActionBarContainer) decor.findViewById(
com.android.internal.R.id.action_bar_container);
+ mTopVisibilityView = (ViewGroup)decor.findViewById(
+ com.android.internal.R.id.top_action_bar);
+ if (mTopVisibilityView == null) {
+ mTopVisibilityView = mContainerView;
+ }
mSplitView = (ActionBarContainer) decor.findViewById(
com.android.internal.R.id.split_action_bar);
@@ -190,11 +206,22 @@
}
final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS;
if (mTabScrollView != null) {
- mTabScrollView.setVisibility(isInTabMode ? View.VISIBLE : View.GONE);
+ if (isInTabMode) {
+ mTabScrollView.setVisibility(View.VISIBLE);
+ if (mOverlayLayout != null) {
+ mOverlayLayout.requestFitSystemWindows();
+ }
+ } else {
+ mTabScrollView.setVisibility(View.GONE);
+ }
}
mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode);
}
+ public boolean hasNonEmbeddedTabs() {
+ return !mHasEmbeddedTabs && getNavigationMode() == NAVIGATION_MODE_TABS;
+ }
+
private void ensureTabsExist() {
if (mTabScrollView != null) {
return;
@@ -206,8 +233,14 @@
tabScroller.setVisibility(View.VISIBLE);
mActionView.setEmbeddedTabView(tabScroller);
} else {
- tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ?
- View.VISIBLE : View.GONE);
+ if (getNavigationMode() == NAVIGATION_MODE_TABS) {
+ tabScroller.setVisibility(View.VISIBLE);
+ if (mOverlayLayout != null) {
+ mOverlayLayout.requestFitSystemWindows();
+ }
+ } else {
+ tabScroller.setVisibility(View.GONE);
+ }
mContainerView.setTabContainer(tabScroller);
}
mTabScrollView = tabScroller;
@@ -221,6 +254,10 @@
}
}
+ public void setWindowVisibility(int visibility) {
+ mCurWindowVisibility = visibility;
+ }
+
/**
* Enables or disables animation between show/hide states.
* If animation is disabled using this method, animations in progress
@@ -396,7 +433,12 @@
animateToMode(true);
if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
// TODO animate this
- mSplitView.setVisibility(View.VISIBLE);
+ if (mSplitView.getVisibility() != View.VISIBLE) {
+ mSplitView.setVisibility(View.VISIBLE);
+ if (mOverlayLayout != null) {
+ mOverlayLayout.requestFitSystemWindows();
+ }
+ }
}
mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
mActionMode = mode;
@@ -530,28 +572,29 @@
@Override
public void show() {
- show(true);
+ show(true, false);
}
- void show(boolean markHiddenBeforeMode) {
+ public void show(boolean markHiddenBeforeMode, boolean alwaysAnimate) {
if (mCurrentShowAnim != null) {
mCurrentShowAnim.end();
}
- if (mContainerView.getVisibility() == View.VISIBLE) {
+ if (mTopVisibilityView.getVisibility() == View.VISIBLE) {
if (markHiddenBeforeMode) mWasHiddenBeforeMode = false;
return;
}
- mContainerView.setVisibility(View.VISIBLE);
+ mTopVisibilityView.setVisibility(View.VISIBLE);
- if (mShowHideAnimationEnabled) {
- mContainerView.setAlpha(0);
+ if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled
+ || alwaysAnimate)) {
+ mTopVisibilityView.setAlpha(0);
AnimatorSet anim = new AnimatorSet();
- AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 1));
+ AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mTopVisibilityView, "alpha", 1));
if (mContentView != null) {
b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
- -mContainerView.getHeight(), 0));
- mContainerView.setTranslationY(-mContainerView.getHeight());
- b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0));
+ -mTopVisibilityView.getHeight(), 0));
+ mTopVisibilityView.setTranslationY(-mTopVisibilityView.getHeight());
+ b.with(ObjectAnimator.ofFloat(mTopVisibilityView, "translationY", 0));
}
if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) {
mSplitView.setAlpha(0);
@@ -562,7 +605,7 @@
mCurrentShowAnim = anim;
anim.start();
} else {
- mContainerView.setAlpha(1);
+ mTopVisibilityView.setAlpha(1);
mContainerView.setTranslationY(0);
mShowListener.onAnimationEnd(null);
}
@@ -570,23 +613,28 @@
@Override
public void hide() {
+ hide(false);
+ }
+
+ public void hide(boolean alwaysAnimate) {
if (mCurrentShowAnim != null) {
mCurrentShowAnim.end();
}
- if (mContainerView.getVisibility() == View.GONE) {
+ if (mTopVisibilityView.getVisibility() == View.GONE) {
return;
}
- if (mShowHideAnimationEnabled) {
- mContainerView.setAlpha(1);
+ if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled
+ || alwaysAnimate)) {
+ mTopVisibilityView.setAlpha(1);
mContainerView.setTransitioning(true);
AnimatorSet anim = new AnimatorSet();
- AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 0));
+ AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mTopVisibilityView, "alpha", 0));
if (mContentView != null) {
b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
- 0, -mContainerView.getHeight()));
- b.with(ObjectAnimator.ofFloat(mContainerView, "translationY",
- -mContainerView.getHeight()));
+ 0, -mTopVisibilityView.getHeight()));
+ b.with(ObjectAnimator.ofFloat(mTopVisibilityView, "translationY",
+ -mTopVisibilityView.getHeight()));
}
if (mSplitView != null && mSplitView.getVisibility() == View.VISIBLE) {
mSplitView.setAlpha(1);
@@ -601,12 +649,12 @@
}
public boolean isShowing() {
- return mContainerView.getVisibility() == View.VISIBLE;
+ return mTopVisibilityView.getVisibility() == View.VISIBLE;
}
void animateToMode(boolean toActionMode) {
if (toActionMode) {
- show(false);
+ show(false, false);
}
if (mCurrentModeAnim != null) {
mCurrentModeAnim.end();
@@ -980,6 +1028,11 @@
mTabScrollView.setVisibility(View.GONE);
break;
}
+ if (oldMode != mode && !mHasEmbeddedTabs) {
+ if (mOverlayLayout != null) {
+ mOverlayLayout.requestFitSystemWindows();
+ }
+ }
mActionView.setNavigationMode(mode);
switch (mode) {
case NAVIGATION_MODE_TABS:
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 6e36fdb..294d4c4 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -30,7 +30,7 @@
void disable(int state);
void animateExpand();
void animateCollapse();
- void setSystemUiVisibility(int vis);
+ void setSystemUiVisibility(int vis, int mask);
void topAppWindowChanged(boolean menuVisible);
void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
void setHardKeyboardStatus(boolean available, boolean enabled);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 118e541..c64f170 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -44,7 +44,7 @@
int uid, int initialPid, String message);
void onClearAllNotifications();
void onNotificationClear(String pkg, String tag, int id);
- void setSystemUiVisibility(int vis);
+ void setSystemUiVisibility(int vis, int mask);
void setHardKeyboardEnabled(boolean enabled);
void toggleRecentApps();
void preloadRecentApps();
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index d462d7f..7c2b1b5 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -49,7 +49,7 @@
public static final int BASE_DATA_CONNECTION = 0x00040000;
public static final int BASE_DATA_CONNECTION_AC = 0x00041000;
public static final int BASE_DATA_CONNECTION_TRACKER = 0x00042000;
-
public static final int BASE_DNS_PINGER = 0x00050000;
+ public static final int BASE_NSD_MANAGER = 0x00060000;
//TODO: define all used protocols
}
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
new file mode 100644
index 0000000..8521481
--- /dev/null
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -0,0 +1,195 @@
+/*
+ * 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.internal.widget;
+
+import com.android.internal.app.ActionBarImpl;
+
+import android.animation.LayoutTransition;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
+
+/**
+ * Special layout for the containing of an overlay action bar (and its
+ * content) to correctly handle fitting system windows when the content
+ * has request that its layout ignore them.
+ */
+public class ActionBarOverlayLayout extends FrameLayout {
+ private int mActionBarHeight;
+ private ActionBarImpl mActionBar;
+ private int mWindowVisibility = View.VISIBLE;
+ private View mContent;
+ private View mActionBarTop;
+ private ActionBarContainer mContainerView;
+ private ActionBarView mActionView;
+ private View mActionBarBottom;
+ private int mLastSystemUiVisibility;
+ private final Rect mZeroRect = new Rect(0, 0, 0, 0);
+
+ static final int[] mActionBarSizeAttr = new int [] {
+ com.android.internal.R.attr.actionBarSize
+ };
+
+ public ActionBarOverlayLayout(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public ActionBarOverlayLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ private void init(Context context) {
+ TypedArray ta = getContext().getTheme().obtainStyledAttributes(mActionBarSizeAttr);
+ mActionBarHeight = ta.getDimensionPixelSize(0, 0);
+ ta.recycle();
+ }
+
+ public void setActionBar(ActionBarImpl impl) {
+ mActionBar = impl;
+ if (getWindowToken() != null) {
+ // This is being initialized after being added to a window;
+ // make sure to update all state now.
+ mActionBar.setWindowVisibility(mWindowVisibility);
+ if (mLastSystemUiVisibility != 0) {
+ int newVis = mLastSystemUiVisibility;
+ onWindowSystemUiVisibilityChanged(newVis);
+ requestFitSystemWindows();
+ }
+ }
+ }
+
+ @Override
+ public void onWindowSystemUiVisibilityChanged(int visible) {
+ super.onWindowSystemUiVisibilityChanged(visible);
+ pullChildren();
+ final int diff = mLastSystemUiVisibility ^ visible;
+ mLastSystemUiVisibility = visible;
+ final boolean barVisible = (visible&SYSTEM_UI_FLAG_FULLSCREEN) == 0;
+ final boolean wasVisible = mActionBar != null ? mActionBar.isShowing() : true;
+ if (barVisible != wasVisible || (diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
+ if (mActionBar != null) {
+ if (barVisible) mActionBar.show(true, true);
+ else mActionBar.hide(true);
+ requestFitSystemWindows();
+ }
+ }
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ mWindowVisibility = visibility;
+ if (mActionBar != null) {
+ mActionBar.setWindowVisibility(visibility);
+ }
+ }
+
+ private boolean applyInsets(View view, Rect insets, boolean left, boolean top,
+ boolean bottom, boolean right) {
+ boolean changed = false;
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)view.getLayoutParams();
+ if (left && lp.leftMargin != insets.left) {
+ changed = true;
+ lp.leftMargin = insets.left;
+ }
+ if (top && lp.topMargin != insets.top) {
+ changed = true;
+ lp.topMargin = insets.top;
+ }
+ if (right && lp.rightMargin != insets.right) {
+ changed = true;
+ lp.rightMargin = insets.right;
+ }
+ if (bottom && lp.bottomMargin != insets.bottom) {
+ changed = true;
+ lp.bottomMargin = insets.bottom;
+ }
+ return changed;
+ }
+
+ @Override
+ protected boolean fitSystemWindows(Rect insets) {
+ pullChildren();
+
+ final int vis = getWindowSystemUiVisibility();
+ final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
+
+ // The top and bottom action bars are always within the content area.
+ boolean changed = applyInsets(mActionBarTop, insets, true, true, false, true);
+ if (mActionBarBottom != null) {
+ changed |= applyInsets(mActionBarBottom, insets, true, false, true, true);
+ }
+
+ // If the window has not requested system UI layout flags, we need to
+ // make sure its content is not being covered by system UI... though it
+ // will still be covered by the action bar since they have requested it to
+ // overlay.
+ if ((vis & SYSTEM_UI_LAYOUT_FLAGS) == 0) {
+ changed |= applyInsets(mContent, insets, true, true, true, true);
+ // The insets are now consumed.
+ insets.set(0, 0, 0, 0);
+ } else {
+ changed |= applyInsets(mContent, mZeroRect, true, true, true, true);
+ }
+
+
+ if (stable || mActionBarTop.getVisibility() == VISIBLE) {
+ // The action bar creates additional insets for its content to use.
+ insets.top += mActionBarHeight;
+ }
+
+ if (mActionBar != null && mActionBar.hasNonEmbeddedTabs()) {
+ View tabs = mContainerView.getTabContainer();
+ if (stable || (tabs != null && tabs.getVisibility() == VISIBLE)) {
+ // If tabs are not embedded, adjust insets to account for them.
+ insets.top += mActionBarHeight;
+ }
+ }
+
+ if (mActionView.isSplitActionBar()) {
+ if (stable || (mActionBarBottom != null
+ && mActionBarBottom.getVisibility() == VISIBLE)) {
+ // If action bar is split, adjust buttom insets for it.
+ insets.bottom += mActionBarHeight;
+ }
+ }
+
+ if (changed) {
+ requestLayout();
+ }
+
+ return super.fitSystemWindows(insets);
+ }
+
+ void pullChildren() {
+ if (mContent == null) {
+ mContent = findViewById(com.android.internal.R.id.content);
+ mActionBarTop = findViewById(com.android.internal.R.id.top_action_bar);
+ mContainerView = (ActionBarContainer)findViewById(
+ com.android.internal.R.id.action_bar_container);
+ mActionView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
+ mActionBarBottom = findViewById(com.android.internal.R.id.split_action_bar);
+ }
+ }
+}
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index 7c937ed..1a8e80f 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -519,98 +519,99 @@
* @throws MmsException Failed to load some fields of a PDU.
*/
public GenericPdu load(Uri uri) throws MmsException {
- PduCacheEntry cacheEntry;
- synchronized(PDU_CACHE_INSTANCE) {
- if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "load: " + uri + " blocked by isUpdating()");
- }
- try {
- PDU_CACHE_INSTANCE.wait();
- } catch (InterruptedException e) {
- Log.e(TAG, "load: ", e);
- }
- cacheEntry = PDU_CACHE_INSTANCE.get(uri);
- if (cacheEntry != null) {
- return cacheEntry.getPdu();
- }
- }
- // Tell the cache to indicate to other callers that this item
- // is currently being updated.
- PDU_CACHE_INSTANCE.setUpdating(uri, true);
- }
-
- Cursor c = SqliteWrapper.query(mContext, mContentResolver, uri,
- PDU_PROJECTION, null, null, null);
- PduHeaders headers = new PduHeaders();
- Set<Entry<Integer, Integer>> set;
- long msgId = ContentUris.parseId(uri);
- int msgBox;
- long threadId;
-
- try {
- if ((c == null) || (c.getCount() != 1) || !c.moveToFirst()) {
- throw new MmsException("Bad uri: " + uri);
- }
-
- msgBox = c.getInt(PDU_COLUMN_MESSAGE_BOX);
- threadId = c.getLong(PDU_COLUMN_THREAD_ID);
-
- set = ENCODED_STRING_COLUMN_INDEX_MAP.entrySet();
- for (Entry<Integer, Integer> e : set) {
- setEncodedStringValueToHeaders(
- c, e.getValue(), headers, e.getKey());
- }
-
- set = TEXT_STRING_COLUMN_INDEX_MAP.entrySet();
- for (Entry<Integer, Integer> e : set) {
- setTextStringToHeaders(
- c, e.getValue(), headers, e.getKey());
- }
-
- set = OCTET_COLUMN_INDEX_MAP.entrySet();
- for (Entry<Integer, Integer> e : set) {
- setOctetToHeaders(
- c, e.getValue(), headers, e.getKey());
- }
-
- set = LONG_COLUMN_INDEX_MAP.entrySet();
- for (Entry<Integer, Integer> e : set) {
- setLongToHeaders(
- c, e.getValue(), headers, e.getKey());
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
-
- // Check whether 'msgId' has been assigned a valid value.
- if (msgId == -1L) {
- throw new MmsException("Error! ID of the message: -1.");
- }
-
- // Load address information of the MM.
- loadAddress(msgId, headers);
-
- int msgType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
- PduBody body = new PduBody();
-
- // For PDU which type is M_retrieve.conf or Send.req, we should
- // load multiparts and put them into the body of the PDU.
- if ((msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
- || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
- PduPart[] parts = loadParts(msgId);
- if (parts != null) {
- int partsNum = parts.length;
- for (int i = 0; i < partsNum; i++) {
- body.addPart(parts[i]);
- }
- }
- }
-
GenericPdu pdu = null;
- switch (msgType) {
+ PduCacheEntry cacheEntry = null;
+ int msgBox = 0;
+ long threadId = -1;
+ try {
+ synchronized(PDU_CACHE_INSTANCE) {
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "load: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "load: ", e);
+ }
+ cacheEntry = PDU_CACHE_INSTANCE.get(uri);
+ if (cacheEntry != null) {
+ return cacheEntry.getPdu();
+ }
+ }
+ // Tell the cache to indicate to other callers that this item
+ // is currently being updated.
+ PDU_CACHE_INSTANCE.setUpdating(uri, true);
+ }
+
+ Cursor c = SqliteWrapper.query(mContext, mContentResolver, uri,
+ PDU_PROJECTION, null, null, null);
+ PduHeaders headers = new PduHeaders();
+ Set<Entry<Integer, Integer>> set;
+ long msgId = ContentUris.parseId(uri);
+
+ try {
+ if ((c == null) || (c.getCount() != 1) || !c.moveToFirst()) {
+ throw new MmsException("Bad uri: " + uri);
+ }
+
+ msgBox = c.getInt(PDU_COLUMN_MESSAGE_BOX);
+ threadId = c.getLong(PDU_COLUMN_THREAD_ID);
+
+ set = ENCODED_STRING_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setEncodedStringValueToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = TEXT_STRING_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setTextStringToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = OCTET_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setOctetToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = LONG_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setLongToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ // Check whether 'msgId' has been assigned a valid value.
+ if (msgId == -1L) {
+ throw new MmsException("Error! ID of the message: -1.");
+ }
+
+ // Load address information of the MM.
+ loadAddress(msgId, headers);
+
+ int msgType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
+ PduBody body = new PduBody();
+
+ // For PDU which type is M_retrieve.conf or Send.req, we should
+ // load multiparts and put them into the body of the PDU.
+ if ((msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
+ || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
+ PduPart[] parts = loadParts(msgId);
+ if (parts != null) {
+ int partsNum = parts.length;
+ for (int i = 0; i < partsNum; i++) {
+ body.addPart(parts[i]);
+ }
+ }
+ }
+
+ switch (msgType) {
case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
pdu = new NotificationInd(headers);
break;
@@ -657,16 +658,20 @@
default:
throw new MmsException(
"Unrecognized PDU type: " + Integer.toHexString(msgType));
+ }
+ } finally {
+ synchronized(PDU_CACHE_INSTANCE) {
+ if (pdu != null) {
+ assert(PDU_CACHE_INSTANCE.get(uri) == null);
+ // Update the cache entry with the real info
+ cacheEntry = new PduCacheEntry(pdu, msgBox, threadId);
+ PDU_CACHE_INSTANCE.put(uri, cacheEntry);
+ }
+ PDU_CACHE_INSTANCE.setUpdating(uri, false);
+ PDU_CACHE_INSTANCE.notifyAll(); // tell anybody waiting on this entry to go ahead
+ }
}
-
- synchronized(PDU_CACHE_INSTANCE ) {
- assert(PDU_CACHE_INSTANCE.get(uri) == null);
- // Update the cache entry with the real info
- cacheEntry = new PduCacheEntry(pdu, msgBox, threadId);
- PDU_CACHE_INSTANCE.put(uri, cacheEntry);
- PDU_CACHE_INSTANCE.notifyAll(); // tell anybody waiting on this entry to go ahead
- return pdu;
- }
+ return pdu;
}
private void persistAddress(
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 5e73a5f..3c27caf 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -236,7 +236,7 @@
0, 0, width, height, bitmap);
}
- return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL);
+ return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL, NULL);
}
static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
@@ -248,7 +248,7 @@
return NULL;
}
- return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL);
+ return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL, NULL);
}
static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {
@@ -407,7 +407,7 @@
bitmap->unlockPixels();
blob.release();
- return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, density);
+ return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, NULL, density);
}
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
@@ -485,7 +485,7 @@
env->ReleaseIntArrayElements(offsetXY, array, 0);
}
- return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL);
+ return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL, NULL);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index dcd1d28..dd59444 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -35,6 +35,7 @@
jfieldID gOptions_mCancelID;
jfieldID gOptions_bitmapFieldID;
jfieldID gBitmap_nativeBitmapFieldID;
+jfieldID gBitmap_layoutBoundsFieldID;
#if 0
#define TRACE_BITMAP(code) code
@@ -276,7 +277,7 @@
}
jbyteArray ninePatchChunk = NULL;
- if (peeker.fPatchIsValid) {
+ if (peeker.fPatch != NULL) {
if (willScale) {
scaleNinePatchChunk(peeker.fPatch, scale);
}
@@ -296,6 +297,18 @@
env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
}
+ jintArray layoutBounds = NULL;
+ if (peeker.fLayoutBounds != NULL) {
+ layoutBounds = env->NewIntArray(4);
+ if (layoutBounds == NULL) {
+ return nullObjectReturn("layoutBounds == null");
+ }
+
+ env->SetIntArrayRegion(layoutBounds, 0, 4, (jint*) peeker.fLayoutBounds);
+ if (javaBitmap != NULL) {
+ env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds);
+ }
+ }
// detach bitmap from its autodeleter, since we want to own it now
adb.detach();
@@ -321,7 +334,7 @@
}
if (padding) {
- if (peeker.fPatchIsValid) {
+ if (peeker.fPatch != NULL) {
GraphicsJNI::set_jrect(env, padding,
peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop,
peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom);
@@ -350,7 +363,7 @@
}
// now create the java bitmap
return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(),
- isMutable, ninePatchChunk);
+ isMutable, ninePatchChunk, layoutBounds, -1);
}
static jobject nativeDecodeStreamScaled(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
@@ -576,7 +589,7 @@
jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
SkASSERT(bitmap_class);
gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "I");
-
+ gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mLayoutBounds", "[I");
int ret = AndroidRuntime::registerNativeMethods(env,
"android/graphics/BitmapFactory$Options",
gOptionsMethods,
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 682877a..dd8e84f 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -244,7 +244,7 @@
JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
jbyteArray buff = allocator->getStorageObjAndReset();
- return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, -1);
+ return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, NULL, -1);
}
static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index a1d41ee..d4c7600 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -345,14 +345,14 @@
///////////////////////////////////////////////////////////////////////////////////////////
jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
- bool isMutable, jbyteArray ninepatch, int density)
+ bool isMutable, jbyteArray ninepatch, jintArray layoutbounds,
+ int density)
{
SkASSERT(bitmap);
SkASSERT(bitmap->pixelRef());
-
jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)),
- buffer, isMutable, ninepatch, density);
+ buffer, isMutable, ninepatch, layoutbounds, density);
hasException(env); // For the side effect of logging.
return obj;
}
@@ -360,7 +360,7 @@
jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
jbyteArray ninepatch, int density)
{
- return createBitmap(env, bitmap, NULL, isMutable, ninepatch, density);
+ return createBitmap(env, bitmap, NULL, isMutable, ninepatch, NULL, density);
}
@@ -587,7 +587,7 @@
gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
- "(I[BZ[BI)V");
+ "(I[BZ[B[II)V");
gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index cc32f44..c5b06f5 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -53,7 +53,8 @@
storage array (may be null).
*/
static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
- bool isMutable, jbyteArray ninepatch, int density = -1);
+ bool isMutable, jbyteArray ninepatch, jintArray layoutbounds,
+ int density = -1);
static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
jbyteArray ninepatch, int density = -1);
diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp
index 365d985..df996af 100644
--- a/core/jni/android/graphics/NinePatchPeeker.cpp
+++ b/core/jni/android/graphics/NinePatchPeeker.cpp
@@ -31,14 +31,11 @@
// this relies on deserialization being done in place
Res_png_9patch::deserialize(patchNew);
patchNew->fileToDevice();
- if (fPatchIsValid) {
- free(fPatch);
- }
+ free(fPatch);
fPatch = patchNew;
//printf("9patch: (%d,%d)-(%d,%d)\n",
// fPatch.sizeLeft, fPatch.sizeTop,
// fPatch.sizeRight, fPatch.sizeBottom);
- fPatchIsValid = true;
// now update our host to force index or 32bit config
// 'cause we don't want 565 predithered, since as a 9patch, we know
@@ -52,8 +49,9 @@
SkBitmap::kARGB_8888_Config,
};
fHost->setPrefConfigTable(gNo565Pref);
- } else {
- fPatch = NULL;
+ } else if (strcmp("npLb", tag) == 0 && length == sizeof(int) * 4) {
+ fLayoutBounds = new int[4];
+ memcpy(fLayoutBounds, data, sizeof(int) * 4);
}
return true; // keep on decoding
}
diff --git a/core/jni/android/graphics/NinePatchPeeker.h b/core/jni/android/graphics/NinePatchPeeker.h
index 207536c..10d268a 100644
--- a/core/jni/android/graphics/NinePatchPeeker.h
+++ b/core/jni/android/graphics/NinePatchPeeker.h
@@ -28,17 +28,17 @@
NinePatchPeeker(SkImageDecoder* host) {
// the host lives longer than we do, so a raw ptr is safe
fHost = host;
- fPatchIsValid = false;
+ fPatch = NULL;
+ fLayoutBounds = NULL;
}
~NinePatchPeeker() {
- if (fPatchIsValid) {
- free(fPatch);
- }
+ free(fPatch);
+ delete fLayoutBounds;
}
- bool fPatchIsValid;
Res_png_9patch* fPatch;
+ int *fLayoutBounds;
virtual bool peek(const char tag[], const void* data, size_t length);
};
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 480c3a6..2fe0b9e 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -254,7 +254,7 @@
// ----------------------------------------------------------------------------
static int
-android_media_AudioRecord_start(JNIEnv *env, jobject thiz)
+android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
{
AudioRecord *lpRecorder =
(AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
@@ -263,7 +263,8 @@
return AUDIORECORD_ERROR;
}
- return android_media_translateRecorderErrorCode(lpRecorder->start());
+ return android_media_translateRecorderErrorCode(
+ lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
}
@@ -508,7 +509,7 @@
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
// name, signature, funcPtr
- {"native_start", "()I", (void *)android_media_AudioRecord_start},
+ {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
{"native_stop", "()V", (void *)android_media_AudioRecord_stop},
{"native_setup", "(Ljava/lang/Object;IIIII[I)I",
(void *)android_media_AudioRecord_setup},
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index 31151a6..da6f1ed 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -65,6 +65,16 @@
lpToneGen->stopTone();
}
+static jint android_media_ToneGenerator_getAudioSessionId(JNIEnv *env, jobject thiz) {
+ ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
+ fields.context);
+ if (lpToneGen == NULL) {
+ jniThrowRuntimeException(env, "Method called after release()");
+ return 0;
+ }
+ return lpToneGen->getSessionId();
+}
+
static void android_media_ToneGenerator_release(JNIEnv *env, jobject thiz) {
ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
fields.context);
@@ -120,6 +130,7 @@
static JNINativeMethod gMethods[] = {
{ "startTone", "(II)Z", (void *)android_media_ToneGenerator_startTone },
{ "stopTone", "()V", (void *)android_media_ToneGenerator_stopTone },
+ { "getAudioSessionId", "()I", (void *)android_media_ToneGenerator_getAudioSessionId},
{ "release", "()V", (void *)android_media_ToneGenerator_release },
{ "native_setup", "(II)V", (void *)android_media_ToneGenerator_native_setup },
{ "native_finalize", "()V", (void *)android_media_ToneGenerator_native_finalize }
diff --git a/core/jni/android_util_Log.cpp b/core/jni/android_util_Log.cpp
index 2895171..536a582af 100644
--- a/core/jni/android_util_Log.cpp
+++ b/core/jni/android_util_Log.cpp
@@ -64,7 +64,7 @@
char buf[PROPERTY_VALUE_MAX];
if (property_get(key.string(), buf, "") <= 0) {
- return false;
+ buf[0] = '\0';
}
int logLevel = toLevel(buf);
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index b472eef..6028814 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -163,6 +163,21 @@
return renderer->callDrawGLFunction(functor, dirty);
}
+static jint android_view_GLES20Canvas_invokeFunctors(JNIEnv* env,
+ jobject clazz, OpenGLRenderer* renderer, jobject dirty) {
+ android::uirenderer::Rect bounds;
+ status_t status = renderer->invokeFunctors(bounds);
+ if (status != DrawGlInfo::kStatusDone && dirty != NULL) {
+ env->CallVoidMethod(dirty, gRectClassInfo.set,
+ int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));
+ }
+ return status;
+}
+
+// ----------------------------------------------------------------------------
+// Misc
+// ----------------------------------------------------------------------------
+
static jint android_view_GLES20Canvas_getMaxTextureWidth(JNIEnv* env, jobject clazz) {
return Caches::getInstance().maxTextureSize;
}
@@ -824,6 +839,8 @@
{ "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize },
{ "nCallDrawGLFunction", "(II)I", (void*) android_view_GLES20Canvas_callDrawGLFunction },
+ { "nInvokeFunctors", "(ILandroid/graphics/Rect;)I",
+ (void*) android_view_GLES20Canvas_invokeFunctors },
{ "nSave", "(II)I", (void*) android_view_GLES20Canvas_save },
{ "nRestore", "(I)V", (void*) android_view_GLES20Canvas_restore },
@@ -899,9 +916,9 @@
{ "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList },
{ "nGetDisplayListSize", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListSize },
{ "nSetDisplayListName", "(ILjava/lang/String;)V",
- (void*) android_view_GLES20Canvas_setDisplayListName },
+ (void*) android_view_GLES20Canvas_setDisplayListName },
{ "nDrawDisplayList", "(IIIILandroid/graphics/Rect;I)I",
- (void*) android_view_GLES20Canvas_drawDisplayList },
+ (void*) android_view_GLES20Canvas_drawDisplayList },
{ "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer },
{ "nResetDisplayListRenderer", "(I)V", (void*) android_view_GLES20Canvas_resetDisplayListRenderer },
diff --git a/core/jni/android_view_GLES20DisplayList.cpp b/core/jni/android_view_GLES20DisplayList.cpp
index 407c196..b307a2f 100644
--- a/core/jni/android_view_GLES20DisplayList.cpp
+++ b/core/jni/android_view_GLES20DisplayList.cpp
@@ -45,9 +45,14 @@
displayList->setCaching(caching);
}
-static void android_view_GLES20DisplayList_setApplicationScale(JNIEnv* env,
- jobject clazz, DisplayList* displayList, float scale) {
- displayList->setApplicationScale(scale);
+static void android_view_GLES20DisplayList_setStaticMatrix(JNIEnv* env,
+ jobject clazz, DisplayList* displayList, SkMatrix* matrix) {
+ displayList->setStaticMatrix(matrix);
+}
+
+static void android_view_GLES20DisplayList_setAnimationMatrix(JNIEnv* env,
+ jobject clazz, DisplayList* displayList, SkMatrix* matrix) {
+ displayList->setAnimationMatrix(matrix);
}
static void android_view_GLES20DisplayList_setClipChildren(JNIEnv* env,
@@ -60,6 +65,11 @@
displayList->setAlpha(alpha);
}
+static void android_view_GLES20DisplayList_setHasOverlappingRendering(JNIEnv* env,
+ jobject clazz, DisplayList* displayList, bool hasOverlappingRendering) {
+ displayList->setHasOverlappingRendering(hasOverlappingRendering);
+}
+
static void android_view_GLES20DisplayList_setTranslationX(JNIEnv* env,
jobject clazz, DisplayList* displayList, float tx) {
displayList->setTranslationX(tx);
@@ -175,33 +185,34 @@
static JNINativeMethod gMethods[] = {
#ifdef USE_OPENGL_RENDERER
- { "nSetCaching", "(IZ)V", (void*) android_view_GLES20DisplayList_setCaching },
- { "nSetApplicationScale", "(IF)V",
- (void*) android_view_GLES20DisplayList_setApplicationScale },
- { "nSetClipChildren", "(IZ)V", (void*) android_view_GLES20DisplayList_setClipChildren },
- { "nSetAlpha", "(IF)V", (void*) android_view_GLES20DisplayList_setAlpha },
- { "nSetTranslationX", "(IF)V", (void*) android_view_GLES20DisplayList_setTranslationX },
- { "nSetTranslationY", "(IF)V", (void*) android_view_GLES20DisplayList_setTranslationY },
- { "nSetRotation", "(IF)V", (void*) android_view_GLES20DisplayList_setRotation },
- { "nSetRotationX", "(IF)V", (void*) android_view_GLES20DisplayList_setRotationX },
- { "nSetRotationY", "(IF)V", (void*) android_view_GLES20DisplayList_setRotationY },
- { "nSetScaleX", "(IF)V", (void*) android_view_GLES20DisplayList_setScaleX },
- { "nSetScaleY", "(IF)V", (void*) android_view_GLES20DisplayList_setScaleY },
- { "nSetTransformationInfo", "(IFFFFFFFF)V",
+ { "nSetCaching", "(IZ)V", (void*) android_view_GLES20DisplayList_setCaching },
+ { "nSetStaticMatrix", "(II)V", (void*) android_view_GLES20DisplayList_setStaticMatrix },
+ { "nSetAnimationMatrix", "(II)V", (void*) android_view_GLES20DisplayList_setAnimationMatrix },
+ { "nSetClipChildren", "(IZ)V", (void*) android_view_GLES20DisplayList_setClipChildren },
+ { "nSetAlpha", "(IF)V", (void*) android_view_GLES20DisplayList_setAlpha },
+ { "nSetHasOverlappingRendering", "(IZ)V",
+ (void*) android_view_GLES20DisplayList_setHasOverlappingRendering },
+ { "nSetTranslationX", "(IF)V", (void*) android_view_GLES20DisplayList_setTranslationX },
+ { "nSetTranslationY", "(IF)V", (void*) android_view_GLES20DisplayList_setTranslationY },
+ { "nSetRotation", "(IF)V", (void*) android_view_GLES20DisplayList_setRotation },
+ { "nSetRotationX", "(IF)V", (void*) android_view_GLES20DisplayList_setRotationX },
+ { "nSetRotationY", "(IF)V", (void*) android_view_GLES20DisplayList_setRotationY },
+ { "nSetScaleX", "(IF)V", (void*) android_view_GLES20DisplayList_setScaleX },
+ { "nSetScaleY", "(IF)V", (void*) android_view_GLES20DisplayList_setScaleY },
+ { "nSetTransformationInfo","(IFFFFFFFF)V",
(void*) android_view_GLES20DisplayList_setTransformationInfo },
- { "nSetPivotX", "(IF)V", (void*) android_view_GLES20DisplayList_setPivotX },
- { "nSetPivotY", "(IF)V", (void*) android_view_GLES20DisplayList_setPivotY },
- { "nSetCameraDistance", "(IF)V",
- (void*) android_view_GLES20DisplayList_setCameraDistance },
- { "nSetLeft", "(II)V", (void*) android_view_GLES20DisplayList_setLeft },
- { "nSetTop", "(II)V", (void*) android_view_GLES20DisplayList_setTop },
- { "nSetRight", "(II)V", (void*) android_view_GLES20DisplayList_setRight },
- { "nSetBottom", "(II)V", (void*) android_view_GLES20DisplayList_setBottom },
- { "nSetLeftTop", "(III)V", (void*) android_view_GLES20DisplayList_setLeftTop },
- { "nSetLeftTopRightBottom", "(IIIII)V",
+ { "nSetPivotX", "(IF)V", (void*) android_view_GLES20DisplayList_setPivotX },
+ { "nSetPivotY", "(IF)V", (void*) android_view_GLES20DisplayList_setPivotY },
+ { "nSetCameraDistance", "(IF)V", (void*) android_view_GLES20DisplayList_setCameraDistance },
+ { "nSetLeft", "(II)V", (void*) android_view_GLES20DisplayList_setLeft },
+ { "nSetTop", "(II)V", (void*) android_view_GLES20DisplayList_setTop },
+ { "nSetRight", "(II)V", (void*) android_view_GLES20DisplayList_setRight },
+ { "nSetBottom", "(II)V", (void*) android_view_GLES20DisplayList_setBottom },
+ { "nSetLeftTop", "(III)V", (void*) android_view_GLES20DisplayList_setLeftTop },
+ { "nSetLeftTopRightBottom","(IIIII)V",
(void*) android_view_GLES20DisplayList_setLeftTopRightBottom },
- { "nOffsetLeftRight", "(II)V", (void*) android_view_GLES20DisplayList_offsetLeftRight },
- { "nOffsetTopBottom", "(II)V", (void*) android_view_GLES20DisplayList_offsetTopBottom },
+ { "nOffsetLeftRight", "(II)V", (void*) android_view_GLES20DisplayList_offsetLeftRight },
+ { "nOffsetTopBottom", "(II)V", (void*) android_view_GLES20DisplayList_offsetTopBottom },
#endif
};
diff --git a/core/jni/android_view_HardwareRenderer.cpp b/core/jni/android_view_HardwareRenderer.cpp
index 082e726..fa83170 100644
--- a/core/jni/android_view_HardwareRenderer.cpp
+++ b/core/jni/android_view_HardwareRenderer.cpp
@@ -20,8 +20,9 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
+#include <EGL/egl_cache.h>
+
#ifdef USE_OPENGL_RENDERER
- #include <EGL/egl_cache.h>
EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
#endif
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
index fb62eb6..36d4a2a 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
@@ -98,7 +98,7 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:background="@null"
android:textColor="#ffffffff"
- android:imeOptions="flagForceAscii"
+ android:imeOptions="flagForceAscii|actionDone"
/>
<!-- This delete button is only visible for numeric PIN entry -->
diff --git a/core/res/res/layout-xlarge/screen_action_bar_overlay.xml b/core/res/res/layout-xlarge/screen_action_bar_overlay.xml
index 89f0d89..f2a1ea1 100644
--- a/core/res/res/layout-xlarge/screen_action_bar_overlay.xml
+++ b/core/res/res/layout-xlarge/screen_action_bar_overlay.xml
@@ -19,31 +19,39 @@
the Action Bar enabled overlaying application content.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:fitsSystemWindows="true">
+<com.android.internal.widget.ActionBarOverlayLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/action_bar_overlay_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="?android:attr/actionBarStyle"
- android:gravity="top">
- <com.android.internal.widget.ActionBarView
- android:id="@+id/action_bar"
+ <LinearLayout android:id="@+id/top_action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top">
+ <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="?android:attr/actionBarStyle" />
- <com.android.internal.widget.ActionBarContextView
- android:id="@+id/action_context_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"
- style="?android:attr/actionModeStyle" />
- </com.android.internal.widget.ActionBarContainer>
- <ImageView android:src="?android:attr/windowContentOverlay"
- android:scaleType="fitXY"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/action_bar_container" />
-</RelativeLayout>
+ android:layout_alignParentTop="true"
+ style="?android:attr/actionBarStyle"
+ android:gravity="top">
+ <com.android.internal.widget.ActionBarView
+ android:id="@+id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/actionBarStyle" />
+ <com.android.internal.widget.ActionBarContextView
+ android:id="@+id/action_context_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ style="?android:attr/actionModeStyle" />
+ </com.android.internal.widget.ActionBarContainer>
+ <ImageView android:src="?android:attr/windowContentOverlay"
+ android:scaleType="fitXY"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+</com.android.internal.widget.ActionBarOverlayLayout>
diff --git a/core/res/res/layout/date_picker_holo.xml b/core/res/res/layout/date_picker_holo.xml
index 57b5614..122a61a 100644
--- a/core/res/res/layout/date_picker_holo.xml
+++ b/core/res/res/layout/date_picker_holo.xml
@@ -41,6 +41,8 @@
android:id="@+id/month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_marginBottom="10dip"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
android:focusable="true"
@@ -52,6 +54,8 @@
android:id="@+id/day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_marginBottom="10dip"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
android:focusable="true"
@@ -63,6 +67,8 @@
android:id="@+id/year"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_marginBottom="10dip"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
android:focusable="true"
diff --git a/core/res/res/layout/notification_action.xml b/core/res/res/layout/notification_action.xml
new file mode 100644
index 0000000..54fde70
--- /dev/null
+++ b/core/res/res/layout/notification_action.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/action0"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/Widget.Holo.Button.Small"
+ android:gravity="left"
+ />
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_base.xml b/core/res/res/layout/notification_template_base.xml
new file mode 100644
index 0000000..b9710d6
--- /dev/null
+++ b/core/res/res/layout/notification_template_base.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+ <ImageView android:id="@+id/icon"
+ android:layout_width="@dimen/notification_large_icon_width"
+ android:layout_height="@dimen/notification_large_icon_height"
+ android:background="@android:drawable/notify_panel_notification_icon_bg_tile"
+ android:scaleType="center"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_vertical"
+ android:layout_marginLeft="@dimen/notification_large_icon_width"
+ android:minHeight="@dimen/notification_large_icon_height"
+ android:orientation="vertical"
+ android:paddingLeft="12dp"
+ android:paddingRight="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:gravity="center_vertical"
+ >
+ <LinearLayout
+ android:id="@+id/line1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView android:id="@+id/title"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:layout_weight="1"
+ />
+ <ViewStub android:id="@+id/time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_time"
+ />
+ <ViewStub android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_chronometer"
+ />
+ </LinearLayout>
+ <TextView android:id="@+id/text2"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Line2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-2dp"
+ android:layout_marginBottom="-2dp"
+ android:singleLine="true"
+ android:fadingEdge="horizontal"
+ android:ellipsize="marquee"
+ android:visibility="gone"
+ />
+ <TextView android:id="@+id/big_text"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="false"
+ android:visibility="gone"
+ />
+ <LinearLayout
+ android:id="@+id/line3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView android:id="@+id/text"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ />
+ <TextView android:id="@+id/info"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:singleLine="true"
+ android:gravity="center"
+ android:paddingLeft="8dp"
+ />
+ <ImageView android:id="@+id/right_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:scaleType="center"
+ android:paddingLeft="8dp"
+ android:visibility="gone"
+ android:drawableAlpha="180"
+ />
+ </LinearLayout>
+ <ProgressBar
+ android:id="@android:id/progress"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ style="?android:attr/progressBarStyleHorizontal"
+ />
+ <LinearLayout
+ android:id="@+id/actions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ >
+ <!-- actions will be added here -->
+ </LinearLayout>
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_big_picture.xml b/core/res/res/layout/notification_template_big_picture.xml
index 6eb934e..8be84bd 100644
--- a/core/res/res/layout/notification_template_big_picture.xml
+++ b/core/res/res/layout/notification_template_big_picture.xml
@@ -1,3 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
@@ -9,9 +25,9 @@
android:layout_height="192dp"
android:scaleType="centerCrop"
/>
- <include layout="@layout/status_bar_latest_event_content"
+ <include layout="@layout/notification_template_base"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_large_icon_height"
+ android:layout_height="wrap_content"
android:layout_marginTop="192dp"
/>
</FrameLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_part_chronometer.xml b/core/res/res/layout/notification_template_part_chronometer.xml
new file mode 100644
index 0000000..382b0e4
--- /dev/null
+++ b/core/res/res/layout/notification_template_part_chronometer.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<Chronometer android:id="@+id/chronometer" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:singleLine="true"
+ android:gravity="center"
+ android:paddingLeft="8dp"
+ />
diff --git a/core/res/res/layout/notification_template_part_time.xml b/core/res/res/layout/notification_template_part_time.xml
new file mode 100644
index 0000000..410fcaf
--- /dev/null
+++ b/core/res/res/layout/notification_template_part_time.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<DateTimeView android:id="@+id/time" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:singleLine="true"
+ android:gravity="center"
+ android:paddingLeft="8dp"
+ />
diff --git a/core/res/res/layout/screen_action_bar_overlay.xml b/core/res/res/layout/screen_action_bar_overlay.xml
index 2a8c7c3..20a7db1 100644
--- a/core/res/res/layout/screen_action_bar_overlay.xml
+++ b/core/res/res/layout/screen_action_bar_overlay.xml
@@ -19,14 +19,16 @@
the Action Bar enabled overlaying application content.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.internal.widget.ActionBarOverlayLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/action_bar_overlay_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true">
+ android:layout_height="match_parent">
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <LinearLayout android:layout_width="match_parent"
+ <LinearLayout android:id="@+id/top_action_bar"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top">
<com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container"
@@ -50,8 +52,7 @@
<ImageView android:src="?android:attr/windowContentOverlay"
android:scaleType="fitXY"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/action_bar_container" />
+ android:layout_height="wrap_content" />
</LinearLayout>
<com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar"
android:layout_width="match_parent"
@@ -60,4 +61,4 @@
style="?android:attr/actionBarSplitStyle"
android:visibility="gone"
android:gravity="center"/>
-</FrameLayout>
+</com.android.internal.widget.ActionBarOverlayLayout>
diff --git a/core/res/res/layout/status_bar_latest_event_content.xml b/core/res/res/layout/status_bar_latest_event_content.xml
index 57c149f..b3db01a 100644
--- a/core/res/res/layout/status_bar_latest_event_content.xml
+++ b/core/res/res/layout/status_bar_latest_event_content.xml
@@ -1,17 +1,29 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Nobody should be using this file directly. If you do, you will get a
+ purple background. Have fun with that. -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="#FFFF00FF"
>
- <ImageView android:id="@+id/icon"
- android:layout_width="@dimen/notification_large_icon_width"
- android:layout_height="@dimen/notification_large_icon_height"
- android:background="@android:drawable/notify_panel_notification_icon_bg_tile"
- android:scaleType="center"
- />
- <include layout="@layout/status_bar_latest_event_content_large_icon"
+ <include layout="@layout/notification_template_base"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center"
/>
-</LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/status_bar_latest_event_content_large_icon.xml b/core/res/res/layout/status_bar_latest_event_content_large_icon.xml
deleted file mode 100644
index 5f38e6a..0000000
--- a/core/res/res/layout/status_bar_latest_event_content_large_icon.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/status_bar_latest_event_content_large_icon"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:orientation="vertical"
- android:paddingLeft="12dp"
- android:paddingRight="12dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- >
- <LinearLayout
- android:id="@+id/line1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- >
- <TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:layout_weight="1"
- />
- <DateTimeView android:id="@+id/time"
- android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:singleLine="true"
- android:gravity="center"
- android:paddingLeft="8dp"
- />
- </LinearLayout>
- <TextView android:id="@+id/text2"
- android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Line2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="-2dp"
- android:layout_marginBottom="-2dp"
- android:singleLine="true"
- android:fadingEdge="horizontal"
- android:ellipsize="marquee"
- android:visibility="gone"
- />
- <TextView android:id="@+id/big_text"
- android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="false"
- android:visibility="gone"
- />
- <LinearLayout
- android:id="@+id/line3"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- >
- <TextView android:id="@+id/text"
- android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- />
- <TextView android:id="@+id/info"
- android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:singleLine="true"
- android:gravity="center"
- android:paddingLeft="8dp"
- />
- <ImageView android:id="@+id/right_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:scaleType="center"
- android:paddingLeft="8dp"
- android:visibility="gone"
- android:drawableAlpha="180"
- />
- </LinearLayout>
- <ProgressBar
- android:id="@android:id/progress"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"
- style="?android:attr/progressBarStyleHorizontal"
- />
-</LinearLayout>
diff --git a/core/res/res/layout/time_picker_holo.xml b/core/res/res/layout/time_picker_holo.xml
index 29c97b7..24b6194 100644
--- a/core/res/res/layout/time_picker_holo.xml
+++ b/core/res/res/layout/time_picker_holo.xml
@@ -30,6 +30,8 @@
android:id="@+id/hour"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_marginBottom="10dip"
android:layout_marginLeft="16dip"
android:layout_marginRight="14dip"
android:focusable="true"
@@ -49,6 +51,8 @@
android:id="@+id/minute"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_marginBottom="10dip"
android:layout_marginLeft="14dip"
android:layout_marginRight="16dip"
android:focusable="true"
@@ -60,6 +64,8 @@
android:id="@+id/amPm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_marginBottom="10dip"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
android:focusable="true"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 428790f..2b27585 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1506,6 +1506,14 @@
<enum name="KEYCODE_CALENDAR" value="208" />
<enum name="KEYCODE_MUSIC" value="209" />
<enum name="KEYCODE_CALCULATOR" value="210" />
+ <enum name="KEYCODE_ZENKAKU_HANKAKU" value="211" />
+ <enum name="KEYCODE_EISU" value="212" />
+ <enum name="KEYCODE_MUHENKAN" value="213" />
+ <enum name="KEYCODE_HENKAN" value="214" />
+ <enum name="KEYCODE_KATAKANA_HIRAGANA" value="215" />
+ <enum name="KEYCODE_YEN" value="216" />
+ <enum name="KEYCODE_RO" value="217" />
+ <enum name="KEYCODE_KANA" value="218" />
</attr>
<!-- ***************************************************************** -->
@@ -2071,6 +2079,7 @@
<!-- Locale -->
<enum name="locale" value="3" />
</attr>
+
<!-- Direction of the text. A heuristic is used to determine the resolved text direction
of paragraphs. -->
<attr name="textDirection" format="integer">
@@ -2091,6 +2100,29 @@
<!-- The paragraph direction is coming from the system Locale. -->
<enum name="locale" value="5" />
</attr>
+
+ <!-- Alignment of the text. A heuristic is used to determine the resolved
+ text alignment. -->
+ <attr name="textAlignment" format="integer">
+ <!-- Default -->
+ <enum name="inherit" value="0" />
+ <!-- Default for the root view. The gravity determines the alignment, ALIGN_NORMAL,
+ ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s
+ text direction -->
+ <enum name="gravity" value="1" />
+ <!-- Align to the start of the paragraph, e.g. ALIGN_NORMAL. -->
+ <enum name="textStart" value="2" />
+ <!-- Align to the end of the paragraph, e.g. ALIGN_OPPOSITE. -->
+ <enum name="textEnd" value="3" />
+ <!-- Center the paragraph, e.g. ALIGN_CENTER. -->
+ <enum name="center" value="4" />
+ <!-- Align to the start of the view, which is ALIGN_LEFT if the view’s resolved
+ layoutDirection is LTR, and ALIGN_RIGHT otherwise. -->
+ <enum name="viewStart" value="5" />
+ <!-- Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved
+ layoutDirection is LTR, and ALIGN_LEFT otherwise -->
+ <enum name="viewEnd" value="6" />
+ </attr>
</declare-styleable>
<!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 4aa7dde..1649c48 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -843,6 +843,9 @@
@hide -->
<attr name="cantSaveState" format="boolean" />
<attr name="uiOptions" />
+ <!-- Declare that your application will be able to deal with RTL (right to left) layouts.
+ If set to false (default value), your application will not care about RTL layouts. -->
+ <attr name="supportsRtl" format="boolean" />
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7b4f50b..31b444a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -34,6 +34,7 @@
<java-symbol type="id" name="account_type" />
<java-symbol type="id" name="action_bar" />
<java-symbol type="id" name="action_bar_container" />
+ <java-symbol type="id" name="action_bar_overlay_layout" />
<java-symbol type="id" name="action_bar_title" />
<java-symbol type="id" name="action_bar_subtitle" />
<java-symbol type="id" name="action_context_bar" />
@@ -183,6 +184,7 @@
<java-symbol type="id" name="to_common" />
<java-symbol type="id" name="to_org" />
<java-symbol type="id" name="to_org_unit" />
+ <java-symbol type="id" name="top_action_bar" />
<java-symbol type="id" name="topPanel" />
<java-symbol type="id" name="up" />
<java-symbol type="id" name="value" />
@@ -200,6 +202,7 @@
<java-symbol type="id" name="action2" />
<java-symbol type="id" name="big_picture" />
<java-symbol type="id" name="big_text" />
+ <java-symbol type="id" name="chronometer" />
<java-symbol type="attr" name="actionModeShareDrawable" />
<java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -419,6 +422,7 @@
<java-symbol type="string" name="date_picker_increment_year_button" />
<java-symbol type="string" name="date_time" />
<java-symbol type="string" name="date_time_set" />
+ <java-symbol type="string" name="date_time_done" />
<java-symbol type="string" name="day_of_week_long_friday" />
<java-symbol type="string" name="day_of_week_long_monday" />
<java-symbol type="string" name="day_of_week_long_saturday" />
@@ -1011,7 +1015,6 @@
<java-symbol type="layout" name="select_dialog" />
<java-symbol type="layout" name="simple_dropdown_hint" />
<java-symbol type="layout" name="status_bar_latest_event_content" />
- <java-symbol type="layout" name="status_bar_latest_event_content_large_icon" />
<java-symbol type="layout" name="status_bar_latest_event_ticker" />
<java-symbol type="layout" name="status_bar_latest_event_ticker_large_icon" />
<java-symbol type="layout" name="text_edit_action_popup_text" />
@@ -1073,8 +1076,12 @@
<java-symbol type="layout" name="zoom_container" />
<java-symbol type="layout" name="zoom_controls" />
<java-symbol type="layout" name="zoom_magnify" />
+ <java-symbol type="layout" name="notification_action" />
<java-symbol type="layout" name="notification_intruder_content" />
+ <java-symbol type="layout" name="notification_template_base" />
<java-symbol type="layout" name="notification_template_big_picture" />
+ <java-symbol type="layout" name="notification_template_part_time" />
+ <java-symbol type="layout" name="notification_template_part_chronometer" />
<java-symbol type="anim" name="slide_in_child_bottom" />
<java-symbol type="anim" name="slide_in_right" />
@@ -3547,7 +3554,10 @@
=============================================================== -->
<public type="attr" name="isolatedProcess" id="0x010103a7" />
+ <public type="attr" name="supportsRtl" id="0x010103a8" />
+
<public type="attr" name="textDirection"/>
+ <public type="attr" name="textAlignment"/>
<public type="attr" name="layoutDirection" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2848999..718de0a 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2816,6 +2816,8 @@
<string name="date_picker_dialog_title">Set date</string>
<!-- Name of the button in the date/time picker to accept the date/time change -->
<string name="date_time_set">Set</string>
+ <!-- Name of the button in the date/time picker to accept the date/time change -->
+ <string name="date_time_done">Done</string>
<!-- Security Permissions strings-->
<!-- The default permission group for any permissions that have not explicitly set a group. -->
@@ -3285,13 +3287,13 @@
<string name="content_description_sliding_handle">"Sliding handle. Touch & hold."</string>
<!-- Description of the up direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
- <string name="description_direction_up">Up for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+ <string name="description_direction_up">Slide up for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
<!-- Description of the down direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
- <string name="description_direction_down">Down for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+ <string name="description_direction_down">Slide down for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
<!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
- <string name="description_direction_left">"Left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+ <string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
<!-- Description of the right direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
- <string name="description_direction_right">Right for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+ <string name="description_direction_right">Slide right for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
<!-- Description of the unlock target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
<string name="description_target_unlock">Unlock</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 288b8b2..baeb9cc 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1643,7 +1643,7 @@
<item name="android:selectionDividerHeight">2dip</item>
<item name="android:selectionDividersDistance">48dip</item>
<item name="android:internalMinWidth">48dip</item>
- <item name="android:internalMaxHeight">200dip</item>
+ <item name="android:internalMaxHeight">180dip</item>
<item name="android:minFlingDistance">150dip</item>
</style>
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index ef38a60..5ba6bf9 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -54,7 +54,7 @@
# On space-constrained devices, we include a subset of fonts:
ifeq ($(SMALLER_FONT_FOOTPRINT),true)
droidsans_fallback_src := DroidSansFallback.ttf
-extra_droidsans_fonts := DroidSans.ttf DroidSans-Bold.ttf
+extra_font_files := DroidSans.ttf DroidSans-Bold.ttf
else
include $(CLEAR_VARS)
LOCAL_MODULE := DroidSansEthiopic-Regular.ttf
@@ -64,8 +64,29 @@
LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
include $(BUILD_PREBUILT)
+include $(CLEAR_VARS)
+LOCAL_MODULE := MTLmr3m.ttf
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := fallback_fonts-ja.xml
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+include $(BUILD_PREBUILT)
+
droidsans_fallback_src := DroidSansFallbackFull.ttf
-extra_droidsans_fonts := DroidSans.ttf DroidSans-Bold.ttf DroidSansEthiopic-Regular.ttf
+extra_font_files := \
+ DroidSans.ttf \
+ DroidSans-Bold.ttf \
+ DroidSansEthiopic-Regular.ttf \
+ MTLmr3m.ttf \
+ fallback_fonts-ja.xml
endif # SMALLER_FONT_FOOTPRINT
################################
@@ -75,13 +96,13 @@
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
-LOCAL_REQUIRED_MODULES := $(extra_droidsans_fonts)
+LOCAL_REQUIRED_MODULES := $(extra_font_files)
include $(BUILD_PREBUILT)
font_symlink_src :=
font_symlink :=
droidsans_fallback_src :=
-extra_droidsans_fonts :=
+extra_font_files :=
################################
# Build the rest font files as prebuilt.
diff --git a/data/fonts/fallback_fonts-ja.xml b/data/fonts/fallback_fonts-ja.xml
new file mode 100644
index 0000000..62491d8
--- /dev/null
+++ b/data/fonts/fallback_fonts-ja.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Fallback Fonts
+
+ This file specifies the fonts, and the priority order, that will be searched for any
+ glyphs not handled by the default fonts specified in /system/etc/system_fonts.xml.
+ Each entry consists of a family tag and a list of files (file names) which support that
+ family. The fonts for each family are listed in the order of the styles that they
+ handle (the order is: regular, bold, italic, and bold-italic). The order in which the
+ families are listed in this file represents the order in which these fallback fonts
+ will be searched for glyphs that are not supported by the default system fonts (which are
+ found in /system/etc/system_fonts.xml).
+
+ Note that there is not nameset for fallback fonts, unlike the fonts specified in
+ system_fonts.xml. The ability to support specific names in fallback fonts may be supported
+ in the future. For now, the lack of files entries here is an indicator to the system that
+ these are fallback fonts, instead of default named system fonts.
+
+ There is another optional file in /vendor/etc/fallback_fonts.xml. That file can be used to
+ provide references to other font families that should be used in addition to the default
+ fallback fonts. That file can also specify the order in which the fallback fonts should be
+ searched, to ensure that a vendor-provided font will be used before another fallback font
+ which happens to handle the same glyph.
+
+ Han languages (Chinese, Japanese, and Korean) share a common range of unicode characters;
+ their ordering in the fallback or vendor files gives priority to the first in the list.
+ Locale-specific ordering can be configured by adding language and region codes to the end
+ of the filename (e.g. /system/etc/fallback_fonts-ja.xml). When no region code is used,
+ as with this example, all regions are matched. Use separate files for each supported locale.
+ The standard fallback file (fallback_fonts.xml) is used when a locale does not have its own
+ file. All fallback files must contain the same complete set of fonts; only their ordering
+ can differ.
+-->
+<familyset>
+ <family>
+ <fileset>
+ <file>DroidSansArabic.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>DroidSansEthiopic-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>DroidSansHebrew-Regular.ttf</file>
+ <file>DroidSansHebrew-Bold.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>DroidSansThai.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>DroidSansArmenian.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>DroidSansGeorgian.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>Lohit-Devanagari.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>Lohit-Bengali.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>Lohit-Tamil.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>AndroidEmoji.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>MTLmr3m.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>DroidSansFallback.ttf</file>
+ </fileset>
+ </family>
+</familyset>
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 63b3a58..ba01947 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -88,4 +88,9 @@
<file>DroidSansFallback.ttf</file>
</fileset>
</family>
+ <family>
+ <fileset>
+ <file>MTLmr3m.ttf</file>
+ </fileset>
+ </family>
</familyset>
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 458f85b..db26765 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -15,8 +15,8 @@
# Warning: this is actually a product definition, to be inherited from
PRODUCT_COPY_FILES := \
- frameworks/base/data/fonts/system_fonts.xml:system/etc/system_fonts.xml \
- frameworks/base/data/fonts/fallback_fonts.xml:system/etc/fallback_fonts.xml
+ frameworks/base/data/fonts/system_fonts.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/system_fonts.xml \
+ frameworks/base/data/fonts/fallback_fonts.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/fallback_fonts.xml
PRODUCT_PACKAGES := \
DroidSansFallback.ttf \
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index fdd9040..768ee5e 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -104,16 +104,16 @@
key 82 NUMPAD_0
key 83 NUMPAD_DOT
# key 84 (undefined)
-# key 85 "KEY_ZENKAKUHANKAKU"
+key 85 ZENKAKU_HANKAKU
key 86 BACKSLASH
key 87 F11
key 88 F12
-# key 89 "KEY_RO"
+key 89 RO
# key 90 "KEY_KATAKANA"
# key 91 "KEY_HIRAGANA"
-# key 92 "KEY_HENKAN"
-# key 93 "KEY_KATAKANAHIRAGANA"
-# key 94 "KEY_MUHENKAN"
+key 92 HENKAN
+key 93 KATAKANA_HIRAGANA
+key 94 MUHENKAN
key 95 NUMPAD_COMMA
key 96 NUMPAD_ENTER
key 97 CTRL_RIGHT
@@ -141,9 +141,9 @@
key 119 BREAK
# key 120 (undefined)
key 121 NUMPAD_COMMA
-# key 122 "KEY_HANGEUL"
-# key 123 "KEY_HANJA"
-# key 124 "KEY_YEN"
+key 122 KANA
+key 123 EISU
+key 124 YEN
key 125 META_LEFT
key 126 META_RIGHT
key 127 MENU WAKE_DROPPED
diff --git a/docs/html/design/building-blocks/progress.jd b/docs/html/design/building-blocks/progress.jd
index dc3ded1..b188538 100644
--- a/docs/html/design/building-blocks/progress.jd
+++ b/docs/html/design/building-blocks/progress.jd
@@ -42,9 +42,9 @@
<li class="value-1"><h4>Activity bar (shown with the Holo Dark theme)</h4>
<p>
-An indeterminate activity bar is used at the start of an application download because Google Play hasn't
-been able to contact the server yet, and it's not possible to determine how long it will take for
-the download to begin.
+An indeterminate activity bar is used at the start of an application download because the Play Store
+app hasn't been able to contact the server yet, and it's not possible to determine how long it will
+take for the download to begin.
</p>
</li>
diff --git a/docs/html/design/building-blocks/tabs.jd b/docs/html/design/building-blocks/tabs.jd
index 2c854d3..19ed1c3 100644
--- a/docs/html/design/building-blocks/tabs.jd
+++ b/docs/html/design/building-blocks/tabs.jd
@@ -25,7 +25,7 @@
<source src="{@docRoot}design/media/tabs_scrolly.ogv" type="video/ogg">
</video>
<div class="figure-caption">
- Scrolling tabs in Google Play.
+ Scrolling tabs in the Play Store app.
<div class="video-instructions"> </div>
</div>
diff --git a/docs/html/design/design_toc.cs b/docs/html/design/design_toc.cs
index 19b58d9..6dd8d61 100644
--- a/docs/html/design/design_toc.cs
+++ b/docs/html/design/design_toc.cs
@@ -35,6 +35,7 @@
<li><a href="<?cs var:toroot ?>design/patterns/swipe-views.html">Swipe Views</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/selection.html">Selection</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/notifications.html">Notifications</a></li>
+ <li><a href="<?cs var:toroot ?>design/patterns/settings.html">Settings</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/compatibility.html">Compatibility</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/pure-android.html">Pure Android</a></li>
</ul>
diff --git a/docs/html/design/downloads/index.jd b/docs/html/design/downloads/index.jd
index 618c44b..67dfd79 100644
--- a/docs/html/design/downloads/index.jd
+++ b/docs/html/design/downloads/index.jd
@@ -39,7 +39,7 @@
<p>
<a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Fireworks_Stencil_20120229.png">Adobe® Fireworks® PNG Stencil</a>
<a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_OmniGraffle_Stencil_20120229.graffle">Omni® OmniGraffle® Stencil</a>
- <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Holo_Widgets_20120229.zip">Adobe® Photoshop® Sources</a>
+ <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Holo_Widgets_20120302.zip">Adobe® Photoshop® Sources</a>
</p>
</div>
diff --git a/docs/html/design/get-started/principles.jd b/docs/html/design/get-started/principles.jd
index 8f5b446..0b7147b 100644
--- a/docs/html/design/get-started/principles.jd
+++ b/docs/html/design/get-started/principles.jd
@@ -10,7 +10,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Delight me in surprising ways</h4>
+<h4 id="delight-me">Delight me in surprising ways</h4>
<p>A beautiful surface, a carefully-placed animation, or a well-timed sound effect is a joy to
experience. Subtle effects contribute to a feeling of effortlessness and a sense that a powerful
force is at hand.</p>
@@ -28,7 +28,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Real objects are more fun than buttons and menus</h4>
+<h4 id="real-objects-more-fun">Real objects are more fun than buttons and menus</h4>
<p>Allow people to directly touch and manipulate objects in your app. It reduces the cognitive effort
needed to perform a task while making it more emotionally satisfying.</p>
@@ -45,7 +45,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Let me make it mine</h4>
+<h4 id="make-it-mine">Let me make it mine</h4>
<p>People love to add personal touches because it helps them feel at home and in control. Provide
sensible, beautiful defaults, but also consider fun, optional customizations that don't hinder
primary tasks.</p>
@@ -63,7 +63,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Get to know me</h4>
+<h4 id="get-to-know-me">Get to know me</h4>
<p>Learn peoples' preferences over time. Rather than asking them to make the same choices over and
over, place previous choices within easy reach.</p>
@@ -80,7 +80,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Keep it brief</h4>
+<h4 id="keep-it-brief">Keep it brief</h4>
<p>Use short phrases with simple words. People are likely to skip sentences if they're long.</p>
</div>
@@ -96,7 +96,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Pictures are faster than words</h4>
+<h4 id="pictures-faster-than-words">Pictures are faster than words</h4>
<p>Consider using pictures to explain ideas. They get people's attention and can be much more efficient
than words.</p>
@@ -113,7 +113,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Decide for me but let me have the final say</h4>
+<h4 id="decide-for-me">Decide for me but let me have the final say</h4>
<p>Take your best guess and act rather than asking first. Too many choices and decisions make people
unhappy. Just in case you get it wrong, allow for 'undo'.</p>
@@ -130,7 +130,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Only show what I need when I need it</h4>
+<h4 id="only-show-when-i-need-it">Only show what I need when I need it</h4>
<p>People get overwhelmed when they see too much at once. Break tasks and information into small,
digestible chunks. Hide options that aren't essential at the moment, and teach people as they go.</p>
@@ -147,7 +147,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>I should always know where I am</h4>
+<h4 id="always-know-where-i-am">I should always know where I am</h4>
<p>Give people confidence that they know their way around. Make places in your app look distinct and
use transitions to show relationships among screens. Provide feedback on tasks in progress.</p>
@@ -164,7 +164,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Never lose my stuff</h4>
+<h4 id="never-lose-my-stuff">Never lose my stuff</h4>
<p>Save what people took time to create and let them access it from anywhere. Remember settings,
personal touches, and creations across phones, tablets, and computers. It makes upgrading the
easiest thing in the world.</p>
@@ -182,7 +182,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>If it looks the same, it should act the same</h4>
+<h4 id="looks-same-should-act-same">If it looks the same, it should act the same</h4>
<p>Help people discern functional differences by making them visually distinct rather than subtle.
Avoid modes, which are places that look similar but act differently on the same input.</p>
@@ -199,7 +199,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Only interrupt me if it's important</h4>
+<h4 id="interrupt-only-if-important">Only interrupt me if it's important</h4>
<p>Like a good personal assistant, shield people from unimportant minutiae. People want to stay
focused, and unless it's critical and time-sensitive, an interruption can be taxing and frustrating.</p>
@@ -216,7 +216,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Give me tricks that work everywhere</h4>
+<h4 id="give-me-tricks">Give me tricks that work everywhere</h4>
<p>People feel great when they figure things out for themselves. Make your app easier to learn by
leveraging visual patterns and muscle memory from other Android apps. For example, the swipe gesture
may be a good navigational shortcut.</p>
@@ -234,7 +234,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>It's not my fault</h4>
+<h4 id="its-not-my-fault">It's not my fault</h4>
<p>Be gentle in how you prompt people to make corrections. They want to feel smart when they use your
app. If something goes wrong, give clear recovery instructions but spare them the technical details.
If you can fix it behind the scenes, even better.</p>
@@ -252,7 +252,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Sprinkle encouragement</h4>
+<h4 id="sprinkle-encouragement">Sprinkle encouragement</h4>
<p>Break complex tasks into smaller steps that can be easily accomplished. Give feedback on actions,
even if it's just a subtle glow.</p>
@@ -269,7 +269,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Do the heavy lifting for me</h4>
+<h4 id="do-heavy-lifting-for-me">Do the heavy lifting for me</h4>
<p>Make novices feel like experts by enabling them to do things they never thought they could. For
example, shortcuts that combine multiple photo effects can make amateur photographs look amazing in
only a few steps.</p>
@@ -287,7 +287,7 @@
<div class="layout-content-row">
<div class="layout-content-col span-7">
-<h4>Make important things fast</h4>
+<h4 id="make-important-things-fast">Make important things fast</h4>
<p>Not all actions are equal. Decide what's most important in your app and make it easy to find and
fast to use, like the shutter button in a camera, or the pause button in a music player.</p>
diff --git a/docs/html/design/media/app_structure_market.png b/docs/html/design/media/app_structure_market.png
index 3b0b786..5aa595e 100644
--- a/docs/html/design/media/app_structure_market.png
+++ b/docs/html/design/media/app_structure_market.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_music_lndscp.png b/docs/html/design/media/app_structure_music_lndscp.png
index 0dd400c..67354de 100644
--- a/docs/html/design/media/app_structure_music_lndscp.png
+++ b/docs/html/design/media/app_structure_music_lndscp.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_scrolltabs.png b/docs/html/design/media/app_structure_scrolltabs.png
index ef4fca4..ea742c2 100644
--- a/docs/html/design/media/app_structure_scrolltabs.png
+++ b/docs/html/design/media/app_structure_scrolltabs.png
Binary files differ
diff --git a/docs/html/design/media/app_structure_shortcut_on_item.png b/docs/html/design/media/app_structure_shortcut_on_item.png
index 3874e1d4..1341f1f 100644
--- a/docs/html/design/media/app_structure_shortcut_on_item.png
+++ b/docs/html/design/media/app_structure_shortcut_on_item.png
Binary files differ
diff --git a/docs/html/design/media/iconography_launcher_example.png b/docs/html/design/media/iconography_launcher_example.png
index a5db53e..0cce7ef9 100644
--- a/docs/html/design/media/iconography_launcher_example.png
+++ b/docs/html/design/media/iconography_launcher_example.png
Binary files differ
diff --git a/docs/html/design/media/iconography_overview.png b/docs/html/design/media/iconography_overview.png
index 688c1b5..56cd409 100644
--- a/docs/html/design/media/iconography_overview.png
+++ b/docs/html/design/media/iconography_overview.png
Binary files differ
diff --git a/docs/html/design/media/migrating_intents.png b/docs/html/design/media/migrating_intents.png
new file mode 100644
index 0000000..65fc1a5
--- /dev/null
+++ b/docs/html/design/media/migrating_intents.png
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png b/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png
deleted file mode 100644
index bab6aca..0000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_land_span13.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png b/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png
deleted file mode 100644
index bdccc2f..0000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_port_span5.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png b/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png
deleted file mode 100644
index 5e0135b..0000000
--- a/docs/html/design/media/misc_full_galaxynexus_blank_port_span9.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_back.png b/docs/html/design/media/navigation_between_apps_back.png
new file mode 100755
index 0000000..ded5d0a
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_back.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_inward.png b/docs/html/design/media/navigation_between_apps_inward.png
new file mode 100755
index 0000000..1f5e401
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_inward.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_apps_up.png b/docs/html/design/media/navigation_between_apps_up.png
new file mode 100755
index 0000000..f192c88
--- /dev/null
+++ b/docs/html/design/media/navigation_between_apps_up.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_siblings_market1.png b/docs/html/design/media/navigation_between_siblings_market1.png
old mode 100644
new mode 100755
index c3148f8..8f2b3dc
--- a/docs/html/design/media/navigation_between_siblings_market1.png
+++ b/docs/html/design/media/navigation_between_siblings_market1.png
Binary files differ
diff --git a/docs/html/design/media/navigation_between_siblings_market2.png b/docs/html/design/media/navigation_between_siblings_market2.png
old mode 100644
new mode 100755
index 208be47..33b654c
--- a/docs/html/design/media/navigation_between_siblings_market2.png
+++ b/docs/html/design/media/navigation_between_siblings_market2.png
Binary files differ
diff --git a/docs/html/design/media/navigation_from_outside_up.png b/docs/html/design/media/navigation_from_outside_up.png
deleted file mode 100644
index eaa3cdb..0000000
--- a/docs/html/design/media/navigation_from_outside_up.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/media/navigation_indirect_notification.png b/docs/html/design/media/navigation_indirect_notification.png
new file mode 100644
index 0000000..6f99267
--- /dev/null
+++ b/docs/html/design/media/navigation_indirect_notification.png
Binary files differ
diff --git a/docs/html/design/media/navigation_popup_notification.png b/docs/html/design/media/navigation_popup_notification.png
new file mode 100644
index 0000000..a0a3ee7
--- /dev/null
+++ b/docs/html/design/media/navigation_popup_notification.png
Binary files differ
diff --git a/docs/html/design/media/navigation_up_vs_back_gmail.png b/docs/html/design/media/navigation_up_vs_back_gmail.png
index 71e6484..ff7adfe 100644
--- a/docs/html/design/media/navigation_up_vs_back_gmail.png
+++ b/docs/html/design/media/navigation_up_vs_back_gmail.png
Binary files differ
diff --git a/docs/html/design/media/navigation_with_back_and_up.png b/docs/html/design/media/navigation_with_back_and_up.png
index 4fb6dce..5440220 100644
--- a/docs/html/design/media/navigation_with_back_and_up.png
+++ b/docs/html/design/media/navigation_with_back_and_up.png
Binary files differ
diff --git a/docs/html/design/media/progress_activity.png b/docs/html/design/media/progress_activity.png
index 51210b4..32cf1f5 100644
--- a/docs/html/design/media/progress_activity.png
+++ b/docs/html/design/media/progress_activity.png
Binary files differ
diff --git a/docs/html/design/media/progress_download.png b/docs/html/design/media/progress_download.png
index f567f74..ab6bf58 100644
--- a/docs/html/design/media/progress_download.png
+++ b/docs/html/design/media/progress_download.png
Binary files differ
diff --git a/docs/html/design/media/settings_checkbox.png b/docs/html/design/media/settings_checkbox.png
new file mode 100644
index 0000000..6615bfb
--- /dev/null
+++ b/docs/html/design/media/settings_checkbox.png
Binary files differ
diff --git a/docs/html/design/media/settings_date_time.png b/docs/html/design/media/settings_date_time.png
new file mode 100644
index 0000000..8df92d4
--- /dev/null
+++ b/docs/html/design/media/settings_date_time.png
Binary files differ
diff --git a/docs/html/design/media/settings_dependency.png b/docs/html/design/media/settings_dependency.png
new file mode 100644
index 0000000..4821c61
--- /dev/null
+++ b/docs/html/design/media/settings_dependency.png
Binary files differ
diff --git a/docs/html/design/media/settings_flowchart.png b/docs/html/design/media/settings_flowchart.png
new file mode 100644
index 0000000..7e8623c
--- /dev/null
+++ b/docs/html/design/media/settings_flowchart.png
Binary files differ
diff --git a/docs/html/design/media/settings_grouping.png b/docs/html/design/media/settings_grouping.png
new file mode 100644
index 0000000..d271ea8
--- /dev/null
+++ b/docs/html/design/media/settings_grouping.png
Binary files differ
diff --git a/docs/html/design/media/settings_individual_on_off.png b/docs/html/design/media/settings_individual_on_off.png
new file mode 100644
index 0000000..03bea0b
--- /dev/null
+++ b/docs/html/design/media/settings_individual_on_off.png
Binary files differ
diff --git a/docs/html/design/media/settings_list_subscreen.png b/docs/html/design/media/settings_list_subscreen.png
new file mode 100644
index 0000000..385aa6a
--- /dev/null
+++ b/docs/html/design/media/settings_list_subscreen.png
Binary files differ
diff --git a/docs/html/design/media/settings_main.png b/docs/html/design/media/settings_main.png
new file mode 100644
index 0000000..f42a358
--- /dev/null
+++ b/docs/html/design/media/settings_main.png
Binary files differ
diff --git a/docs/html/design/media/settings_master_on_off.png b/docs/html/design/media/settings_master_on_off.png
new file mode 100644
index 0000000..e46bb97
--- /dev/null
+++ b/docs/html/design/media/settings_master_on_off.png
Binary files differ
diff --git a/docs/html/design/media/settings_master_on_off_2.png b/docs/html/design/media/settings_master_on_off_2.png
new file mode 100644
index 0000000..ab4e992
--- /dev/null
+++ b/docs/html/design/media/settings_master_on_off_2.png
Binary files differ
diff --git a/docs/html/design/media/settings_multiple_choice.png b/docs/html/design/media/settings_multiple_choice.png
new file mode 100644
index 0000000..9b28566
--- /dev/null
+++ b/docs/html/design/media/settings_multiple_choice.png
Binary files differ
diff --git a/docs/html/design/media/settings_overflow.png b/docs/html/design/media/settings_overflow.png
new file mode 100644
index 0000000..9000bec
--- /dev/null
+++ b/docs/html/design/media/settings_overflow.png
Binary files differ
diff --git a/docs/html/design/media/settings_slider.png b/docs/html/design/media/settings_slider.png
new file mode 100644
index 0000000..51545c8
--- /dev/null
+++ b/docs/html/design/media/settings_slider.png
Binary files differ
diff --git a/docs/html/design/media/settings_subscreen_navigation.png b/docs/html/design/media/settings_subscreen_navigation.png
new file mode 100644
index 0000000..2ab0b96
--- /dev/null
+++ b/docs/html/design/media/settings_subscreen_navigation.png
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.mp4 b/docs/html/design/media/tabs_scrolly.mp4
index 4329243..dc9e9c6 100644
--- a/docs/html/design/media/tabs_scrolly.mp4
+++ b/docs/html/design/media/tabs_scrolly.mp4
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.ogv b/docs/html/design/media/tabs_scrolly.ogv
index 345e57a..3e484f9 100644
--- a/docs/html/design/media/tabs_scrolly.ogv
+++ b/docs/html/design/media/tabs_scrolly.ogv
Binary files differ
diff --git a/docs/html/design/media/tabs_scrolly.webm b/docs/html/design/media/tabs_scrolly.webm
index 17e368e..e9d371d 100644
--- a/docs/html/design/media/tabs_scrolly.webm
+++ b/docs/html/design/media/tabs_scrolly.webm
Binary files differ
diff --git a/docs/html/design/media/ui_overview_all_apps.png b/docs/html/design/media/ui_overview_all_apps.png
index 467f5ad..17e7ece 100644
--- a/docs/html/design/media/ui_overview_all_apps.png
+++ b/docs/html/design/media/ui_overview_all_apps.png
Binary files differ
diff --git a/docs/html/design/patterns/actionbar.jd b/docs/html/design/patterns/actionbar.jd
index 9e3f48c..2226fec 100644
--- a/docs/html/design/patterns/actionbar.jd
+++ b/docs/html/design/patterns/actionbar.jd
@@ -176,7 +176,7 @@
<source src="{@docRoot}design/media/tabs_scrolly.ogv" type="video/ogg">
</video>
<div class="figure-caption">
- Scrolling tabs in Google Play.
+ Scrolling tabs in the Play Store app.
<div class="video-instructions"> </div>
</div>
diff --git a/docs/html/design/patterns/app-structure.jd b/docs/html/design/patterns/app-structure.jd
index b54b12f..e2398ed 100644
--- a/docs/html/design/patterns/app-structure.jd
+++ b/docs/html/design/patterns/app-structure.jd
@@ -7,7 +7,7 @@
single screen</li>
<li>Apps such as Phone whose main purpose is to switch between different activities without deeper
navigation</li>
-<li>Apps such as Gmail or Google Play that combine a broad set of data views with deep navigation</li>
+<li>Apps such as Gmail or the Play Store that combine a broad set of data views with deep navigation</li>
</ul>
<p>Your app's structure depends largely on the content and tasks you want to surface for your users.</p>
<h2 id="general-structure">General Structure</h2>
@@ -60,7 +60,7 @@
<img src="{@docRoot}design/media/app_structure_market.png">
<div class="figure-caption">
- The Google Play app's start screen primarily allows navigation into the stores for Apps, Music, Books,
+ The Play Store app's start screen primarily allows navigation into the stores for Apps, Music, Books,
Movies and Games. It is also enriched with tailored recommendations and promotions that
surface content of interest to the user. Search is readily available from the action bar.
</div>
@@ -145,8 +145,8 @@
<img src="{@docRoot}design/media/app_structure_scrolltabs.png">
<div class="figure-caption">
- Google Play uses tabs to simultaneously show category choice and content. To navigate between
- categories, users can swipe left/right on the content.
+ The Play Store app uses tabs to simultaneously show category choice and content. To navigate
+ between categories, users can swipe left/right on the content.
</div>
</div>
diff --git a/docs/html/design/patterns/navigation.jd b/docs/html/design/patterns/navigation.jd
index d35cd82..7e288ae 100644
--- a/docs/html/design/patterns/navigation.jd
+++ b/docs/html/design/patterns/navigation.jd
@@ -13,32 +13,36 @@
<h2 id="up-vs-back">Up vs. Back</h2>
-<p>The Up button is used to navigate within an application based on the hierarchical relationships
+<p>The Up button is used to navigate within an app based on the hierarchical relationships
between screens. For instance, if screen A displays a list of items, and selecting an item leads to
screen B (which presents that item in more detail), then screen B should offer an Up button that
returns to screen A.</p>
-<p>If a screen is the topmost one in an app (i.e. the home of the app), it should not present an Up
+<p>If a screen is the topmost one in an app (that is, the app's home), it should not present an Up
button.</p>
-<p>The system Back key is used to navigate based on the history of screens the user has recently seen,
-in reverse chronological order—in effect, the temporal relationships between screens.</p>
+
+<p>The system Back button is used to navigate, in reverse chronological order, through the history
+of screens the user has recently worked with. It is generally based on the temporal relationships
+between screens, rather than the app's hierarchy.</p>
+
<p>When the previously viewed screen is also the hierarchical parent of the current screen, pressing
-the Back key will have the same result as pressing an Up button -- this is a common occurrence.
-However, unlike the Up button, which ensures the user remains within your app, the Back key can
-return the user to the Home screen, or even to a different application.</p>
+the Back button has the same result as pressing an Up button—this is a common
+occurrence. However, unlike the Up button, which ensures the user remains within your app, the Back
+button can return the user to the Home screen, or even to a different app.</p>
<img src="{@docRoot}design/media/navigation_up_vs_back_gmail.png">
-<p>The Back key also supports a few behaviors not directly tied to screen-to-screen navigation:</p>
+<p>The Back button also supports a few behaviors not directly tied to screen-to-screen navigation:
+</p>
<ul>
-<li>Back dismisses floating windows (dialogs, popups)</li>
-<li>Back dismisses contextual action bars, and removes the highlight from the selected items</li>
-<li>Back hides the onscreen keyboard (IME)</li>
+<li>Dismisses floating windows (dialogs, popups)</li>
+<li>Dismisses contextual action bars, and removes the highlight from the selected items</li>
+<li>Hides the onscreen keyboard (IME)</li>
</ul>
<h2 id="within-app">Navigation Within Your App</h2>
<h4>Navigating to screens with multiple entry points</h4>
<p>Sometimes a screen doesn't have a strict position within the app's hierarchy, and can be reached
-from multiple entry points—e.g., a settings screen which can be navigated to from any screen
+from multiple entry points—such as a settings screen that can be reached from any other screen
in your app. In this case, the Up button should choose to return to the referring screen, behaving
identically to Back.</p>
<h4>Changing view within a screen</h4>
@@ -50,7 +54,7 @@
<li>Switching views using a dropdown (aka collapsed tabs)</li>
<li>Filtering a list</li>
<li>Sorting a list</li>
-<li>Changing display characteristics (e.g. zooming)</li>
+<li>Changing display characteristics (such as zooming)</li>
</ul>
<h4>Navigating between sibling screens</h4>
<p>When your app supports navigation from a list of items to a detail view of one of those items, it's
@@ -61,56 +65,140 @@
<img src="{@docRoot}design/media/navigation_between_siblings_gmail.png">
-<p>However, a notable exception to this occurs when browsing between "related" detail views not tied
-together by the referring list—for example, when browsing on Google Play between apps from
+<p>However, a notable exception to this occurs when browsing between related detail views not tied
+together by the referring list—for example, when browsing in the Play Store between apps from
the same developer, or albums by the same artist. In these cases, following each link does create
-history, causing the Back button to step through each screen of related content which has been
-viewed. Up should continue to bypass these related screens and navigate to the most recently viewed
-container screen.</p>
+history, causing the Back button to step through each previously viewed screen. Up should continue
+to bypass these related screens and navigate to the most recently viewed container screen.</p>
<img src="{@docRoot}design/media/navigation_between_siblings_market1.png">
<p>You have the ability to make the Up behavior even smarter based on your knowledge of detail
-view. If we extend our Google Play sample from above, imagine the user has navigated from the last Book
-viewed to the details for the Movie adaptation. In that case, Up can return to a container (Movies)
-which the user had not previously navigated through.</p>
+view. Extending the Play Store example from above, imagine the user has navigated from the last
+Book viewed to the details for the Movie adaptation. In that case, Up can return to a container
+(Movies) which the user hasn't previously navigated through.</p>
<img src="{@docRoot}design/media/navigation_between_siblings_market2.png">
-<h2 id="from-outside">Navigation From Outside Your App</h2>
+<h2 id="into-your-app">Navigation into Your App via Home Screen Widgets and Notifications</h2>
-<p>There are two categories of navigation from outside your app to screens deep within the app's
-hierarchy:</p>
+<p>You can use Home screen widgets or notifications to help your users navigate directly to screens
+deep within your app's hierarchy. For example, Gmail's Inbox widget and new message notification can
+both bypass the Inbox screen, taking the user directly to a conversation view.</p>
+
+<p>For both of these cases, handle the Up button as follows:</p>
+
<ul>
-<li>App-to-app navigation, such as via intent completion.</li>
-<li>System-to-app navigation, such as via notifications and home screen widgets.</li>
+<li><em>If the destination screen is typically reached from one particular screen within your
+app</em>, Up should navigate to that screen.</li>
+<li><em>Otherwise</em>, Up should navigate to the topmost ("Home") screen of your app.</li>
</ul>
-<p>Gmail provides examples of each of these. For app-to-app navigation, a "Share" intent goes directly
-to the compose screen. For system-to-app navigation, both a new message notification and a home
-screen widget can bypass the Inbox screen, taking the user directly to a conversation view.</p>
-<h4>App-to-app navigation</h4>
-<p>When navigating deep into your app's hierarchy directly from another app via an intent, Back will
-return to the referring app.</p>
-<p>The Up button is handled as follows:
-- If the destination screen is typically reached from one particular screen within your app, Up
- should navigate to that screen.
-- Otherwise, Up should navigate to the topmost ("Home") screen of your app.</p>
-<p>For example, after choosing to share a book being viewed on Google Play, the user navigates directly to
-Gmail's compose screen. From there, Up returns to the Inbox (which happens to be both the
-typical referrer to compose, as well as the topmost screen of the app), while Back returns to
-Google Play.</p>
-<img src="{@docRoot}design/media/navigation_from_outside_up.png">
+<p>In the case of the Back button, you should make navigation more predictable by inserting into the
+task's back stack the complete upward navigation path to the app's topmost screen. This allows users
+who've forgotten how they entered your app to navigate to the app's topmost screen before
+exiting.</p>
-<h4>System-to-app navigation</h4>
-<p>If your app was reached via the system mechanisms of notifications or home screen widgets, Up
-behaves as described for app-to-app navigation, above.</p>
-<p>For the Back key, you should make navigation more predictably by inserting into the task's back
-stack the complete upward navigation path to the app's topmost screen. This way, a user who has
-forgotten how they entered your app can safely navigate to the app's topmost screen before exiting
-it.</p>
-<p>For example, Gmail's Home screen widget has a button for diving directly to its compose screen.
-After following that path, the Back key first returns to the Inbox, and from there continues to
-Home.</p>
+<p>As an example, Gmail's Home screen widget has a button for diving directly to its compose
+screen. Up or Back from the compose screen would take the user to the Inbox, and from there the
+Back button continues to Home.</p>
<img src="{@docRoot}design/media/navigation_from_outside_back.png">
+
+<h4>Indirect notifications</h4>
+
+<p>When your app needs to present information about multiple events simultaneously, it can use a
+single notification that directs the user to an interstitial screen. This screen summarizes these
+events, and provides paths for the user to dive deeply into the app. Notifications of this style are
+called <em>indirect notifications</em>.</p>
+
+<p>Unlike standard (direct) notifications, pressing Back from an indirect notification's
+interstitial screen returns the user to the point the notification was triggered from—no
+additional screens are inserted into the back stack. Once the user proceeds into the app from its
+interstitial screen, Up and Back behave as for standard notifications, as described above:
+navigating within the app rather than returning to the interstitial.</p>
+
+<p>For example, suppose a user in Gmail receives an indirect notification from Calendar. Touching
+this notification opens the interstitial screen, which displays reminders for several different
+events. Touching Back from the interstitial returns the user to Gmail. Touching on a particular
+event takes the user away from the interstitial and into the full Calendar app to display details of
+the event. From the event details, Up and Back navigate to the top-level view of Calendar.</p>
+
+<img src="{@docRoot}design/media/navigation_indirect_notification.png">
+
+<h4>Pop-up notifications</h4>
+
+<p><em>Pop-up notifications</em> bypass the notification drawer, instead appearing directly in
+front of the user. They are rarely used, and <strong>should be reserved for occasions where a timely
+response is required and the interruption of the user's context is necessary</strong>. For example,
+Talk uses this style to alert the user of an invitation from a friend to join a video chat, as this
+invitation will automatically expire after a few seconds.</p>
+
+<p>In terms of navigation behavior, pop-up notifications closely follow the behavior of an indirect
+notification's interstitial screen. Back dismisses the pop-up notification. If the user navigates
+from the pop-up into the notifying app, Up and Back follow the rules for standard notifications,
+navigating within the app.</p>
+
+<img src="{@docRoot}design/media/navigation_popup_notification.png">
+
+<h2 id="between-apps">Navigation Between Apps</h2>
+
+<p>One of the fundamental strengths of the Android system is the ability for apps to activate each
+other, giving the user the ability to navigate directly from one app into another. For example, an
+app that needs to capture a photo can activate the Camera app, which will return the photo
+to the referring app. This is a tremendous benefit to both the developer, who can easily leverage
+code from other apps, and the user, who enjoys a consistent experience for commonly performed
+actions.</p>
+
+<p>To understand app-to-app navigation, it's important to understand the Android framework behavior
+discussed below.</p>
+
+<h4>Activities, tasks, and intents</h4>
+
+<p>In Android, an <strong>activity</strong> is an application component that defines a screen of
+information and all of the associated actions the user can perform. Your app is a collection of
+activities, consisting of both the activities you create and those you re-use from other apps.</p>
+
+<p>A <strong>task</strong> is the sequence of activities a user follows to accomplish a goal. A
+single task can make use of activities from just one app, or may draw on activities from a number
+of different apps.</p>
+
+<p>An <strong>intent</strong> is a mechanism for one app to signal it would like another
+app's assistance in performing an action. An app's activities can indicate which intents
+they can respond to. For common intents such as "Share", the user may have many apps installed
+that can fulfill that request.</p>
+
+<h4>Example: navigating between apps to support sharing</h4>
+
+<p>To understand how activities, tasks, and intents work together, consider how one app allows users
+to share content by using another app. For example, launching the Play Store app from Home begins
+new Task A (see figure below). After navigating through the Play Store and touching a promoted book
+to see its details, the user remains in the same task, extending it by adding activities. Triggering
+the Share action prompts the user with a dialog listing each of the activities (from different apps)
+which have registered to handle the Share intent.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_inward.png">
+
+<p>When the user elects to share via Gmail, Gmail's compose activity is added as a continuation of
+Task A—no new task is created. If Gmail had its own task running in the background, it would
+be unaffected.</p>
+
+<p>From the compose activity, sending the message or touching the Back button returns the user to
+the book details activity. Subsequent touches of Back continue to navigate back through the Play
+Store, ultimately arriving at Home.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_back.png">
+
+<p>However, by touching Up from the compose activity, the user indicates a desire to remain within
+Gmail. Gmail's conversation list activity appears, and a new Task B is created for it. New tasks are
+always rooted to Home, so touching Back from the conversation list returns there.</p>
+
+<img src="{@docRoot}design/media/navigation_between_apps_up.png">
+
+<p>Task A persists in the background, and the user may return to it later (for example, via the
+Recents screen). If Gmail already had its own task running in the background, it would be replaced
+with Task B—the prior context is abandoned in favor of the user's new goal.</p>
+
+<p>When your app registers to handle intents with an activity deep within the app's hierarchy,
+refer to <a href="#into-your-app">Navigation into Your App via Home Screen Widgets and
+Notifications</a> for guidance on how to specify Up navigation.</p>
diff --git a/docs/html/design/patterns/pure-android.jd b/docs/html/design/patterns/pure-android.jd
index 8ed1347..77813c0 100644
--- a/docs/html/design/patterns/pure-android.jd
+++ b/docs/html/design/patterns/pure-android.jd
@@ -48,7 +48,8 @@
document or deleting.</p>
<p>As you are migrating your app to Android, please swap out platform-specific icons with their Android
counterparts.</p>
-<p>You can find a wide variety of icons for use in your app in the Android SDK.</p>
+<p>You can find a wide variety of icons for use in your app on the
+<a href="{@docRoot}design/downloads/index.html">Downloads</a> page.</p>
</div>
<div class="layout-content-col span-8">
@@ -89,6 +90,33 @@
<div class="layout-content-row">
<div class="layout-content-col span-5">
+<h4>Don't hardcode links to other apps</h4>
+<p>In some cases you might want your app to take advantage of another app's feature set. For
+example, you may want to share the content that your app created via a social network or messaging
+app, or view the content of a weblink in a browser. Don't use hard-coded, explicit links to
+particular apps to achieve this. Instead, use Android's intent API to launch an activity chooser
+which lists all applications that are set up to handle the particular request. This lets the user
+complete the task with their preferred app. For sharing in particular, consider using the <em>Share
+Action Provider</em> in your action bar to provide faster access to the user's most recently used
+sharing target.</p>
+
+ </div>
+ <div class="layout-content-col span-8">
+
+ <img src="{@docRoot}design/media/migrating_intents.png">
+ <div class="figure-caption">
+ Link to other apps with the activity chooser or use the <em>Share Action Provider</em> in the
+ action bar.
+ </div>
+
+ </div>
+</div>
+
+<div class="vspace size-2"> </div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-5">
+
<h4>Don't use labeled back buttons on action bars</h4>
<p>Other platforms use an explicit back button with label to allow the user to navigate up the
application's hierarchy. Instead, Android uses the main action bar's app icon for hierarchical
diff --git a/docs/html/design/patterns/settings.jd b/docs/html/design/patterns/settings.jd
new file mode 100644
index 0000000..3b28b84
--- /dev/null
+++ b/docs/html/design/patterns/settings.jd
@@ -0,0 +1,689 @@
+page.title=Settings
+@jd:body
+
+<p>Settings is a place in your app where users indicate their preferences for how your app should
+behave. This benefits users because:</p>
+
+<ul>
+<li>You don't need to interrupt them with the same questions over and over when certain situations
+arise. The settings predetermine what will always happen in those situations (see design
+principle: <a href="{@docRoot}design/get-started/principles.html#decide-for-me">Decide for me but
+let me have the final say</a>).</li>
+<li>You help them feel at home and in control (see design principle:
+<a href="{@docRoot}design/get-started/principles.html#make-it-mine">Let me make it mine</a>).</li>
+</ul>
+
+<h2 id="flow-structure">Flow and Structure</h2>
+
+<h4 id="settings-access">Provide access to Settings in the action overflow</h4>
+
+<p>Settings is given low prominence in the UI because it's not frequently needed. Even if there's
+room in the <a href="{@docRoot}design/patterns/actionbar.html">action bar</a>, never make Settings
+an action button. Always keep it in the action overflow and label it "Settings". Place it below
+all other items except "Help".</p>
+
+<img src="{@docRoot}design/media/settings_overflow.png">
+
+<div class="vspace size-2"> </div>
+
+<h4 id="what-to-make-a-setting">Avoid the temptation to make everything a setting</h4>
+
+<p>Because Settings is a few navigational steps away, no matter how many items you have, they'll
+never clutter up the core part of your UI. This may seem like good news, but it also poses a
+challenge.</p>
+
+<p>Settings can be a tempting place to keep a lot of stuff—like a hall closet where things
+get stashed when you tidy up before company comes over. It's not a place where you spend lots of
+time, so it's easy to rationalize and ignore its cluttered condition. But when users visit
+Settings—however infrequently—they'll have the same expectations for the experience as
+they do everywhere else in your app. More settings means more choices to make, and too many are
+overwhelming.</p>
+
+<p>So don't punt on the difficult product decisions and debates that can bring on the urge to
+"just make it a setting". For each control you're considering adding to Settings, make sure it
+meets the bar:</p>
+
+<img src="{@docRoot}design/media/settings_flowchart.png">
+
+<div class="vspace size-3"> </div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-5 with-callouts">
+
+<h4 id="group-settings">If you still have lots of settings, group related settings together</h4>
+
+<p>The number of items an average human can hold in short-term memory is 7±2. If you
+present a list of 10 or more settings (even after applying the criteria above), users will have
+more difficulty scanning, comprehending, and processing them.</p>
+
+<p>You can remedy this by dividing some or all of the settings into groups, effectively turning
+one long list into multiple shorter lists. A group of related settings can be presented in one of
+two ways:</p>
+
+<ol>
+<li><h4>Under a section divider</h4></li>
+<li><h4>In a separate subscreen</h4></li>
+</ol>
+
+<p>You can use one or both these grouping techniques to organize your app's settings.</p>
+
+<p>For example, in the main screen of the Android Settings app, each item in the list navigates
+to a subscreen of related settings. In addition, the items themselves are grouped under section
+dividers.</p>
+
+ </div>
+ <div class="layout-content-col span-8">
+
+ <img src="{@docRoot}design/media/settings_grouping.png">
+
+ </div>
+</div>
+
+<p>Grouping settings is not an exact science, but here's some advice for how to approach it, based
+on the total number of settings in your app.</p>
+
+<div class="vspace size-1"> </div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>7 or fewer</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>Don't group them at all. It won't benefit users and will seem like overkill.</p>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>8 to 10</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>Try grouping related settings under 1 or 2 section dividers. If you have any "singletons"
+(settings that don't relate to any other settings and can't be grouped under your section
+dividers), treat them as follows:</p>
+
+<ul>
+<li>If they include some of your most important settings, list them at the top without a section
+divider.</li>
+<li>Otherwise, list them at the bottom with a section divider called "OTHER", in order of
+importance.</li>
+</ul>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>11 to 15</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>Same advice as above, but try 2 to 4 section dividers.</p>
+
+<p>Also, try the following to reduce the list:</p>
+
+<ul>
+<li>If 2 or more of the settings are mainly for power users, move them out of your main Settings
+screen and into an "Advanced" subscreen. Place an item in the action overflow called "Advanced" to
+navigate to it.</li>
+<li>Look for "doubles": two settings that relate to one another, but not to any other settings.
+Try to combine them into one setting, using the design patterns described later in this section.
+For example, you might be able to redesign two related checkbox settings into one multiple choice
+setting.</li>
+</ul>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-2">
+
+<h4>16 or more</h4>
+
+ </div>
+ <div class="layout-content-col span-11">
+
+<p>If you have any instances of 4 or more related settings, group them under a subscreen. Then use
+the advice suggested above for the reduced list size.</p>
+
+ </div>
+</div>
+
+
+<h2 id="patterns">Design Patterns</h2>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Checkbox</h4>
+<p>Use this pattern for a setting that is either selected or not selected.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_checkbox.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Multiple choice</h4>
+<p>Use this pattern for a setting that needs to present a discrete set of options, from which the
+user can choose only one.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_multiple_choice.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Slider</h4>
+<p>Use this pattern for a setting where the range of values are not discrete and fall along a
+continuum.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_slider.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Date/time</h4>
+<p>Use this pattern for a setting that needs to collect a date and/or time from the user.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_date_time.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Subscreen navigation</h4>
+<p>Use this pattern for navigating to a subscreen or sequence of subscreens that guide the user
+through a more complex setup process.</p>
+<ul>
+<li>If navigating to a single subscreen, use the same title in both the subscreen and the label
+navigating to it.</li>
+<li>If navigating to a sequence of subscreens (as in this example), use a title that describes the
+first step in the sequence.</li>
+</ul>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_subscreen_navigation.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>List subscreen</h4>
+<p>Use this pattern for a setting or category of settings that contains a list of equivalent items.
+</p>
+<p>The label provides the name of the item, and secondary text may be used for status. (In this
+example, status is reinforced with an icon to the right of the label.) Any actions associated with
+the list appear in the action bar rather than the list itself.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_list_subscreen.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Master on/off switch</h4>
+<p>Use this pattern for a category of settings that need a mechanism for turning on or off as a
+whole.</p>
+<p>An on/off switch is placed as the first item in the action bar of a subscreen. When the switch
+is turned off, the items in the list disappear, replaced by text that describes why the list is
+empty. If any actions require the switch to be on, they become disabled.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_master_on_off.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<div class="vspace size-2"> </div>
+
+<p>You can also echo the master on/off switch in the menu item that leads to the subscreen.
+However, you should only do this in cases where users rarely need to access the subscreen once
+it's initially set up and more often just want to toggle the switch.</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_master_on_off_2.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Individual on/off switch</h4>
+<p>Use this pattern for an individual setting that requires a more elaborate description than can
+be provided in checkbox form.</p>
+<p>The on/off switch only appears in the subscreen so that users aren't able to toggle it without
+also being exposed to the descriptive text. Secondary text appears below the setting label to
+reflect the current selection.</p>
+<p>In this example, Android Beam is on by default. Since users might not know what this setting
+does, we made the status more descriptive than just "On".</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_individual_on_off.png">
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-3">
+
+<h4>Dependency</h4>
+<p>Use this pattern for a setting that changes availability based on the value of another setting.
+</p>
+<p>The disabled setting appears below its dependency, without any indentation. If the setting
+includes a status line, it says "Unavailable", and if the reason isn't obvious, a brief
+explanation is included in the status.</p>
+<p>If a given setting is a dependency to 3 or more settings, consider using a subscreen with a
+master on/off switch so that your main settings screen isn't cluttered by lots of disabled items.
+</p>
+
+ </div>
+ <div class="layout-content-col span-10">
+
+<img src="{@docRoot}design/media/settings_dependency.png">
+
+ </div>
+</div>
+
+<h2 id="defaults">Defaults</h2>
+
+<p>Take great care in choosing default values for each of your settings. Because settings
+determine app behavior, your choices will contribute to users' first impressions of your app. Even
+though users can change settings, they'll expect the initial states to be sensible. The following
+questions (when applicable) may help inform your decisions:</p>
+
+<ul>
+<li>Which choice would most users be likely to choose on their own if there were no default?</li>
+<li>Which choice is the most neutral or middle-of-the-road?</li>
+<li>Which choice is the least risky, controversial, or over-the-top?</li>
+<li>Which choice uses the least amount of battery or mobile data?</li>
+<li>Which choice best supports the design principle
+<a href="{@docRoot}design/get-started/principles.html#never-lose-my-stuff">Never lose my stuff</a>?</li>
+<li>Which choice best supports the design principle
+<a href="{@docRoot}design/get-started/principles.html#interrupt-only-if-important">Only interrupt
+me if it's important</a>?
+</li>
+</ul>
+
+<h2 id="writing">Writing Guidelines</h2>
+
+<h4>Label clearly and concisely</h4>
+
+<p>Writing a good label for a setting can be challenging because space is very limited. You only
+get one line, and it's incredibly short on the smallest of devices. Follow these guidelines to
+make your labels brief, meaningful, and scannable:</p>
+
+<ul>
+<li>Write each label in sentence case (i.e. only the first word and proper nouns are capitalized).
+</li>
+<li>Don't start a label with an instructional verb like "Set", "Change", "Edit", "Modify",
+"Manage", "Use", "Select", or "Choose". Users already understand that they can do these things to
+settings.</li>
+<li>Likewise, don't end a label with a word like "setting" or "settings". It's already implied.
+</li>
+<li>If the setting is part of a grouping, don't repeat the word(s) used in the section divider or
+subscreen title.</li>
+<li>Avoid starting a label with a negative word like "Don't" or "Never". For example, "Don't
+allow" could be rephrased to "Block".</li>
+<li>Steer clear of technical jargon as much as possible, unless it's a term widely understood by
+your target users. Use common verbs and nouns to convey the setting's purpose rather than its
+underlying technology.</li>
+<li>Don't refer to the user. For example, for a setting allowing the user to turn notifications on
+or off, label it "Notifications" instead of "Notify me".</li>
+</ul>
+
+<p>Once you've decided on labels for your settings, be sure to preview them on an
+<a href="{@docRoot}design/style/metrics-grids.html">LDPI handset</a> in portrait to make sure
+they'll fit everywhere.</p>
+
+<h4>Secondary text below is for status, not description…</h4>
+
+<p>Before Ice Cream Sandwich, we often displayed secondary text below a label to further describe
+it or provide instructions. Starting in Ice Cream Sandwich, we're using secondary text for status.
+</p>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Screen timeout
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Adjust the delay before the screen automatically turns off
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Sleep
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ After 10 minutes of activity
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+</div>
+
+<p>Status in secondary text has the following benefits:</p>
+<ul>
+<li>Users can see at a glance what the current value of a setting is without having to navigate
+any further.</li>
+<li>It applies the design principle
+<a href="{@docRoot}design/get-started/principles.html#keep-it-brief">Keep it brief</a>, which
+users greatly appreciate.</li>
+</ul>
+
+<h4>…unless it's a checkbox setting</h4>
+<p>There's one important exception to the using secondary text for status: checkbox settings.
+Here, use secondary text for description, not status. Status below a checkbox is unnecessary
+because the checkbox already indicates it. The reason why it's appropriate to have a description
+below a checkbox setting is because—unlike other controls—it doesn't display a dialog
+or navigate to another screen where additional information can be provided.</p>
+
+<p>That said, if a checkbox setting's label is clear enough on its own, there's no need to also
+provide a description. Only include one if necessary.</p>
+
+<p>Follow these guidelines to write checkbox setting descriptions:</p>
+<ul>
+<li>Keep it to one sentence and don't use ending punctuation.</li>
+<li>Convey what happens when the setting is checked, phrased in the form of a command. Example:
+"Allow data exchange", not "Allows data exchange".</li>
+<li>Avoid repetition by choosing words that don't already appear in the label.</li>
+<li>Don't refer to the user unless it's necessary for understanding the setting.</li>
+<li>If you must refer to the user, do so in the second person ("you") rather than the first person
+("I"). Android speaks to users, not on behalf of them.</li>
+</ul>
+
+<h4>Writing examples</h4>
+
+<p>The following are examples of changes we made to labels and secondary text in the Settings app
+in Ice Cream Sandwich.</p>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Use tactile feedback
+ </th>
+ </tr>
+ </thead>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Vibrate on touch
+ </th>
+ </tr>
+ </thead>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>In this checkbox setting, we eliminated the throwaway word "Use" and rephrased the label to be
+more direct and understandable.</p>
+
+ </div>
+
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Screen timeout
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Adjust the delay before the screen automatically turns off
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Sleep
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ After 10 minutes of activity
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>In this multiple choice setting, we changed the label to a friendlier term and also replaced
+the description with status. We put some descriptive words around the selected value, "10
+minutes", because on its own, the meaning could be misinterpreted as "sleep for 10 minutes".</p>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ Change screen lock
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Change or disable pattern, PIN, or password security
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ Screen lock
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Pattern
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>This setting navigates to a a sequence of subscreens that allow users to choose a type of
+screen lock and then set it up. We eliminated the throwaway word "Change" in the label, and
+replaced the description with the current type of screen lock set up by the user. If the user
+hasn't set up a screen lock, the secondary text says "None".</p>
+
+ </div>
+</div>
+
+<div class="layout-content-row">
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label bad emulate-content-left-padding">Before</div>
+
+ <table class="ui-table bad emulate-content-left-padding">
+ <thead>
+ <tr>
+ <th class="label">
+ NFC
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Use Near Field Communication to read and exchange tags
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-4">
+
+ <div class="do-dont-label good">After</div>
+
+ <table class="ui-table good">
+ <thead>
+ <tr>
+ <th class="label">
+ NFC
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="secondary-text">
+ Allow data exchange when the phone touches another device
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ </div>
+ <div class="layout-content-col span-5">
+
+<p>In this checkbox setting—although it's technical jargon—we kept the "NFC" label
+because: (1) we couldn't find a clear, concise alternative, and (2) user familiarity with the
+acronym is expected to increase dramatically in the next couple of years.</p>
+<p>We did, however, rewrite the description. It's far less technical than before and does a better
+job of conveying how and why you'd use NFC. We didn't include what the acronym stands for because
+it doesn't mean anything to most users and would have taken up a lot of space.</p>
+
+ </div>
+</div>
+
+<h2 id="checklist">Checklist</h2>
+<ul>
+<li><p>Make sure each item in Settings meets the criteria for belonging there.</p></li>
+<li><p>If you have more than 7 items, explore ways to group related settings.</p></li>
+<li><p>Use design patterns wherever applicable so users don't face a learning curve.</p></li>
+<li><p>Choose defaults that are safe, neutral, and fit the majority of users.</p></li>
+<li><p>Give each setting a clear, concise label and use secondary text appropriately.</p></li>
+</ul>
\ No newline at end of file
diff --git a/docs/html/design/style/writing.jd b/docs/html/design/style/writing.jd
index 80fd03e..919ea7aa 100644
--- a/docs/html/design/style/writing.jd
+++ b/docs/html/design/style/writing.jd
@@ -1,58 +1,6 @@
page.title=Writing Style
@jd:body
-<style>
-
-/* UI tables */
-
-.ui_table {
- width: 100%;
- background: #282828;
- color: #fff;
- border-radius: 2px;
- box-shadow: 0 2px 4px rgba(0,0,0,0.25);
- border-collapse: separate;
-}
-
-.ui_table th,
-.ui_table td {
- padding: 5px 10px;
-}
-
-.ui_table thead th {
- font-weight: 600;
-}
-
-.ui_table tfoot td {
- border-top: 1px solid #494949;
- border-right: 1px solid #494949;
- text-align: center;
-}
-
-.ui_table tfoot td:last-child {
- border-right: 0;
-}
-
-.list_item_margins {
- margin-left: 30px !important;
-}
-
-.example_label {
- margin-bottom: 10px;
- padding-left: 20px;
- background: transparent none no-repeat scroll 0px 3px;
-}
-
-.example_label.bad {
- background-image: url({@docRoot}assets/design/ico_wrong.png);
-}
-
-.example_label.good {
- background-image: url({@docRoot}assets/design/ico_good.png);
-}
-
-</style>
-
<p>When choosing words for your app:</p>
<ol>
<li>
@@ -90,20 +38,20 @@
<ol><li class="value-1"><strong>Keep it brief.</strong> From the setup wizard:</ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Too formal</div>
+ <div class="do-dont-label bad">Too formal</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
Consult the documentation that came with your phone for further instructions.
</td></tr></tbody></table>
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Better</div>
+ <div class="do-dont-label good">Better</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
Read the instructions that came with your phone.
</td></tr></tbody></table>
@@ -115,11 +63,11 @@
<ol><li class="value-2"><strong>Keep it simple.</strong> From the Location settings screen:</ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Confusing</div>
+ <div class="do-dont-label bad">Confusing</div>
- <table class="ui_table bad">
+ <table class="ui-table bad">
<thead>
<tr>
<th>
@@ -139,9 +87,9 @@
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Better</div>
+ <div class="do-dont-label good">Better</div>
- <table class="ui_table good">
+ <table class="ui-table good">
<thead>
<tr>
<th>
@@ -167,12 +115,12 @@
crashes:</ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Confusing and annoying—"Sorry" just rubs salt in the
+ <div class="do-dont-label bad">Confusing and annoying—"Sorry" just rubs salt in the
wound.</div>
- <table class="ui_table bad">
+ <table class="ui-table bad">
<thead>
<tr>
<th colspan="3">
@@ -200,9 +148,9 @@
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Shorter, more direct, no faux-apologetic title:<br><br></div>
+ <div class="do-dont-label good">Shorter, more direct, no faux-apologetic title:<br><br></div>
- <table class="ui_table good">
+ <table class="ui-table good">
<thead>
<tr>
<th colspan="3">
@@ -234,20 +182,20 @@
<ol><li class="value-4"><strong>Put the most important thing first.</strong></ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Top news last</div>
+ <div class="do-dont-label bad">Top news last</div>
- <table class="ui_table bad"><tbody><tr><td>
+ <table class="ui-table bad"><tbody><tr><td>
77 other people +1'd this, including Larry Page.
</td></tr></tbody></table>
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Top news first</div>
+ <div class="do-dont-label good">Top news first</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
Larry Page and 77 others +1'd this.
</td></tr></tbody></table>
@@ -255,20 +203,20 @@
</div>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">Task last</div>
+ <div class="do-dont-label bad">Task last</div>
- <table class="ui_table bad"><tbody><tr><td>
+ <table class="ui-table bad"><tbody><tr><td>
Touch Next to complete setup using a Wi-Fi connection.
</td></tr></tbody></table>
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">Task first</div>
+ <div class="do-dont-label good">Task first</div>
- <table class="ui_table good"><tbody><tr><td>
+ <table class="ui-table good"><tbody><tr><td>
To finish setup using Wi-Fi, touch Next.
</td></tr></tbody></table>
@@ -280,11 +228,11 @@
<ol><li class="value-5"><strong>Describe only what's necessary, and no more.</strong></ol>
<div class="layout-content-row">
- <div class="layout-content-col span-6 list_item_margins">
+ <div class="layout-content-col span-6 layout-with-list-item-margins">
- <div class="example_label bad">From a Setup Wizard screen</div>
+ <div class="do-dont-label bad">From a Setup Wizard screen</div>
- <table class="ui_table bad">
+ <table class="ui-table bad">
<thead>
<tr>
<th>
@@ -306,9 +254,9 @@
</div>
<div class="layout-content-col span-6">
- <div class="example_label good">From a Setup Wizard screen</div>
+ <div class="do-dont-label good">From a Setup Wizard screen</div>
- <table class="ui_table good">
+ <table class="ui-table good">
<thead>
<tr>
<th>
diff --git a/docs/html/guide/developing/testing/testing_otheride.jd b/docs/html/guide/developing/testing/testing_otheride.jd
index 93af979..7745ae7 100644
--- a/docs/html/guide/developing/testing/testing_otheride.jd
+++ b/docs/html/guide/developing/testing/testing_otheride.jd
@@ -209,7 +209,7 @@
<p>
To update a test project with the <code>android</code> tool, enter:
</p>
-<pre>android update-test-project -m <main_path> -p <test_path></pre>
+<pre>android update test-project -m <main_path> -p <test_path></pre>
<table>
<tr>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 9d5064e..ba8dc5e 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -90,21 +90,20 @@
<ul>
<li>
<a href="<?cs var:toroot ?>guide/topics/providers/content-provider-basics.html">
- <span class="en">Content Provider Basics</span>
+ <span class="en">Content Provider Basics<span
+class="new"> new!</span></span>
</a>
- <span class="new">new!</span>
</li>
<li>
<a href="<?cs var:toroot ?>guide/topics/providers/content-provider-creating.html">
- <span class="en">Creating a Content Provider</span>
+ <span class="en">Creating a Content Provider<span
+class="new"> new!</span></span>
</a>
- <span class="new">new!</span>
</li>
<li>
<a href="<?cs var:toroot ?>guide/topics/providers/calendar-provider.html">
- <span class="en">Calendar Provider</span>
+ <span class="en">Calendar Provider<span class="new"> new!</span></span>
</a>
- <span class="new">new!</span>
</li>
</ul>
</li>
@@ -130,8 +129,8 @@
<span class="en">Input Events</span>
</a></li>
<li><a href="<?cs var:toroot ?>guide/topics/ui/menus.html">
- <span class="en">Menus</span>
- </a> <span class="new">updated</span></li>
+ <span class="en">Menus<span class="new"> new!</span></span>
+ </a></li>
<li><a href="<?cs var:toroot ?>guide/topics/ui/actionbar.html">
<span class="en">Action Bar</span>
</a></li>
@@ -160,6 +159,19 @@
<li><a href="<?cs var:toroot ?>guide/topics/ui/custom-components.html">
<span class="en">Custom Components</span>
</a></li>
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/index.html">
+ <span class="en">Accessibility<span class="new"> new!</span></span>
+ </a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/apps.html">
+ <span class="en">Making Applications Accessible</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/services.html">
+ <span class="en">Building Accessibility Services</span>
+ </a></li>
+ </ul>
+ </li>
<li><a href="<?cs var:toroot ?>guide/topics/ui/binding.html">
<span class="en">Binding to Data with AdapterView</span>
</a></li>
@@ -259,13 +271,13 @@
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/graphics/index.html">
<span class="en">Graphics</span>
- </a> <span class="new-child">new!</span></div>
+ </a></div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/graphics/2d-graphics.html">
<span class="en">Canvas and Drawables</span></a></li>
<li><a href="<?cs var:toroot ?>guide/topics/graphics/hardware-accel.html">
<span class="en">Hardware Acceleration</span></a>
- <span class="new">new!</span></li>
+ </li>
<li><a href="<?cs var:toroot ?>guide/topics/graphics/opengl.html">
<span class="en">OpenGL</span>
</a></li>
@@ -287,7 +299,6 @@
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/renderscript/index.html">
<span class="en">Renderscript</span></a>
- <span class="new">updated</span>
</div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/renderscript/graphics.html">
@@ -318,7 +329,6 @@
</li>
<li><a href="<?cs var:toroot ?>guide/topics/media/camera.html">
<span class="en">Camera</span></a>
- <span class="new">new!</span>
</li>
<li><a href="<?cs var:toroot ?>guide/topics/media/audio-capture.html">
<span class="en">Audio Capture</span></a>
@@ -332,7 +342,7 @@
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/sensors/index.html">
<span class="en">Sensors</span>
- </a> <span class="new">new!</span></div>
+ </a></div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/sensors/sensors_overview.html">
<span class="en">Sensors Overview</span>
@@ -373,7 +383,7 @@
</li>
<li class="toggle-list">
<div><a href="<?cs var:toroot?>guide/topics/nfc/index.html">
- <span class="en">Near Field Communication</span></a> <span class="new">updated</span>
+ <span class="en">Near Field Communication</span></a>
</div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/nfc/nfc.html">NFC Basics</a></li>
@@ -381,7 +391,7 @@
</ul>
</li>
<li><a href="<?cs var:toroot?>guide/topics/wireless/wifip2p.html">
- <span class="en">Wi-Fi Direct</span></a> <span class="new">new!</span>
+ <span class="en">Wi-Fi Direct</span></a>
</li>
<li class="toggle-list">
<div><a href="<?cs var:toroot?>guide/topics/usb/index.html">
@@ -502,8 +512,7 @@
<span class="en">Multiple APK Support</span></a>
</li>
<li><a href="<?cs var:toroot ?>guide/market/expansion-files.html">
- <span class="en">APK Expansion Files</span></a>
- <span class="new">new!</span>
+ <span class="en">APK Expansion Files<span class="new"> new!</span></span></a>
</li>
</ul>
</li>
@@ -788,7 +797,7 @@
</li>
<li><a href="<?cs var:toroot ?>guide/practices/tablets-and-handsets.html">
<span class="en">Supporting Tablets and Handsets</span>
- </a> <span class="new">new!</span></li>
+ </a></li>
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/index.html">
<span class="en">UI Guidelines</span>
@@ -835,9 +844,6 @@
</li>
</ul>
<ul>
- <li><a href="<?cs var:toroot ?>guide/practices/design/accessibility.html">
- <span class="en">Designing for Accessibility</span>
- </a></li>
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/practices/design/performance.html">
<span class="en">Designing for Performance</span>
diff --git a/docs/html/guide/practices/design/accessibility.html b/docs/html/guide/practices/design/accessibility.html
new file mode 100644
index 0000000..0fa7b32
--- /dev/null
+++ b/docs/html/guide/practices/design/accessibility.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<meta http-equiv="refresh"
+content="0;url=http://developer.android.com/guide/topics/ui/accessibility/index.html">
+<title>Redirecting...</title>
+</head>
+<body>
+<p>You should be redirected. Please <a
+href="http://developer.android.com/guide/topics/ui/accessibility/index.html">click here</a>.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/docs/html/guide/practices/design/accessibility.jd b/docs/html/guide/practices/design/accessibility.jd
deleted file mode 100644
index 72da04e..0000000
--- a/docs/html/guide/practices/design/accessibility.jd
+++ /dev/null
@@ -1,352 +0,0 @@
-page.title=Designing for Accessibility
-@jd:body
-
-
-<div id="qv-wrapper">
-<div id="qv">
-
- <h2>Quickview</h2>
- <ul>
- <li>To make your application more accessible, you should make sure your UI is navigable
-using a directional controller and your widgets provide content descriptions</li>
- <li>If you implement a custom view, you should ensure that it delivers the appropriate
-accessibility events during user interaction</li>
- </ul>
-
- <h2>In this document</h2>
- <ol>
- <li><a href="#Navigation">Allow Navigation with a Directional Controller</a>
- <ol>
- <li><a href="#FocusOrder">Controlling focus order</a></li>
- <li><a href="#ClickingDpad">Clicking with a directional controller</a></li>
- </ol>
- </li>
- <li><a href="#LabelInputs">Label Your Input Widgets</a></li>
- <li><a href="#UiBestPractices">Follow Android UI Best Practices</a></li>
- <li><a href="#CustomViews">Send Accessibility Events from Custom View Components</a></li>
- <li><a href="#Test">Test Your Application’s Accessibility</a></li>
- </ol>
-
- <h2>Key classes</h2>
- <ol>
- <li>{@link android.view.accessibility.AccessibilityEvent}</li>
- <li>{@link android.view.accessibility.AccessibilityEventSource}</li>
- </ol>
-
- <h2>Related samples</h2>
- <ol>
- <li><a
-href="{@docRoot}resources/samples/AccessibilityService/index.html">Accessibility Service</a></li>
- </ol>
-
-</div>
-</div>
-
-
-
-<p>Many Android users have disabilities that require them to interact with their Android devices in
-different ways. These include users who have visual, physical or age-related disabilities that
-prevent them from fully using or seeing a touchscreen.</p>
-
-<p>Android provides an accessibility layer that helps these users navigate their Android-powered
-devices more easily. Android's accessibility services provide things like text-to-speech, haptic
-feedback, and trackball/d-pad navigation that augment the user experience.</p>
-
-<p>Your application should follow the guidelines in this document to ensure that it provides a
-good experience for users with disabilities. Following these two basic rules will solve most
-access-related problems:</p>
-
-<ul>
-<li>Make all of your user interface controls accessible with a trackball or directional
-controller (d-pad).</li>
-<li>Label your {@link android.widget.ImageButton}, {@link android.widget.EditText}, and other input
-widgets using the <a
-href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">{@code
-android:contentDescription}</a> attribute.</li>
-</ul>
-
-
-
-<h2 id="Navigation">Allow Navigation with a Directional Controller</h2>
-
-<p>Many Android devices come with some sort of directional controller, such as:</p>
-<ul>
-<li>A clickable trackball that users can move in any direction</li>
-<li>A clickable d-pad that allows users to navigate in four directions.</li>
-<li>Arrow keys and an OK button that’s equivalent to clicking a trackball or d-pad.</li>
-</ul>
-
-<p>All of these directional controllers allow users to navigate the screen without using the
-touchscreen. On some devices, a user can also navigate to the top or bottom of a list by holding
-down the <em>alt</em> key while pressing a discrete key for up or down.</p>
-
-<p>A directional controller is the primary means of navigation for users with visual or some
-physical impairments (and also for users without impairments when using devices that don't
-have a touchscreen). You should verify that all UI controls in your application are
-accessible without using the touchscreen and that clicking with the center button (or OK button) has
-the same effect as touching the controls on the touchscreen.</p>
-
-<p>A UI control (also called a "widget") is accessible using directional controls when it's
-"focusable" property is "true." This means that users can focus on the widget using the directional
-controls and then interact with it. Widgets provided by the Android APIs are focusable by default
-and visually indicate focus by changing the widget visual appearance in some way.</p>
-
-<p>Android provides several APIs that let you control whether a widget is focusable and even
-request that a widget be given focus. Such methods include:</p>
-
-<ul>
- <li>{@link android.view.View#setFocusable setFocusable()}</li>
- <li>{@link android.view.View#isFocusable isFocusable()}</li>
- <li>{@link android.view.View#requestFocus requestFocus()}</li>
-</ul>
-
-<p>When working with a view that is not focusable by default, you can make it focusable from the XML
-layout file by setting the <a
-href="{@docRoot}reference/android/view/View.html#attr_android:focusable">{@code
-android:focusable}</a> attribute to {@code "true"}.</p>
-
-
-
-<h3 id="FocusOrder">Controlling focus order</h3>
-
-<p>When the user navigates in any direction using the directional controls, focus is passed from one
-view to another, as determined by the focus ordering. The ordering of the focus movement is based on
-an algorithm that finds the nearest neighbor in a given direction. In rare cases, the default
-algorithm may not match the order that you intended for your UI. In these situations, you can
-provide explicit overrides to the ordering using the following XML attributes in the layout
-file:</p>
-
-<dl>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown"
->{@code android:nextFocusDown}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates down.</dd>
- <a><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusLeft"
->{@code android:nextFocusLeft}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates left.</dd>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusRight"
->{@code android:nextFocusRight}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates right.</dd>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp"
->{@code android:nextFocusUp}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates up.</dd>
-</dl>
-
-<p>For example, here is an XML layout that contains a focusable {@link android.widget.TextView}.
-While the {@link android.widget.TextView} is located to the right of the {@link
-android.widget.EditText}, it can now be reached by pressing the down arrow when focus is on the
-{@link android.widget.EditText}: </p>
-
-<pre>
-<LinearLayout android:orientation="horizontal"
- ... >
- <EditText android:id="@+id/edit"
- android:nextFocusDown=”@+id/text”
- ... />
- <TextView android:id="@+id/text"
- android:focusable=”true”
- android:text="Hello, I am a focusable TextView"
- android:nextFocusUp=”@id/edit”
- ... />
-</LinearLayout>
-</pre>
-
-<p>When modifying this ordering, be sure that the navigation works as expected in all directions
-from each widget and when navigating in reverse (to get back to where you came from).</p>
-
-<p>You can also modify the focus ordering at runtime, using methods in the {@link
-android.view.View} class, such as {@link android.view.View#setNextFocusDownId
-setNextFocusDownId()} and {@link android.view.View#setNextFocusRightId
-setNextFocusRightId()}.</p>
-
-
-<h3 id="ClickingDpad">Clicking with a directional controller</h3>
-
-<p>On most devices, clicking a view using a directional controller sends a {@link
-android.view.KeyEvent} with {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER} to the view currently
-in focus. Make sure this event has the same effect as touching the view on the touchscreen. All
-standard Android views already handle {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER}
-appropriately.</p>
-
-<p>If possible, also treat the {@link android.view.KeyEvent#KEYCODE_ENTER} event the same as
-{@link android.view.KeyEvent#KEYCODE_DPAD_CENTER}. That makes interaction much easier from a full
-keyboard.</p>
-
-
-
-
-<h2 id="LabelInputs">Label Your Input Widgets</h2>
-
-<p>Many input widgets rely on visual cues to inform the user of their meaning. For example, a
-notepad application might use an {@link android.widget.ImageButton} with a picture of a plus sign to
-indicate that the user can add a new note. Or, an {@link android.widget.EditText} may have
-a label near it that indicates its purpose. When a visually impaired user accesses your
-application, these visual cues are often useless.</p>
-
-<p>To provide textual information about these widgets (as an alternative to the visual cues), you
-should use the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription"
->{@code android:contentDescription}</a> attribute. The text you provide in this attribute
-is not visible on the screen, but if a user has enabled accessibility speech tools then the
-description in this attribute is read aloud to the user.</p>
-
-<p>You should set the <a
-href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription" >{@code
-android:contentDescription}</a> attribute on every {@link android.widget.ImageButton}, {@link
-android.widget.EditText}, {@link android.widget.CheckBox}, and on any other input widgets that might
-benefit users with extra information.</p>
-
-<p>For example, the following {@link android.widget.ImageButton} sets the content description for
-the plus button to the {@code add_note} string resource, which might be defined in English as
-“Add note":</p>
-
-<pre>
-<ImageButton
- android:id=”@+id/add_entry_button”
- android:src=”@drawable/plus”
- android:contentDescription=”@string/add_note”/>
-</pre>
-
-<p>This way, when using speech accessibility tools, the user hears "Add note" when focused on
-this widget.</p>
-
-
-
-<h2 id="UiBestPractices">Follow Android UI Best Practices</h2>
-
-<p>You can make it easier for users to learn how to use your application by developing a user
-interface that complies with Android's standard interaction patterns, instead of creating your own
-or using interaction patterns from another platform. This consistency is especially important for
-many disabled users, as they may have less contextual information available to try to understand
-your application’s interface.</p>
-
-<p>Specifically, you should:</p>
-
-<ul>
-<li>Use the platform's built-in widgets and layouts whenever possible, as these views provide
-accessibility support by default.</li>
-<li>Use the <a href="{@docRoot}guide/topics/ui/menus.html#options-menu">Options Menu</a> as an
-alternative to complex touchscreen tasks.</li>
-<li>Make sure the BACK button correctly moves the user back one logical step in the <a
-href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">task's back stack</a> or the
-activity's back stack of fragments (when <a
-href="{@docRoot}guide/topics/fundamentals/fragments.html#Transactions">performing fragment
-transactions</a>), as appropriate.</li>
-</ul>
-
-
-
-<h2 id="CustomViews">Send Accessibility Events from Custom View Components</h2>
-
-<p>If your application requires that you create a <a
-href="{@docRoot}guide/topics/ui/custom-components.html">custom view component</a>, you may need to
-do some additional work to ensure that your view is accessible. Specifically, you should make sure
-that your view implements the {@link android.view.accessibility.AccessibilityEventSource}
-interface and emits {@link android.view.accessibility.AccessibilityEvent}s at the proper times,
-and that each {@link android.view.accessibility.AccessibilityEvent} contains relevant information
-about the state of the view.</p>
-
-<p>Events are emitted whenever something notable happens in the user interface. Currently, there
-are five types of accessibility events that a view should send to the system as the user interacts
-with it:</p>
-
-<dl>
-<dt>{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED}</dt>
-<dd>Indicates that the user clicked on the view (for example, the user selects a button).</dd>
-
-<dt>{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</dt>
-<dd>Indicates that the user performed a long press on the view. </dd>
-
-<dt>{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED}</dt>
-<dd>Indicates that the user selected an item from within the view. This is usually used in the
-context of an {@link android.widget.AdapterView}.</dd>
-
-<dt>{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED}</dt>
-<dd>Indicates that the user moved the focus to the view.</dd>
-
-<dt>{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</dt>
-<dd>Indicates that the text or contents of the view changed.</dd>
-</dl>
-
-
-<p>The basic {@link android.view.View} class implements {@link
-android.view.accessibility.AccessibilityEventSource} and emits these events at the proper time in
-the standard cases. Your custom view should extend from {@link android.view.View} (or one of its
-subclasses) to take advantage of these default implementations.</p>
-
-<p>Depending on the specifics of your custom view, your view may need to emit one of these events at
-a different time than the default {@link android.view.View} implementation. To do so, simply call
-{@link android.view.accessibility.AccessibilityEventSource#sendAccessibilityEvent
-sendAccessibilityEvent()} with the specific event type at the correct time.</p>
-
-<p>For example, say you are implementing a custom slider bar that allows the user to select a
-numeric value by pressing the left or right arrows. This view should emit an event of type {@link
-android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} whenever the slider value
-changes:</p>
-
-<pre>
-@Override
-public boolean onKeyUp (int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
- mCurrentValue--;
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
- return true;
- }
- ...
-}
-</pre>
-
-<p>Each {@link android.view.accessibility.AccessibilityEvent} has a set of required properties that
-describe the current state of the view. These properties include things like the view’s class name,
-text and checked state. The specific properties required for each event type are described in the
-{@link android.view.accessibility.AccessibilityEvent} documentation. The {@link android.view.View}
-implementation will fill in default values for these properties. Most of these values, like the
-class name and event timestamp, will not need to be changed. However, depending on the specifics of
-your custom view, you may want to provide a different value for one or more of the properties. For
-example, your view may have additional state information that you want to add to the event text.</p>
-
-<p>The {@link android.view.View#dispatchPopulateAccessibilityEvent
-dispatchPopulateAccessibilityEvent()} method in {@link android.view.View} provides a hook for making
-changes to the {@link android.view.accessibility.AccessibilityEvent} object before it is
-emitted.</p>
-
-<p>In the above slider bar example, the view should add the current value of the slider bar to the
-text of the event:</p>
-
-<pre>
-@Override
-public boolean dispatchPopulateAccessibilityEvent(final AccessibilityEvent event) {
- super.dispatchPopulateAccessibilityEvent(event);
- if (!isShown()) {
- return false;
- }
- CharSequence text = String.valueOf(mCurrentValue);
- if (text.length() > AccessibilityEvent.MAX_TEXT_LENGTH) {
- text = text.subSequence(0, AccessiblityEvent.MAX_TEXT_LENGTH);
- }
- event.getText().add(text);
- return true;
-}
-</pre>
-
-
-<h2 id="Test">Test Your Application’s Accessibility</h2>
-
-<p>You can simulate the experience for many users by enabling an accessibility service that speaks
-as you move around the screen. One such service is <a
-href="https://play.google.com/store/details?id=com.google.android.marvin.talkback">TalkBack</a>, by the
-<a href="http://code.google.com/p/eyes-free/">Eyes-Free Project</a>. It comes preinstalled on many
-Android-powered devices, but is also available for free from the <a
-href="https://play.google.com/store/details?id=com.google.android.marvin.talkback">Google Play</a> store.</p>
-
-<p>This service requires that you have a text-to-speech engine installed on your phone. You can
-verify if you have one installed in the <strong>Text-to-speech</strong> settings menu by selecting
-<strong>Listen to an example</strong>. If you do not hear anything spoken, install the required
-voice data by selecting <strong>Install voice data</strong>.</p>
-
-<p>Once text-to-speech is functioning correctly, you can enable TalkBack (or another accessibility
-service) in the <strong>Accessibility</strong> settings menu. Enable both
-<strong>Accessibility</strong> and <strong>TalkBack</strong>. As you navigate about the device, you
-should now hear spoken feedback.</p>
-
-<p>You can now attempt to use your application as a blind user would. As you move around using only
-the directional controller, make sure that the spoken feedback you hear makes sense and is
-sufficient to navigate the application without any visual cues.</p>
diff --git a/docs/html/guide/publishing/app-signing.jd b/docs/html/guide/publishing/app-signing.jd
index e86ec30..5bd9be55 100644
--- a/docs/html/guide/publishing/app-signing.jd
+++ b/docs/html/guide/publishing/app-signing.jd
@@ -66,7 +66,7 @@
application's signer certificate expires after the application is installed, the application
will continue to function normally.</li>
<li>You can use standard tools — Keytool and Jarsigner — to generate keys and
-sign your application .apk files.</li>
+sign your application {@code .apk} files.</li>
<li>After you sign your application for release, we recommend that you use the
<code>zipalign</code> tool to optimize the final APK package.</li>
</ul>
@@ -186,9 +186,9 @@
<p>The Android build tools provide a debug signing mode that makes it easier for you
to develop and debug your application, while still meeting the Android system
-requirement for signing your .apk.
+requirement for signing your APK.
When using debug mode to build your app, the SDK tools invoke Keytool to automatically create
-a debug keystore and key. This debug key is then used to automatically sign the .apk, so
+a debug keystore and key. This debug key is then used to automatically sign the APK, so
you do not need to sign the package with your own key.</p>
<p>The SDK tools create the debug keystore/key with predetermined names/passwords:</p>
@@ -215,19 +215,19 @@
<p>If you are developing in Eclipse/ADT (and have set up Keytool and Jarsigner as described above in
<a href="#setup">Basic Setup for Signing</a>),
signing in debug mode is enabled by default. When you run or debug your
-application, ADT signs the .apk with the debug certificate, runs {@code zipalign} on the
-package, then installs it on
+application, ADT signs the {@code .apk} file with the debug certificate, runs {@code zipalign} on
+the package, then installs it on
the selected emulator or connected device. No specific action on your part is needed,
provided ADT has access to Keytool.</p>
<h3>Ant Users</h3>
-<p>If you are using Ant to build your .apk files, debug signing mode
+<p>If you are using Ant to build your {@code .apk} file, debug signing mode
is enabled by using the <code>debug</code> option with the <code>ant</code> command
(assuming that you are using a <code>build.xml</code> file generated by the
<code>android</code> tool). When you run <code>ant debug</code> to
-compile your app, the build script generates a keystore/key and signs the .apk for you.
-The script then also aligns the .apk with the <code>zipalign</code> tool.
+compile your app, the build script generates a keystore/key and signs the APK for you.
+The script then also aligns the APK with the <code>zipalign</code> tool.
No other action on your part is needed. Read
<a href="{@docRoot}guide/developing/building/building-cmdline.html#DebugMode">Building and Running Apps
on the Command Line</a> for more information.</p>
@@ -383,8 +383,8 @@
<p>For more information about Keytool, see the documentation at
<a
-href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/#security">
-http://java.sun.com/j2se/1.5.0/docs/tooldocs/#security</a></p>
+href="http://docs.oracle.com/javase/6/docs/technotes/tools/windows/keytool.html">
+http://docs.oracle.com/javase/6/docs/technotes/tools/windows/keytool.html</a></p>
@@ -399,11 +399,11 @@
<h4>With Eclipse</h4>
-<p>To export an <em>unsigned</em> .apk from Eclipse, right-click the project in the Package
+<p>To export an <em>unsigned</em> APK from Eclipse, right-click the project in the Package
Explorer and select <strong>Android Tools</strong> > <strong>Export Unsigned Application
-Package</strong>. Then specify the file location for the unsigned .apk.
-(Alternatively, open your <code>AndroidManifest.xml</code> file in Eclipse, open
-the <em>Overview</em> tab, and click <strong>Export an unsigned .apk</strong>.)</p>
+Package</strong>. Then specify the file location for the unsigned APK.
+(Alternatively, open your <code>AndroidManifest.xml</code> file in Eclipse, select
+the <strong>Manifest</strong> tab, and click <strong>Export an unsigned APK</strong>.)</p>
<p>Note that you can combine the compiling and signing steps with the Export Wizard. See
<a href="#ExportWizard">Compiling and signing with Eclipse ADT</a>.</p>
@@ -414,11 +414,11 @@
with the <code>ant</code> command. For example, if you are running Ant from the
directory containing your {@code build.xml} file, the command would look like this:</p>
-<pre>ant release</pre>
+<pre>$ ant release</pre>
-<p>By default, the build script compiles the application .apk without signing it. The output file
+<p>By default, the build script compiles the application APK without signing it. The output file
in your project {@code bin/} will be <code><em><your_project_name></em>-unsigned.apk</code>.
-Because the application .apk is still unsigned, you must manually sign it with your private
+Because the application APK is still unsigned, you must manually sign it with your private
key and then align it using {@code zipalign}.</p>
<p>However, the Ant build script can also perform the signing
@@ -443,8 +443,8 @@
the keystore containing your private key is available.</p>
<p>To sign your application, you run Jarsigner, referencing both the
-application's .apk and the keystore containing the private key with which to
-sign the .apk. The table below shows the options you could use. </p>
+application's APK and the keystore containing the private key with which to
+sign the APK. The table below shows the options you could use. </p>
<table>
<tr>
@@ -459,6 +459,14 @@
<td><code>-verbose</code></td><td>Enable verbose output.</td>
</tr>
<tr>
+<td><code>-sigalg</code></td><td>The name of the signature algorithim to use in signing the APK.
+Use the value {@code MD5withRSA}.</td>
+</tr>
+<tr>
+<td><code>-digestalg</code></td><td>The message digest algorithim to use in processing the entries
+of an APK. Use the value {@code SHA1}.</td>
+</tr>
+<tr>
<td><code>-storepass <password></code></td><td><p>The password for the
keystore. </p><p>As a security precaution, do not include this option
in your command line unless you are working at a secure computer.
@@ -478,19 +486,23 @@
<code>my_application.apk</code>, using the example keystore created above.
</p>
-<pre>$ jarsigner -verbose -keystore my-release-key.keystore
+<pre>$ jarsigner -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore my-release-key.keystore
my_application.apk alias_name</pre>
<p>Running the example command above, Jarsigner prompts you to provide
-passwords for the keystore and key. It then modifies the .apk
-in-place, meaning the .apk is now signed. Note that you can sign an
-.apk multiple times with different keys.</p>
+passwords for the keystore and key. It then modifies the APK
+in-place, meaning the APK is now signed. Note that you can sign an
+APK multiple times with different keys.</p>
-<p>To verify that your .apk is signed, you can use a command like this:</p>
+<p class="caution"><strong>Caution:</strong> As of JDK 7, the default signing algorithim has
+changed, requiring you to specify the signature and digest algorithims ({@code -sigalg} and {@code
+-digestalg}) when you sign an APK.</p>
+
+<p>To verify that your APK is signed, you can use a command like this:</p>
<pre>$ jarsigner -verify my_signed.apk</pre>
-<p>If the .apk is signed properly, Jarsigner prints "jar verified".
+<p>If the APK is signed properly, Jarsigner prints "jar verified".
If you want more details, you can try one of these commands:</p>
<pre>$ jarsigner -verify -verbose my_application.apk</pre>
@@ -502,19 +514,19 @@
<p>The command above, with the <code>-certs</code> option added, will show you the
"CN=" line that describes who created the key.</p>
-<p class="note"><strong>Note:</strong> If you see "CN=Android Debug", this means the .apk was
+<p class="note"><strong>Note:</strong> If you see "CN=Android Debug", this means the APK was
signed with the debug key generated by the Android SDK. If you intend to release
your application, you must sign it with your private key instead of the debug
key.</p>
<p>For more information about Jarsigner, see the documentation at
-<a href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/#security">
-http://java.sun.com/j2se/1.5.0/docs/tooldocs/#security</a></p>
+<a href="http://docs.oracle.com/javase/6/docs/technotes/tools/windows/jarsigner.html">
+http://docs.oracle.com/javase/6/docs/technotes/tools/windows/jarsigner.html</a></p>
<h3 id="align">4. Align the final APK package</h3>
-<p>Once you have signed the .apk with your private key, run <code>zipalign</code> on the file.
+<p>Once you have signed the APK with your private key, run <code>zipalign</code> on the file.
This tool ensures that all uncompressed data starts with a particular byte alignment,
relative to the start of the file. Ensuring alignment at 4-byte boundaries provides
a performance optimization when installed on a device. When aligned, the Android
@@ -524,16 +536,16 @@
RAM consumed by the running application.</p>
<p>The <code>zipalign</code> tool is provided with the Android SDK, inside the
-<code>tools/</code> directory. To align your signed .apk, execute:</p>
+<code>tools/</code> directory. To align your signed APK, execute:</p>
-<pre>zipalign -v 4 <em>your_project_name</em>-unaligned.apk <em>your_project_name</em>.apk</pre>
+<pre>$ zipalign -v 4 <em>your_project_name</em>-unaligned.apk <em>your_project_name</em>.apk</pre>
<p>The {@code -v} flag turns on verbose output (optional). {@code 4} is the
byte-alignment (don't use anything other than 4). The first file argument is
-your signed .apk (the input) and the second file is the destination .apk file (the output).
-If you're overriding an existing .apk, add the {@code -f} flag.</p>
+your signed {@code .apk} file (the input) and the second file is the destination {@code .apk} file
+(the output). If you're overriding an existing APK, add the {@code -f} flag.</p>
-<p class="caution"><strong>Caution:</strong> Your input .apk must be signed with your
+<p class="caution"><strong>Caution:</strong> Your input APK must be signed with your
private key <strong>before</strong> you optimize the package with {@code zipalign}.
If you sign it after using {@code zipalign}, it will undo the alignment.</p>
@@ -544,7 +556,7 @@
<h3 id="ExportWizard">Compile and sign with Eclipse ADT</h3>
<p>If you are using Eclipse with the ADT plugin, you can use the Export Wizard to
-export a <em>signed</em> .apk (and even create a new keystore,
+export a <em>signed</em> APK (and even create a new keystore,
if necessary). The Export Wizard performs all the interaction with
the Keytool and Jarsigner for you, which allows you to sign the package using a GUI
instead of performing the manual procedures to compile, sign,
@@ -554,7 +566,7 @@
ensure that they are accessible on your computer, as described above
in the <a href="#setup">Basic Setup for Signing</a>.</p>
-<p>To create a signed and aligned .apk in Eclipse:</p>
+<p>To create a signed and aligned APK in Eclipse:</p>
<ol>
<li>Select the project in the Package
@@ -563,7 +575,7 @@
and click <strong>Next</strong>.
<p>The Export Android Application wizard now starts, which will
guide you through the process of signing your application,
- including steps for selecting the private key with which to sign the .apk
+ including steps for selecting the private key with which to sign the APK
(or creating a new keystore and private key).</p>
<li>Complete the Export Wizard and your application will be compiled,
signed, aligned, and ready for distribution.</li>
diff --git a/docs/html/guide/topics/ui/accessibility/apps.jd b/docs/html/guide/topics/ui/accessibility/apps.jd
new file mode 100644
index 0000000..ff34be6
--- /dev/null
+++ b/docs/html/guide/topics/ui/accessibility/apps.jd
@@ -0,0 +1,570 @@
+page.title=Making Applications Accessible
+parent.title=Accessibility
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#label-ui">Labeling User Interface Elements</a></li>
+ <li><a href="#focus-nav">Enabling Focus Navigation</a>
+ <ol>
+ <li><a href="#focus-enable">Enabling view focus</a></li>
+ <li><a href="#focus-order">Controlling focus order</a></li>
+ </ol>
+ </li>
+ <li><a href="#custom-views">Building Accessible Custom Views</a>
+ <ol>
+ <li><a href="#directional-control">Handling directional controller clicks</a></li>
+ <li><a href="#accessibility-methods">Implementing accessibility API methods</a></li>
+ <li><a href="#send-events">Sending accessibility events</a></li>
+ <li><a href="#populate-events">Populating accessibility events</a></li>
+ </ol>
+ </li>
+ <li><a href="#test">Testing Accessibility</a>
+ <ol>
+ <li><a href="#test-audibles">Testing audible feedback</a></li>
+ <li><a href="#test-navigation">Testing focus navigation</a></li>
+ </ol>
+ </li>
+ </ol>
+
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.view.accessibility.AccessibilityEvent}</li>
+ <li>{@link android.view.accessibility.AccessibilityNodeInfo}</li>
+ <li>{@link android.support.v4.view.accessibility.AccessibilityNodeInfoCompat}</li>
+ <li>{@link android.view.View.AccessibilityDelegate}</li>
+ <li>{@link android.support.v4.view.AccessibilityDelegateCompat}</li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a>
+ </li>
+ <li><a href="{@docRoot}design/index.html">Android Design</a></li>
+ </ol>
+
+</div>
+</div>
+
+<p>Applications built for Android are accessible to users with visual, physical or age-related
+disabilities when they activate accessibility features and services on a device. By default,
+these services make your application more accessible. However, there are further steps you should
+take to optimize the accessibility of your application and ensure a pleasant experience for all your
+users.</p>
+
+<p>Making sure your application is accessible to all users is relatively easy, particularly when you
+use framework-provided user interface components. If you only use these standard components for your
+application, there are just a few steps required to ensure your application is accessible:</p>
+
+<ol>
+ <li>Label your {@link android.widget.ImageButton}, {@link android.widget.ImageView}, {@link
+android.widget.EditText}, {@link android.widget.CheckBox} and other user interface controls using
+the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+ {@code android:contentDescription}</a> attribute.</li>
+ <li>Make all of your user interface elements accessible with a directional controller,
+ such as a trackball or D-pad.</li>
+ <li>Test your application by turning on accessibility services like TalkBack and Explore by
+ Touch, and try using your application using only directional controls.</li>
+</ol>
+
+<p>Developers who create custom controls that extend from the {@link android.view.View} class have
+some additional responsibilities for making sure their components are accessible for users. This
+document also discusses how to make custom view controls compatible with accessibility services.</p>
+
+
+<h2 id="label-ui">Labeling User Interface Elements</h2>
+
+<p>Many user interface controls rely on visual cues to inform users of their meaning. For
+example, a note-taking application might use an {@link android.widget.ImageButton} with a
+picture of a plus sign to indicate that the user can add a new note. Or, an {@link
+android.widget.EditText} component may have a label near it that indicates its purpose. When a user
+with impaired vision accesses your application, these visual cues are often useless.</p>
+
+<p>To provide textual information about interface controls (as an alternative to the visual cues),
+use the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+{@code android:contentDescription}</a> attribute. The text you provide in this attribute is not
+visible on the screen, but if a user has enabled accessibility services that provide audible
+prompts, then the description in this attribute is read aloud to the user.</p>
+
+<p>Set the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+{@code android:contentDescription}</a> attribute for every {@link android.widget.ImageButton},
+{@link android.widget.ImageView}, {@link android.widget.EditText}, {@link android.widget.CheckBox}
+in your application's user interface, and on any other input controls that might require additional
+information for users who are not able to see it.</p>
+
+<p>For example, the following {@link android.widget.ImageButton} sets the content description for
+the plus button to the {@code add_note} string resource, which could be defined as “Add note" for an
+English language interface:</p>
+
+<pre>
+<ImageButton
+ android:id=”@+id/add_note_button”
+ android:src=”@drawable/add_note”
+ android:contentDescription=”@string/add_note”/>
+</pre>
+
+<p>By including the description, speech-based accessibility services can announce "Add note" when a
+user moves focus to this button or hovers over it.</p>
+
+<p class="note">Note: For {@link android.widget.EditText} fields, provide an
+<a href="{@docRoot}reference/android/widget/TextView.html#attr_android:hint">android:hint</a>
+attribute to help users understand what content is expected.</p>
+
+<h2 id="focus-nav">Enabling Focus Navigation</h2>
+
+<p>Focus navigation allows users with disabilities to step through user interface controls using a
+directional controller. Directional controllers can be physical, such as a clickable trackball,
+directional pad (D-Pad) or arrow keys, tab key navigation with an attached keyboard or a software
+application that provides an on-screen directional control.</p>
+
+<p>A directional controller is a primary means of navigation for many users.
+Verify that all user interface (UI) controls in your application are accessible
+without using the touchscreen and that clicking with the center button (or OK button) of a
+directional controller has the same effect as touching the controls on the touchscreen. For
+information on testing directional controls, see <a href="#test-navigation">Testing focus
+navigation</a>.</p>
+
+<h3 id="focus-enable">Enabling view focus</h3>
+
+<p>A user interface element is accessible using directional controls when its
+<a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">
+{@code android:focusable}</a> attribute is set to {@code true}. This setting allows users to focus
+on the element using the directional controls and then interact with it. The user interface controls
+provided by the Android framework are focusable by default and visually indicate focus by changing
+the control’s appearance.</p>
+
+<p>Android provides several APIs that let you control whether a user interface control is focusable
+and even request that a control be given focus:</p>
+
+<ul>
+ <li>{@link android.view.View#setFocusable setFocusable()}</li>
+ <li>{@link android.view.View#isFocusable isFocusable()}</li>
+ <li>{@link android.view.View#requestFocus requestFocus()}</li>
+</ul>
+
+<p>When working with a view that is not focusable by default, you can make it focusable from the XML
+layout file by setting the
+<a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">
+{@code android:focusable}</a> attribute to {@code true} or by using the {@link
+android.view.View#setFocusable setFocusable()} method.</p>
+
+<h3 id="focus-order">Controlling focus order</h3>
+
+<p>When users navigate in any direction using directional controls, focus is passed from one
+user interface element (View) to another, as determined by the focus ordering. The ordering of the
+focus movement is based on an algorithm that finds the nearest neighbor in a given direction. In
+rare cases, the default algorithm may not match the order that you intended for your UI. In these
+situations, you can provide explicit overrides to the ordering using the following XML attributes in
+the layout file:</p>
+
+<dl>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown"
+>{@code android:nextFocusDown}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates down.</dd>
+ <a><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusLeft"
+>{@code android:nextFocusLeft}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates left.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusRight"
+>{@code android:nextFocusRight}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates right.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp"
+>{@code android:nextFocusUp}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates up.</dd>
+</dl>
+
+<p>The following example XML layout shows two focusable user interface elements where the <a
+href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown"
+>{@code android:nextFocusDown}</a> and <a
+href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp"
+>{@code android:nextFocusUp}</a> attributes have been explicitly set. The {@link android.widget.TextView} is
+located to the right of the {@link android.widget.EditText}. However, since these properties have
+been set, the {@link android.widget.TextView} element can now be reached by pressing the down arrow
+when focus is on the {@link android.widget.EditText} element: </p>
+
+<pre>
+<LinearLayout android:orientation="horizontal"
+ ... >
+ <EditText android:id="@+id/edit"
+ android:nextFocusDown=”@+id/text”
+ ... />
+ <TextView android:id="@+id/text"
+ android:focusable=”true”
+ android:text="Hello, I am a focusable TextView"
+ android:nextFocusUp=”@id/edit”
+ ... />
+</LinearLayout>
+</pre>
+
+<p>When modifying focus order, be sure that the navigation works as expected in all directions from
+each user interface control and when navigating in reverse (to get back to where you came from).</p>
+
+<p class="note"><strong>Note:</strong> You can modify the focus order of user interface components
+at runtime, using methods such as {@link android.view.View#setNextFocusDownId setNextFocusDownId()}
+and {@link android.view.View#setNextFocusRightId setNextFocusRightId()}.</p>
+
+
+<h2 id="custom-views">Building Accessible Custom Views</h2>
+
+<p>If your application requires a <a href="{@docRoot}guide/topics/ui/custom-components.html">custom
+view component</a>, you must do some additional work to ensure that your custom view is accessible.
+These are the main tasks for ensuring the accessibility of your view:</p>
+
+<ul>
+ <li>Handle directional controller clicks</li>
+ <li>Implement Accessibility API methods</li>
+ <li>Send {@link android.view.accessibility.AccessibilityEvent} objects specific to your custom view</li>
+ <li>Populate {@link android.view.accessibility.AccessibilityEvent} and {@link
+ android.view.accessibility.AccessibilityNodeInfo} for your view</li>
+</ul>
+
+
+<h3 id="directional-control">Handling directional controller clicks</h3>
+
+<p>On most devices, clicking a view using a directional controller sends a {@link
+android.view.KeyEvent} with {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER} to the view currently
+in focus. All standard Android views already handle {@link
+android.view.KeyEvent#KEYCODE_DPAD_CENTER} appropriately. When building a custom {@link
+android.view.View} control, make sure this event has the same effect as touching the view on the
+touchscreen. </p>
+
+<p>Your custom control should also treat the {@link android.view.KeyEvent#KEYCODE_ENTER} event the
+same as {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER}. This approach makes interaction from a
+full keyboard much easier for users.</p>
+
+
+<h3 id="accessibility-methods">Implementing accessibility API methods</h3>
+
+<p>Accessibility events are messages about users interaction with visual interface components in
+your application. These messages are handled by <a href="services.html">Accessibility Services</a>,
+which use the information in these events to produce supplemental feedback and prompts when users
+have enabled accessibility services. As of Android 4.0 (API Level 14) and higher, the methods for
+generating accessibility events have been expanded to provide more detailed information beyond the
+{@link android.view.accessibility.AccessibilityEventSource} interface introduced in Android 1.6 (API
+Level 4). The expanded accessibility methods are part of the {@link android.view.View} class as well
+as the {@link android.view.View.AccessibilityDelegate} class. The methods are as follows:</p>
+
+<dl>
+ <dt>{@link android.view.View#sendAccessibilityEvent sendAccessibilityEvent()}</dt>
+ <dd>(API Level 4) This method is called when a user takes action on a view. The event is
+classified with a user action type such as {@link
+android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED TYPE_VIEW_CLICKED}. You typically do
+not need to implement this method unless you are creating a custom view.</dd>
+
+ <dt>{@link android.view.View#sendAccessibilityEventUnchecked
+sendAccessibilityEventUnchecked()}</dt>
+ <dd>(API Level 4) This method is used when the calling code needs to directly control the check
+for accessibility being enabled on the device ({@link
+android.view.accessibility.AccessibilityManager#isEnabled AccessibilityManager.isEnabled()}). If
+you do implement this method, you must assume that the calling method has already checked that
+accessibility is enabled and the result is {@code true}. You typically do not need to implement this
+method for a custom view.</dd>
+
+ <dt>{@link android.view.View#dispatchPopulateAccessibilityEvent
+dispatchPopulateAccessibilityEvent()} </dt>
+ <dd>(API Level 4) The system calls this method when your custom view generates an
+accessibility event. As of API Level 14, the default implementation of this method calls {@link
+android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} for this view and
+then the {@link android.view.View#dispatchPopulateAccessibilityEvent
+dispatchPopulateAccessibilityEvent()} method for each child of this view. In order to support
+accessibility services on revisions of Android <em>prior</em> to 4.0 (API Level 14) you
+<em>must</em> override this method and populate {@link
+android.view.accessibility.AccessibilityEvent#getText} with descriptive text for your custom
+view.</dd>
+
+ <dt>{@link android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()}</dt>
+ <dd>(API Level 14) This method sets the text output of an {@link
+android.view.accessibility.AccessibilityEvent} for your view. This method is also called if the
+view is a child of a view which generates an accessibility event.
+
+ <p class="note"><strong>Note:</strong> Modifying additional attributes beyond the text within
+this method potentially overwrites properties set by other methods. So, while you are able modify
+attributes of the accessibility event with this method, you should limit these changes
+to text content only and use the {@link android.view.View#onInitializeAccessibilityEvent
+onInitializeAccessibilityEvent()} method to modify other properties of the event.</p>
+
+ <p class="note"><strong>Note:</strong> If your implementation of this event calls for completely
+overiding the output text without allowing other parts of your layout to modify its content, then
+do not call the super implementation of this method in your code.</p>
+ </dd>
+
+ <dt>{@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()}</dt>
+ <dd>(API Level 14) The system calls this method to obtain additional information about the
+state of the view, beyond text content. If your custom view provides interactive control beyond a
+simple {@link android.widget.TextView} or {@link android.widget.Button}, you should override this
+method and set the additional information about your view into the event using this method, such as
+password field type, checkbox type or states that provide user interaction or feedback. If you
+do override this method, you must call its super implementation and then only modify properties
+that have not been set by the super class.</dd>
+
+ <dt>{@link android.view.View#onInitializeAccessibilityNodeInfo
+onInitializeAccessibilityNodeInfo()}</dt>
+ <dd>(API Level 14) This method provides accessibility services with information about the state of
+the view. The default {@link android.view.View} implementation sets a standard set of view
+properties, but if your custom view provides interactive control beyond a simple {@link
+android.widget.TextView} or {@link android.widget.Button}, you should override this method and set
+the additional information about your view into the {@link
+android.view.accessibility.AccessibilityNodeInfo} object handled by this method.</dd>
+
+ <dt>{@link android.view.ViewGroup#onRequestSendAccessibilityEvent
+onRequestSendAccessibilityEvent()}</dt>
+ <dd>(API Level 14) The system calls this method when a child of your view has generated an
+{@link android.view.accessibility.AccessibilityEvent}. This step allows the the parent view to amend
+the accessibility event with additional information. You should implement this method only if your
+custom view can have child views and if the parent view can provide context information to the
+accessibility event that would be useful to accessibility services.</dd>
+</dl>
+
+<p>In order to support these accessibility methods for a custom view, you should take one of the
+following approaches:</p>
+
+<ul>
+ <li>If your application targets Android 4.0 (API level 14) and higher, override and implement the
+accessibility methods listed above directly in your custom view class.</li>
+ <li>If your custom view is intended to be compatible with Android 1.6 (API Level 4) and above, add
+the Android <a href="{@docRoot}sdk/compatibility-library.html">Support Library</a>, revision 5 or
+higher, to your project. Then, within your custom view class, call the
+{@link android.support.v4.view.ViewCompat#setAccessibilityDelegate
+ViewCompat.setAccessibilityDelegate()} method to implement the accessibility methods
+above. For an example of this approach, see the Android Support Library (revision 5 or higher)
+sample {@code AccessibilityDelegateSupportActivity} in
+({@code <sdk>/extras/android/support/v4/samples/Support4Demos/})
+ </li>
+</ul>
+
+<p>In either case, you should implement the following accessibility methods for your custom view
+class:</p>
+
+<ul>
+ <li>{@link android.view.View#dispatchPopulateAccessibilityEvent
+ dispatchPopulateAccessibilityEvent()}</li>
+ <li>{@link android.view.View#onPopulateAccessibilityEvent
+ onPopulateAccessibilityEvent()}</li>
+ <li>{@link android.view.View#onInitializeAccessibilityEvent
+ onInitializeAccessibilityEvent()}</li>
+ <li>{@link android.view.View#onInitializeAccessibilityNodeInfo
+ onInitializeAccessibilityNodeInfo()}</li>
+</ul>
+
+<p>For more information about implementing these methods, see <a href="#populate-events">Populating
+Accessibility Events</a>.</p>
+
+
+<h3 id="send-events">Sending accessibility events</h3>
+
+<p>Depending on the specifics of your custom view, it may need to send {@link
+android.view.accessibility.AccessibilityEvent} objects at a different times or for events not
+handled by the default implementation. The {@link android.view.View} class provides a default
+implementation for these event types:</p>
+
+<ul>
+ <li>Starting with API Level 4:
+ <ul>
+ <li>{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED}</li>
+
+ <li>{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li>
+
+ <li>{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED}</li>
+ </ul>
+ </li>
+ <li>Starting with API Level 14:
+ <ul>
+ <li>{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED}</li>
+
+ <li>{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li>
+
+ <li>{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li>
+ </ul>
+ </li>
+</ul>
+
+<p class="note"><strong>Note:</strong> Hover events are associated with the Explore by
+Touch feature, which uses these events as triggers for providing audible prompts for user interface
+elements.</p>
+
+<p>In general, you should send an {@link android.view.accessibility.AccessibilityEvent} whenever the
+content of your custom view changes. For example, if you are implementing a custom slider bar that
+allows a user to select a numeric value by pressing the left or right arrows, your custom view
+should emit an event of type {@link
+android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} whenever the slider
+value changes. The following sample code demonstrates the use of the {@link
+android.view.accessibility.AccessibilityEventSource#sendAccessibilityEvent
+sendAccessibilityEvent()} method to report this event.</p>
+
+<pre>
+@Override
+public boolean onKeyUp (int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
+ mCurrentValue--;
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
+ return true;
+ }
+ ...
+}
+</pre>
+
+
+<h3 id="populate-events">Populating accessibility events</h3>
+
+<p>Each {@link android.view.accessibility.AccessibilityEvent} has a set of required properties that
+describe the current state of the view. These properties include things such as the view’s class
+name, content description and checked state. The specific properties required for each event type
+are described in the {@link android.view.accessibility.AccessibilityEvent} reference documentation.
+The {@link android.view.View} implementation provides default values for these properties. Many of
+these values, including the class name and event timestamp, are provided automatically. If you are
+creating a custom view component, you must provide some information about the content and
+characteristics of the view. This information may be as simple as a button label, but may also
+include additional state information that you want to add to the event.</p>
+
+<p>The minimum requirement for providing information to accessibility services with a custom
+view is to implement {@link android.view.View#dispatchPopulateAccessibilityEvent
+dispatchPopulateAccessibilityEvent()}. This method is called by the system to request
+information for an {@link android.view.accessibility.AccessibilityEvent} and makes your custom
+view compatible with accessibility services on Android 1.6 (API Level 4) and higher. The
+following example code demonstrates a basic implementation of this method.</p>
+
+<pre>
+@Override
+public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ super.dispatchPopulateAccessibilityEvent(event);
+ // Call the super implementation to populate its text to the event, which
+ // calls onPopulateAccessibilityEvent() on API Level 14 and up.
+
+ // In case this is running on a API revision earlier that 14, check
+ // the text content of the event and add an appropriate text
+ // description for this custom view:
+ CharSequence text = getText();
+ if (!TextUtils.isEmpty(text)) {
+ event.getText().add(text);
+ }
+}
+</pre>
+
+<p>On Android 4.0 (API Level 14) and higher, the {@link
+android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} and
+{@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()}
+methods are the recommended way to populate or modify the information in an {@link
+android.view.accessibility.AccessibilityEvent}. Use the
+{@link android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} method
+specifically for adding or modifying the text content of the event, which is turned into audible
+prompts by accessibility services such as TalkBack. Use the
+{@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()} method for
+populating additional information about the event, such as the selection state of the view.</p>
+
+<p>In addition, you should also implement the
+{@link android.view.View#onInitializeAccessibilityNodeInfo onInitializeAccessibilityNodeInfo()}
+method. {@link android.view.accessibility.AccessibilityNodeInfo} objects populated by this method
+are used by accessibility services to investigate the view hierarchy that generated an accessibility
+event after receiving that event, to obtain a more detailed context information and provide
+appropriate feedback to users.</p>
+
+<p>The example code below shows how override these three methods by using
+{@link android.support.v4.view.ViewCompat#setAccessibilityDelegate
+ViewCompat.setAccessibilityDelegate()}. Note that this sample code requires that the Android
+<a href="{@docRoot}sdk/compatibility-library.html">Support Library</a> for API Level 4 (revision 5
+or higher) is added to your project.</p>
+
+<pre>
+ViewCompat.setAccessibilityDelegate(new AccessibilityDelegateCompat() {
+ @Override
+ public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ super.onPopulateAccessibilityEvent(host, event);
+ // We call the super implementation to populate its text for the
+ // event. Then we add our text not present in a super class.
+ // Very often you only need to add the text for the custom view.
+ CharSequence text = getText();
+ if (!TextUtils.isEmpty(text)) {
+ event.getText().add(text);
+ }
+ }
+ @Override
+ public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(host, event);
+ // We call the super implementation to let super classes
+ // set appropriate event properties. Then we add the new property
+ // (checked) which is not supported by a super class.
+ event.setChecked(isChecked());
+ }
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfoCompat info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ // We call the super implementation to let super classes set
+ // appropriate info properties. Then we add our properties
+ // (checkable and checked) which are not supported by a super class.
+ info.setCheckable(true);
+ info.setChecked(isChecked());
+ // Quite often you only need to add the text for the custom view.
+ CharSequence text = getText();
+ if (!TextUtils.isEmpty(text)) {
+ info.setText(text);
+ }
+ }
+}
+</pre>
+
+<p>On applications targeting Android 4.0 (API Level 14) and higher, these methods can be implemented
+directly in your custom view class. For another example of this approach, see the Android
+<a href="{@docRoot}sdk/compatibility-library.html">Support Library</a> (revision 5 or higher) sample
+{@code AccessibilityDelegateSupportActivity} in
+({@code <sdk>/extras/android/support/v4/samples/Support4Demos/}).</p>
+
+<p class="note"><strong>Note:</strong> You may find information on implementing accessibility for
+custom views written prior to Android 4.0 that describes the use of the
+{@link android.view.View#dispatchPopulateAccessibilityEvent dispatchPopulateAccessibilityEvent()}
+method for populating AccessibilityEvents. As of the Android 4.0 release, however, the recommended
+approach is to use the
+{@link android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} and
+{@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()}
+methods.</p>
+
+
+<h2 id="test">Testing Accessibility</h2>
+
+<p>Testing the accessibility of your application is an important part of ensuring your users have a
+great experience. You can test the most important parts of accessibility by testing your application
+with audible feedback enabled and testing navigation within your application using directional
+controls.</p>
+
+<h3 id="test-audibles">Testing audible feedback</h3>
+<p>You can simulate the experience for many users by enabling an accessibility service that speaks
+as you move around the screen. The Explore by Touch accessibility service, which is available on
+devices with Android 4.0 and later. The <a
+href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback">TalkBack</a>
+accessibility service, by the <a href="http://code.google.com/p/eyes-free/">Eyes-Free
+Project</a> comes preinstalled on many Android devices.</p>
+
+<p>To enable TalkBack on revisions of Android prior to Android 4.0:</p>
+<ol>
+ <li>Launch the Settings application.</li>
+ <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
+ <li>Select <strong>Accessibility</strong> to enable it.</li>
+ <li>Select <strong>TalkBack</strong> to enable it.</li>
+</ol>
+
+<p class="note"><strong>Note:</strong> If the TalkBack accessibility service is not available, you
+can install it for free from <a href="http://play.google.com">Google Play</a>.</p>
+
+<p>To enable Explore by Touch on Android 4.0 and later:</p>
+<ol>
+ <li>Launch the Settings application.</li>
+ <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
+ <li>Select the <strong>TalkBack</strong> to enable it.</li>
+ <li>Return to the <strong>Accessibility</strong> category and select <strong>Explore by
+Touch</strong> to enable it.
+ <p class="note"><strong>Note:</strong> You must turn on TalkBack <em>first</em>, otherwise this
+option is not available.</p>
+ </li>
+</ol>
+
+<h3 id="test-navigation">Testing focus navigation</h3>
+
+<p>As part of your accessibility testing, you can test navigation of your application using focus,
+even if your test devices does not have a directional controller. The <a
+href="{@docRoot}guide/developing/tools/emulator.html">Android Emulator</a> provides a
+simulated directional controller that you can easily use to test navigation. You can also use the
+arrow keys and Enter key on your keyboard with the Emulator to simulate use of a D-pad.</p>
diff --git a/docs/html/guide/topics/ui/accessibility/index.jd b/docs/html/guide/topics/ui/accessibility/index.jd
new file mode 100644
index 0000000..414d5f3
--- /dev/null
+++ b/docs/html/guide/topics/ui/accessibility/index.jd
@@ -0,0 +1,55 @@
+page.title=Accessibility
+parent.title=User Interface
+parent.link=../index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>Topics</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making Applications Accessible</a>
+ </li>
+ <li><a href="{@docRoot}guide/topics/ui/accessibility/services.html">Building Accessibility
+ Services</a></li>
+ </ol>
+
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.view.accessibility.AccessibilityEvent}</li>
+ <li>{@link android.accessibilityservice.AccessibilityService}</li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
+ </ol>
+
+</div>
+</div>
+
+<p>Many Android users have disabilities that require them to interact with their Android devices in
+different ways. These include users who have visual, physical or age-related disabilities that
+prevent them from fully seeing or using a touchscreen.</p>
+
+<p>Android provides accessibility features and services for helping these users navigate their
+devices more easily, including text-to-speech, haptic feedback, trackball and D-pad navigation that
+augment their experience. Android application developers can take advantage of these services to
+make their applications more accessible and also build their own accessibility services.</p>
+
+<p>The following topics show you how to use the Android framework to make applications more
+accessible.</p>
+
+<dl>
+ <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making Applications
+Accessible</a></strong>
+ </dt>
+ <dd>Development practices and API features to ensure your application is accessible to users with
+disabilities.</dd>
+
+ <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/service.html">Building Accessibility
+Services</a></strong>
+ </dt>
+ <dd>How to use API features to build services that make other applications more accessible for
+users.</dd>
+</dl>
\ No newline at end of file
diff --git a/docs/html/guide/topics/ui/accessibility/services.jd b/docs/html/guide/topics/ui/accessibility/services.jd
new file mode 100644
index 0000000..0dad4ec
--- /dev/null
+++ b/docs/html/guide/topics/ui/accessibility/services.jd
@@ -0,0 +1,290 @@
+page.title=Building Accessibility Services
+parent.title=Accessibility
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>Topics</h2>
+ <ol>
+ <li><a href="#manifest">Manifest Declarations and Permissions</a>
+ <ol>
+ <li><a href="service-declaration">Accessibility service declaration</a></li>
+ <li><a href="#service-config">Accessibility service configuration</a></li>
+ </ol>
+ </li>
+ <li><a href="#methods">AccessibilityService Methods</a></li>
+ <li><a href="#event-details">Getting Event Details</a></li>
+ <li><a href="#examples">Example Code</a></li>
+ </ol>
+
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.accessibilityservice.AccessibilityService}</li>
+ <li>{@link android.accessibilityservice.AccessibilityServiceInfo}</li>
+ <li>{@link android.view.accessibility.AccessibilityEvent}</li>
+ <li>{@link android.view.accessibility.AccessibilityRecord}</li>
+ <li>{@link android.view.accessibility.AccessibilityNodeInfo}</li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
+ </ol>
+
+</div>
+</div>
+
+<p>An accessibility service is an application that provides user interface enhancements to
+assist users with disabilities, or who may temporarily be unable to fully interact with a device.
+For example, users who are driving, taking care of a young child or attending a very loud party
+might need additional or alternative interface feedback.</p>
+
+<p>Android provides standard accessibility services, including TalkBack, and developers can
+create and distribute their own services. This document explains the basics of building an
+accessibility service.</p>
+
+<p>The ability for you to build and deploy accessibility services was introduced with Android
+1.6 (API Level 4) and received significant improvements with Android 4.0 (API Level 14). The Android
+Support Library was also updated with the release of Android 4.0 to provide support for these
+enhanced accessibility features back to Android 1.6. Developers aiming for widely compatible
+accessibility services are encouraged to use the
+<a href="{@docRoot}sdk/compatibility-library.html">Support Library</a> and develop for the more
+advanced accessibility features introduced in Android 4.0.</p>
+
+
+<h2 id="manifest">Manifest Declarations and Permissions</h2>
+
+<p>Applications that provide accessibility services must include specific declarations in their
+ application manifests in order to be treated as an accessibility service by an Android system.
+ This section explains the required and optional settings for accessibility services.</p>
+
+
+<h3 id="service-declaration">Accessibility service declaration</h3>
+
+<p>In order to be treated as an accessibility service, your application must include the
+{@code service} element (rather than the {@code activity} element) within the {@code application}
+element in its manifest. In addition, within the {@code service} element, you must also include an
+accessibility service intent filter, as shown in the following sample:</p>
+
+<pre>
+<application>
+ <service android:name=".MyAccessibilityService"
+ android:label="@string/accessibility_service_label">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService" />
+ </intent-filter>
+ </service>
+</application>
+</pre>
+
+<p>These declarations are required for all accessibility services deployed on Android 1.6 (API Level
+ 4) or higher.</p>
+
+
+<h3 id="service-config">Accessibility service configuration</h3>
+
+<p>Accessibility services must also provide a configuration which specifies the types of
+accessibility events that the service handles and additional information about the service. The
+configuration of an accessibility service is contained in the {@link
+android.accessibilityservice.AccessibilityServiceInfo} class. Your service can build and set a
+configuration using an instance of this class and {@link
+android.accessibilityservice.AccessibilityService#setServiceInfo setServiceInfo()} at runtime.
+However, not all configuration options are available using this method.</p>
+
+<p>Beginning with Android 4.0, you can include a {@code <meta-data>} element in your manifest
+with a reference to a configuration file, which allows you to set the full range of options for
+your accessibility service, as shown in the following example:</p>
+
+<pre>
+<service android:name=".MyAccessibilityService">
+ ...
+ <meta-data
+ android:name="android.accessibilityservice"
+ android:resource="@xml/accessibility_service_config" />
+</service>
+</pre>
+
+<p>This meta-data element refers to an XML file that you create in your application’s resource
+directory ({@code <project_dir>/res/xml/accessibility_service_config.xml}). The following code
+shows example contents for the service configuration file:</p>
+
+<pre>
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:description="@string/accessibility_service_description"
+ android:packageNames="com.example.android.apis"
+ android:accessibilityEventTypes="typeAllMask"
+ android:accessibilityFlags="flagDefault"
+ android:accessibilityFeedbackType="feedbackSpoken"
+ android:notificationTimeout="100"
+ android:canRetrieveWindowContent="true"
+ android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"
+/>
+</pre>
+
+<p>One of the most important functions of the accessibility service configuration parameters is to
+allow you to specify what types of accessibility events your service can handle. Being able to
+specify this information enables accessibility services to cooperate with each other, and allows you
+as a developer the flexibility to handle only specific events types from specific applications. The
+event filtering can include the following criteria:</p>
+
+<ul>
+ <li><strong>Package Names</strong> - Specify the package names of applications whose accessibility
+events you want your service to handle. If this parameter is omitted, your accessibility service is
+considered available to service accessibility events for any application. This parameter can be set
+in the accessibility service configuration files with the {@code android:packageNames} attribute as
+a comma-separated list, or set using the {@link
+android.accessibilityservice.AccessibilityServiceInfo#packageNames
+AccessibilityServiceInfo.packageNames} member.</li>
+ <li><strong>Event Types</strong> - Specify the types of accessibility events you want your service
+to handle. This parameter can be set in the accessibility service configuration files with the
+{@code android:accessibilityEventTypes} attribute as a comma-separated list, or set using the
+{@link android.accessibilityservice.AccessibilityServiceInfo#eventTypes
+AccessibilityServiceInfo.eventTypes} member. </li>
+</ul>
+
+<p>For more information about the XML attributes which can be used in the accessibility service
+ configuration file, follow these links to the reference documentation:</p>
+
+<ul>
+ <li><a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_description">{@code android:description}</a></li>
+ <li><a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_packageNames">{@code android:packageNames}</a></li>
+ <li><a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_accessibilityEventTypes">{@code android:accessibilityEventTypes}</a></li>
+ <li><a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_accessibilityFlags">{@code android:accessibilityFlags}</a></li>
+ <li><a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_accessibilityFeedbackType">{@code android:accessibilityFeedbackType}</a></li>
+ <li><a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_notificationTimeout">{@code android:notificationTimeout}</a></li>
+ <li><a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_canRetrieveWindowContent">{@code android:canRetrieveWindowContent}</a></li>
+ <li><a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_settingsActivity">{@code android:settingsActivity}</a></li>
+</ul>
+
+<p>For more information about which configuration settings can be dynamically set at runtime, see
+the {@link android.accessibilityservice.AccessibilityServiceInfo} reference documentation.</p>
+
+
+<h2 id="methods">AccessibilityService Methods</h2>
+
+<p>An application that provides accessibility service must extend the {@link
+android.accessibilityservice.AccessibilityService} class and override the following methods from
+that class. These methods are presented in the order in which they are called by the Android system,
+from when the service is started
+({@link android.accessibilityservice.AccessibilityService#onServiceConnected onServiceConnected()}),
+while it is running ({@link android.accessibilityservice.AccessibilityService#onAccessibilityEvent
+onAccessibilityEvent()},
+{@link android.accessibilityservice.AccessibilityService#onInterrupt onInterrupt()}) to when it is
+shut down ({@link android.accessibilityservice.AccessibilityService#onUnbind onUnbind()}).</p>
+
+<ul>
+ <li>{@link android.accessibilityservice.AccessibilityService#onServiceConnected
+onServiceConnected()} - (optional) This system calls this method when it successfully connects to
+your accessibility service. Use this method to do any one-time setup steps for your service,
+including connecting to user feedback system services, such as the audio manager or device vibrator.
+If you want to set the configuration of your service at runtime or make one-time adjustments, this
+is a convenient location from which to call {@link
+android.accessibilityservice.AccessibilityService#setServiceInfo setServiceInfo()}.</li>
+
+ <li>{@link android.accessibilityservice.AccessibilityService#onAccessibilityEvent
+onAccessibilityEvent()} - (required) This method is called back by the system when it detects an
+{@link android.view.accessibility.AccessibilityEvent} that matches the event filtering parameters
+specified by your accessibility service. For example, when the user clicks a button or focuses on a
+user interface control in an application for which your accessibility service is providing feedback.
+When this happens, the system calls this method of your service with the associated {@link
+android.view.accessibility.AccessibilityEvent}, which you can then interpret and provide feedback to
+the user. This method may be called many times over the lifecycle of your service.</li>
+
+ <li>{@link android.accessibilityservice.AccessibilityService#onInterrupt onInterrupt()} -
+(required) This method is called when the system wants to interrupt the feedback your service is
+providing, usually in response to a user taking action, such as moving focus to a different user
+interface control than the one for which you are currently providing feedback. This method may be
+called many times over the lifecycle of your service.</li>
+
+ <li>{@link android.accessibilityservice.AccessibilityService#onUnbind onUnbind()} - (optional)
+This method is called when the system is about to shutdown the accessibility service. Use this
+method to do any one-time shutdown procedures, including de-allocating user feedback system
+services, such as the audio manager or device vibrator.</li>
+</ul>
+
+<p>These callback methods provide the basic structure for your accessibility service. It is up to
+you to decide on how to process data provided by the Android system in the form of {@link
+android.view.accessibility.AccessibilityEvent} objects and provide feedback to the user.</p>
+
+
+<h2 id="event-details">Getting Event Details</h2>
+
+<p>The Android system provides information to accessibility services about the user interface
+interaction through {@link android.view.accessibility.AccessibilityEvent} objects. Prior to Android
+4.0, the information available in an accessibility event, while providing a significant amount of
+detail about a user interface control selected by the user, typically provided limited contextual
+information. In many cases, this missing context information might be critical to understanding the
+meaning of the selected control.</p>
+
+<p>A typical example of an interface where context is of critical importance is a calendar or day
+planner. If a user selects a 4:00 PM time slot in a Monday to Friday day list and the accessibility
+service announces “4 PM”, but fails to indicate this is a Friday a Monday, the month or day, this is
+hardly ideal feedback for the user. In this case, the context of a user interface control is of
+critical importance to a user who wants to schedule a meeting.</p>
+
+<p>Android 4.0 significantly extends the amount of information that an accessibility service can
+obtain about an user interface interaction by composing accessibility events based on the view
+hierarchy. A view hierarchy is the set of user interface components that contain the component (its
+parents) and the user interface elements that may be contained by that component (its children). In
+this way, the Android system can provide much richer detail about accessibility events, allowing
+accessibility services to provide more useful feedback to users.</p>
+
+<p>An accessibility service gets information about an user interface event through an {@link
+android.view.accessibility.AccessibilityEvent} passed by the system to the service’s
+{@link android.accessibilityservice.AccessibilityService#onAccessibilityEvent
+onAccessibilityEvent()} callback method. This object provides details about the event, including the
+type of object being acted upon, its descriptive text and other details. Starting in Android 4.0
+(and supported in previous releases through the {@link
+android.support.v4.view.accessibility.AccessibilityEventCompat} object in the Support Library), you
+can obtain additional information about the event using these calls:</p>
+
+<ul>
+ <li>{@link android.view.accessibility.AccessibilityEvent#getRecordCount
+AccessibilityEvent.getRecordCount()} and {@link
+android.view.accessibility.AccessibilityEvent#getRecord getRecord(int)} - These methods allow you to
+retrieve the set of {@link android.view.accessibility.AccessibilityRecord} objects which contributed
+to the {@link android.view.accessibility.AccessibilityEvent} passed to you by the system, which can
+provide more context for your accessibility service.</li>
+
+ <li>{@link android.view.accessibility.AccessibilityEvent#getSource
+AccessibilityEvent.getSource()} - This method returns an {@link
+android.view.accessibility.AccessibilityNodeInfo} object. This object allows you to request the
+parents and children of the component that originated the accessibility event and investigate their
+contents and state in order to provide
+
+ <p class="caution"><strong>Important:</strong> The ability to investigate the full view
+hierarchy from an {@link android.view.accessibility.AccessibilityEvent} potentially exposes private
+user information to your accessibility service. For this reason, your service must request this
+level of access through the accessibility <a href="#service-config">service configuration XML</a>
+file, by including the {@code canRetrieveWindowContent} attribute and setting it to {@code true}. If
+you do not include this setting in your service configuration xml file, calls to {@link
+android.view.accessibility.AccessibilityEvent#getSource getSource()} fail.</p>
+ </li>
+</ul>
+
+
+<h2 id="examples">Example Code</h2>
+
+<p>The API Demo project contains two samples which can be used as a starting point for generating
+accessibility services
+({@code <sdk>/samples/<platform>/ApiDemos/src/com/example/android/apis/accessibility}):
+</p>
+
+<ul>
+ <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/accessibility/ClockBackService.html">ClockBackService</a>
+ - This service is based on the original implementation of {@link
+android.accessibilityservice.AccessibilityService} and can be used as a base for developing basic
+accessibility services that are compatible with Android 1.6 (API Level 4) and higher.</li>
+ <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/accessibility/TaskBackService.html">TaskBackService</a>
+ - This service is based on the enhanced accessibility APIs introduced in Android 4.0 (API Level
+14). However, you can use the Android <a href="{@docRoot}sdk/compatibility-library.html">Support
+Libary</a> to substitute classes introduced in later API levels (e.g.,
+{@link android.view.accessibility.AccessibilityRecord},
+{@link android.view.accessibility.AccessibilityNodeInfo}
+) with equivalent support package classes (e.g.,
+{@link android.support.v4.view.accessibility.AccessibilityRecordCompat},
+{@link android.support.v4.view.accessibility.AccessibilityNodeInfoCompat}
+) to make this example work with API versions back to Android 1.6 (API Level 4).</li>
+</ul>
diff --git a/docs/html/images/training/cool-places.png b/docs/html/images/training/cool-places.png
new file mode 100755
index 0000000..769b5b7
--- /dev/null
+++ b/docs/html/images/training/cool-places.png
Binary files differ
diff --git a/docs/html/images/training/panoramio-grid.png b/docs/html/images/training/panoramio-grid.png
new file mode 100755
index 0000000..45c0eb5
--- /dev/null
+++ b/docs/html/images/training/panoramio-grid.png
Binary files differ
diff --git a/docs/html/resources/dashboard/opengl.jd b/docs/html/resources/dashboard/opengl.jd
index b1c3234..4c55522 100644
--- a/docs/html/resources/dashboard/opengl.jd
+++ b/docs/html/resources/dashboard/opengl.jd
@@ -57,7 +57,7 @@
<div class="dashboard-panel">
<img alt="" width="400" height="250"
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A10.8,89.2" />
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A11.9,88.1" />
<table>
<tr>
@@ -66,14 +66,14 @@
</tr>
<tr>
<td>1.1 only</th>
-<td>10.8%</td>
+<td>11.9%</td>
</tr>
<tr>
<td>2.0 & 1.1</th>
-<td>89.2%</td>
+<td>88.1%</td>
</tr>
</table>
-<p><em>Data collected during a 7-day period ending on March 4, 2012</em></p>
+<p><em>Data collected during a 7-day period ending on April 2, 2012</em></p>
</div>
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 65a5575..2cbbe99 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -52,7 +52,7 @@
<div class="dashboard-panel">
<img alt="" height="250" width="470"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.4,0.8,6.6,25.2,0.5,61.4,0.1,1.1,2.1,0.4,1.2&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0|Android%203.1|Android%203.2|Android%204.0|Android%204.0.3&chco=c4df9b,6fad0c" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.3,0.7,6.0,23.1,0.5,63.2,0.1,1.0,2.2,0.5,2.4&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0|Android%203.1|Android%203.2|Android%204.0|Android%204.0.3&chco=c4df9b,6fad0c" />
<table>
<tr>
@@ -61,25 +61,25 @@
<th>API Level</th>
<th>Distribution</th>
</tr>
-<tr><td><a href="{@docRoot}sdk/android-1.5.html">Android 1.5</a></td><td>Cupcake</td> <td>3</td><td>0.4%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-1.6.html">Android 1.6</a></td><td>Donut</td> <td>4</td><td>0.8%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-2.1.html">Android 2.1</a></td><td>Eclair</td> <td>7</td><td>6.6%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-2.2.html">Android 2.2</a></td><td>Froyo</td> <td>8</td><td>25.3%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-1.5.html">Android 1.5</a></td><td>Cupcake</td> <td>3</td><td>0.3%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-1.6.html">Android 1.6</a></td><td>Donut</td> <td>4</td><td>0.7%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-2.1.html">Android 2.1</a></td><td>Eclair</td> <td>7</td><td>6.0%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-2.2.html">Android 2.2</a></td><td>Froyo</td> <td>8</td><td>23.1%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-2.3.html">Android 2.3 -<br/>
Android 2.3.2</a></td><td rowspan="2">Gingerbread</td> <td>9</td><td>0.5%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-2.3.3.html">Android 2.3.3 -<br/>
- Android 2.3.7</a></td><!-- Gingerbread --> <td>10</td><td>61.5%</td></tr>
+ Android 2.3.7</a></td><!-- Gingerbread --> <td>10</td><td>63.2%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-3.0.html">Android 3.0</a></td>
<td rowspan="3">Honeycomb</td> <td>11</td><td>0.1%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-3.1.html">Android 3.1</a></td><!-- Honeycomb --><td>12</td><td>1.1%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-3.2.html">Android 3.2</a></td><!-- Honeycomb --><td>13</td><td>2.1%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-3.1.html">Android 3.1</a></td><!-- Honeycomb --><td>12</td><td>1.0%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-3.2.html">Android 3.2</a></td><!-- Honeycomb --><td>13</td><td>2.2%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-4.0.html">Android 4.0 -<br/>
Android 4.0.2</a></td>
- <td rowspan="2">Ice Cream Sandwich</td><td>14</td><td>0.4%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-4.0.3.html">Android 4.0.3</a></td><!-- ICS --><td>15</td><td>1.2%</td></tr>
+ <td rowspan="2">Ice Cream Sandwich</td><td>14</td><td>0.5%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-4.0.3.html">Android 4.0.3</a></td><!-- ICS --><td>15</td><td>2.4%</td></tr>
</table>
-<p><em>Data collected during a 14-day period ending on March 5, 2012</em></p>
+<p><em>Data collected during a 14-day period ending on April 2, 2012</em></p>
<!--
<p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
-->
@@ -108,9 +108,9 @@
<div class="dashboard-panel">
<img alt="" height="250" width="660" style="padding:5px;background:#fff"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C09/01%7C09/15%7C10/01%7C10/15%7C11/01%7C11/15%7C12/01%7C12/15%7C01/01%7C01/15%7C02/01%7C02/15%7C03/01%7C1%3A%7C2011%7C%7C%7C%7C%7C%7C%7C%7C2012%7C%7C%7C%7C2012%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:97.9,97.9,97.7,97.6,97.5,99.4,99.4,99.2,98.9,98.8,99.2,98.9,98.9|96.9,96.9,96.6,96.6,96.5,98.6,98.6,98.5,98.3,98.2,98.6,98.4,98.4|95.1,95.2,95.1,95.4,95.2,97.2,97.3,97.3,97.2,97.2,97.6,97.5,97.6|81.8,82.7,83.3,84.4,84.6,87.0,87.7,88.1,88.7,89.2,89.9,90.3,90.8|30.6,34.1,37.8,40.8,43.5,48.4,52.4,55.2,58.2,60.1,62.0,63.7,65.2|0.0,0.0,0.0,0.0,0.0,2.0,2.3,2.6,3.5,3.6,4.0,4.1,4.3|0.0,0.0,0.0,0.0,0.0,1.0,1.2,1.3,2.0,2.2,2.6,3.0,3.2|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.3,0.4,0.7,0.8,1.1&chm=b,c3df9b,0,1,0|b,b8dc82,1,2,0|tAndroid%202.1,608920,2,0,15,,t::-5|b,addb67,2,3,0|tAndroid%202.2,517617,3,0,15,,t::-5|b,a3db4b,3,4,0|tAndroid%202.3.3,426210,4,0,15,,t::-5|b,98dc2e,4,5,0|b,8cd41b,5,6,0|b,7ec113,6,7,0|B,6fad0c,7,8,0&chg=7,25&chdl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3.3|Android%203.1|Android%203.2|Android%204.0.3&chco=add274,a2d15a,97d13e,8bcb28,7dba1e,6ea715,5f920e,507d08" />
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C10/01%7C10/15%7C11/01%7C11/15%7C12/01%7C12/15%7C01/01%7C01/15%7C02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C1%3A%7C2011%7C%7C%7C%7C%7C%7C2012%7C%7C%7C%7C%7C%7C2012%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:97.7,97.6,97.5,99.4,99.4,99.2,98.9,98.8,99.2,98.9,98.9,99.0,98.8|96.6,96.6,96.5,98.6,98.6,98.5,98.3,98.2,98.6,98.4,98.4,98.6,98.5|95.1,95.4,95.2,97.2,97.3,97.3,97.2,97.2,97.6,97.5,97.6,97.8,97.8|83.3,84.4,84.6,87.0,87.7,88.1,88.7,89.2,89.9,90.3,90.8,91.4,91.8|37.8,40.8,43.5,48.4,52.4,55.2,58.2,60.1,62.0,63.7,65.2,66.8,68.6|0.0,0.0,0.0,2.0,2.3,2.6,3.5,3.6,4.0,4.1,4.3,4.6,5.5|0.0,0.0,0.0,1.0,1.2,1.3,2.0,2.2,2.6,3.0,3.2,3.5,4.5|0.0,0.0,0.0,0.0,0.0,0.0,0.3,0.4,0.7,0.8,1.1,1.3,2.3&chm=b,c3df9b,0,1,0|b,b8dc82,1,2,0|tAndroid%202.1,608920,2,0,15,,t::-5|b,addb67,2,3,0|tAndroid%202.2,517617,3,0,15,,t::-5|b,a3db4b,3,4,0|tAndroid%202.3.3,426210,4,0,15,,t::-5|b,98dc2e,4,5,0|b,8cd41b,5,6,0|b,7ec113,6,7,0|B,6fad0c,7,8,0&chg=7,25&chdl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3.3|Android%203.1|Android%203.2|Android%204.0.3&chco=add274,a2d15a,97d13e,8bcb28,7dba1e,6ea715,5f920e,507d08" />
-<p><em>Last historical dataset collected during a 14-day period ending on March 5, 2012</em></p>
+<p><em>Last historical dataset collected during a 14-day period ending on April 2, 2012</em></p>
</div><!-- end dashboard-panel -->
diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd
index e9c738e..e5c79a1 100644
--- a/docs/html/resources/dashboard/screens.jd
+++ b/docs/html/resources/dashboard/screens.jd
@@ -60,7 +60,7 @@
<div class="dashboard-panel">
<img alt="" width="400" height="250"
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Normal%20/%20xhdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A4.9,0.2,2.8,66.4,0.7,18.5,2.5,2.4,1.7" />
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Normal%20/%20xhdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A5.8,0.2,2.3,64.6,0.7,19.6,2.4,2.5,1.9" />
<table>
<tr>
@@ -71,31 +71,31 @@
<th scope="col">xhdpi</th>
</tr>
<tr><th scope="row">small</th>
-<td>1.7%</td> <!-- small/ldpi -->
+<td>1.9%</td> <!-- small/ldpi -->
<td></td> <!-- small/mdpi -->
-<td>2.4%</td> <!-- small/hdpi -->
+<td>2.5%</td> <!-- small/hdpi -->
<td></td> <!-- small/xhdpi -->
</tr>
<tr><th scope="row">normal</th>
<td>0.7%</td> <!-- normal/ldpi -->
-<td>18.5%</td> <!-- normal/mdpi -->
-<td>66.3%</td> <!-- normal/hdpi -->
-<td>2.5%</td> <!-- normal/xhdpi -->
+<td>19.6%</td> <!-- normal/mdpi -->
+<td>64.6%</td> <!-- normal/hdpi -->
+<td>2.4%</td> <!-- normal/xhdpi -->
</tr>
<tr><th scope="row">large</th>
<td>0.2%</td> <!-- large/ldpi -->
-<td>2.8%</td> <!-- large/mdpi -->
+<td>2.3%</td> <!-- large/mdpi -->
<td></td> <!-- large/hdpi -->
<td></td> <!-- large/xhdpi -->
</tr>
<tr><th scope="row">xlarge</th>
<td></td> <!-- xlarge/ldpi -->
-<td>4.9%</td> <!-- xlarge/mdpi -->
+<td>5.8%</td> <!-- xlarge/mdpi -->
<td></td> <!-- xlarge/hdpi -->
<td></td> <!-- xlarge/xhdpi -->
</tr>
</table>
-<p><em>Data collected during a 7-day period ending on March 4, 2012</em></p>
+<p><em>Data collected during a 7-day period ending on April 2, 2012</em></p>
</div>
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index 0b82aee..fb4225d 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -559,6 +559,16 @@
}
},
{
+ tags: ['sample', 'new'],
+ path: 'samples/KeyChainDemo/index.html',
+ title: {
+ en: 'KeyChain Demo'
+ },
+ description: {
+ en: 'A demo application to demonstrate how to use KeyChain APIs.'
+ }
+ },
+ {
tags: ['sample', 'gamedev', 'media'],
path: 'samples/LunarLander/index.html',
title: {
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index 303a6d4..5297c23 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -100,8 +100,9 @@
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>training/efficient-downloads/index.html">
- <span class="en">Transferring Data Without Draining the Battery</span>
- </a> <span class="new">new!</span></div>
+ <span class="en">Transferring Data Without Draining the Battery<span
+class="new"> new!</span></span>
+ </a></div>
<ul>
<li><a href="<?cs var:toroot ?>training/efficient-downloads/efficient-network-access.html">
<span class="en">Optimizing Downloads for Efficient Network Access</span>
@@ -124,8 +125,8 @@
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>training/search/index.html">
- <span class="en">Adding Search Functionality</span>
- </a> <span class="new">new!</span>
+ <span class="en">Adding Search Functionality<span class="new"> new!</span></span>
+ </a>
</div>
<ul>
<li><a href="<?cs var:toroot ?>training/search/setup.html">
@@ -277,7 +278,69 @@
</a>
</li>
</ul>
+ </li>
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>training/tv/index.html">
+ <span class="en">Designing for TV<span class="new"> new!</span></span>
+ </a>
+ </div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/tv/optimizing-layouts-tv.html">
+ <span class="en">Optimizing Layouts for TV</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/tv/optimizing-navigation-tv.html">
+ <span class="en">Optimizing Navigation for TV</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/tv/unsupported-features-tv.html">
+ <span class="en">Handling Features Not Supported on TV</span>
+ </a>
+ </li>
+ </ul>
</li>
+
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>training/displaying-bitmaps/index.html">
+ <span class="en">Displaying Bitmaps Efficiently<span class="new"> new!</span></span>
+ </a>
+ </div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/load-bitmap.html">
+ <span class="en">Loading Large Bitmaps Efficiently</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/process-bitmap.html">
+ <span class="en">Processing Bitmaps Off the UI Thread</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/cache-bitmap.html">
+ <span class="en">Caching Bitmaps</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/displaying-bitmaps/display-bitmap.html">
+ <span class="en">Displaying Bitmaps in Your UI</span>
+ </a>
+ </li>
+ </ul>
+ </li>
+
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>training/accessibility/index.html">
+ <span class="en">Implementing Accessibility<span class="new"> new!</span></span>
+ </a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/accessibility/accessible-app.html">
+ <span class="en">Developing Accessible Applications</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/accessibility/service.html">
+ <span class="en">Developing Accessibility Services</span>
+ </a>
+ </li>
+ </ul>
+ </li>
+
</ul>
</li>
diff --git a/docs/html/resources/samples/images/KeyChainDemo1.png b/docs/html/resources/samples/images/KeyChainDemo1.png
new file mode 100644
index 0000000..d426c22
--- /dev/null
+++ b/docs/html/resources/samples/images/KeyChainDemo1.png
Binary files differ
diff --git a/docs/html/resources/samples/images/KeyChainDemo2.png b/docs/html/resources/samples/images/KeyChainDemo2.png
new file mode 100755
index 0000000..e181e58
--- /dev/null
+++ b/docs/html/resources/samples/images/KeyChainDemo2.png
Binary files differ
diff --git a/docs/html/resources/samples/images/KeyChainDemo3.png b/docs/html/resources/samples/images/KeyChainDemo3.png
new file mode 100755
index 0000000..acfdd89
--- /dev/null
+++ b/docs/html/resources/samples/images/KeyChainDemo3.png
Binary files differ
diff --git a/docs/html/resources/samples/images/KeyChainDemo4.png b/docs/html/resources/samples/images/KeyChainDemo4.png
new file mode 100755
index 0000000..a9101ab
--- /dev/null
+++ b/docs/html/resources/samples/images/KeyChainDemo4.png
Binary files differ
diff --git a/docs/html/sdk/android-4.0.3.jd b/docs/html/sdk/android-4.0.3.jd
index c8563ac..f6dbee0 100644
--- a/docs/html/sdk/android-4.0.3.jd
+++ b/docs/html/sdk/android-4.0.3.jd
@@ -68,6 +68,28 @@
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png"
class="toggle-content-img" alt="" />
+ Revision 3</a> <em>(March 2012)</em>
+ </a></p>
+
+ <div class="toggle-content-toggleme" style="padding-left:2em;">
+
+ <p>Maintenance update. The system version is 4.0.4.</p>
+ <p class="note"><strong>Note:</strong> This system image includes support for emulator
+hardware graphics acceleration when used with SDK Tools r17 or higher.
+(<a href="{@docRoot}guide/developing/devices/emulator.html#accel-graphics">more info</a>)</p>
+ <dl>
+ <dt>Dependencies:</dt>
+ <dd>SDK Tools r17 or higher is required.</dd>
+ </dl>
+
+ </div>
+</div>
+
+<div class="toggle-content closed" style="padding-left:1em;">
+
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png"
+class="toggle-content-img" alt="" />
Revision 2</a> <em>(January 2012)</em>
</a></p>
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index 485f01e..e117118 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -1,8 +1,8 @@
page.title=ADT Plugin for Eclipse
-adt.zip.version=17.0.0
-adt.zip.download=ADT-17.0.0.zip
-adt.zip.bytes=12836115
-adt.zip.checksum=ecb12c07e534997cd32c66d57f21b770
+adt.zip.version=18.0.0
+adt.zip.download=ADT-18.0.0.zip
+adt.zip.bytes=12834793
+adt.zip.checksum=b446fa157ed97af79d1e21629201efbb
@jd:body
@@ -108,10 +108,45 @@
}
</style>
+
<div class="toggleable opened">
<a href="#" onclick="return toggleDiv(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px"
width="9px" />
+ADT 18.0.0</a> <em>(April 2012)</em>
+ <div class="toggleme">
+<dl>
+ <dt>Dependencies:</dt>
+
+ <dd>
+ <ul>
+ <li>Java 1.6 or higher is required for ADT 18.0.0.</li>
+ <li>Eclipse Helios (Version 3.6.2) or higher is required for ADT 18.0.0.</li>
+ <li>ADT 18.0.0 is designed for use with <a href="{@docRoot}sdk/tools-notes.html">SDK Tools
+ r18</a>. If you haven't already installed SDK Tools r18 into your SDK, use the Android SDK
+ Manager to do so.</li>
+ </ul>
+ </dd>
+
+ <dt>Bug fixes:</dt>
+ <dd>
+ <ul>
+ <li>Fixed problem where exporting release package does not recompile libraries in release
+ mode.
+ (<a href="http://code.google.com/p/android/issues/detail?id=27940">Issue 27940</a>)</li>
+ </ul>
+ </dd>
+
+</dl>
+
+</div>
+</div>
+
+
+<div class="toggleable closed">
+ <a href="#" onclick="return toggleDiv(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px"
+width="9px" />
ADT 17.0.0</a> <em>(March 2012)</em>
<div class="toggleme">
<dl>
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 175ab50..b56ccdb 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -2,21 +2,21 @@
page.metaDescription=Download the official Android SDK to develop apps for Android-powered devices.
sdk.redirect=0
-sdk.win_installer=installer_r17-windows.exe
-sdk.win_installer_bytes=37410775
-sdk.win_installer_checksum=5afaf6511ebaa52bd6d1dba4afc61e41
+sdk.win_installer=installer_r18-windows.exe
+sdk.win_installer_bytes=37456234
+sdk.win_installer_checksum=48b1fe7b431afe6b9c8a992bf75dd898
-sdk.win_download=android-sdk_r17-windows.zip
-sdk.win_bytes=37417953
-sdk.win_checksum=3af1baeb39707e54df068e939aea5a79
+sdk.win_download=android-sdk_r18-windows.zip
+sdk.win_bytes=37448775
+sdk.win_checksum=bfbfdf8b2d0fdecc2a621544d706fa98
-sdk.mac_download=android-sdk_r17-macosx.zip
-sdk.mac_bytes=33867836
-sdk.mac_checksum=52639aae036b7c2e47cf291696b23236
+sdk.mac_download=android-sdk_r18-macosx.zip
+sdk.mac_bytes=33903758
+sdk.mac_checksum=8328e8a5531c9d6f6f1a0261cb97af36
-sdk.linux_download=android-sdk_r17-linux.tgz
-sdk.linux_bytes=29706368
-sdk.linux_checksum=14e99dfa8eb1a8fadd2f3557322245c4
+sdk.linux_download=android-sdk_r18-linux.tgz
+sdk.linux_bytes=29731463
+sdk.linux_checksum=6cd716d0e04624b865ffed3c25b3485c
@jd:body
diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd
index a1c59e3..6f06de3 100644
--- a/docs/html/sdk/ndk/index.jd
+++ b/docs/html/sdk/ndk/index.jd
@@ -1,16 +1,16 @@
ndk=true
-ndk.win_download=android-ndk-r7b-windows.zip
-ndk.win_bytes=80346206
-ndk.win_checksum=c42b0c9c14428397337421d5e4999380
+ndk.win_download=android-ndk-r7c-windows.zip
+ndk.win_bytes=80361003
+ndk.win_checksum=e86184cdc4bf71d32fa9185fad8544e2
-ndk.mac_download=android-ndk-r7b-darwin-x86.tar.bz2
-ndk.mac_bytes=73817184
-ndk.mac_checksum=6daa82ca6b73bc0614c9997430079c7a
+ndk.mac_download=android-ndk-r7c-darwin-x86.tar.bz2
+ndk.mac_bytes=73836512
+ndk.mac_checksum=025f57feb5f32ed993a5fa7f5996477d
-ndk.linux_download=android-ndk-r7b-linux-x86.tar.bz2
-ndk.linux_bytes=64349733
-ndk.linux_checksum=0eb8af18796cdaa082df8f7c54ad7f9a
+ndk.linux_download=android-ndk-r7c-linux-x86.tar.bz2
+ndk.linux_bytes=63432410
+ndk.linux_checksum=0bc21b78823dcf6f86b988203626b1fe
page.title=Android NDK
@@ -62,6 +62,59 @@
<div class="toggleable open">
<a href="#" onclick="return toggleDiv(this)"><img src=
"{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px">
+ Android NDK, Revision 7c</a> <em>(March 2012)</em>
+
+ <div class="toggleme">
+ <p>This release of the NDK includes an important fix for Tegra2-based devices, and a few
+additional fixes and improvements:</p>
+
+ <dl>
+ <dt>Important bug fixes:</dt>
+
+ <dd>
+ <ul>
+ <li>Fixed GNU STL armeabi-v7a binaries to not crash on non-NEON
+ devices. The files provided with NDK r7b were not configured properly,
+ resulting in crashes on Tegra2-based devices and others when trying to use
+ certain floating-point functions (e.g., {@code cosf}, {@code sinf}, {@code expf}).</li>
+ </ul>
+ </dd>
+
+ <dt>Important changes:</dt>
+
+ <dd>
+ <ul>
+ <li>Added support for custom output directories through the {@code NDK_OUT}
+ environment variable. When defined, this variable is used to store all
+ intermediate generated files, instead of {@code $PROJECT_PATH/obj}. The variable is
+ also recognized by {@code ndk-gdb}. </li>
+ <li>Added support for building modules with hundreds or even thousands of source
+ files by defining {@code LOCAL_SHORT_COMMANDS} to {@code true} in your {@code Android.mk}.
+ <p>This change forces the NDK build system to put most linker or archiver options
+ into list files, as a work-around for command-line length limitations.
+ See {@code docs/ANDROID-MK.html} for details.</p>
+ </li>
+ </ul>
+ </dd>
+
+ <dt>Other bug fixes:</dt>
+
+ <dd>
+ <ul>
+ <li>Fixed {@code android_getCpuCount()} implementation in the {@code cpufeatures}
+helper library. On certain devices, where cores are enabled dynamically by the system, the previous
+implementation would report the total number of <em>active</em> cores the first time the function
+was called, rather than the total number of <em>physically available</em> cores.</li>
+ </ul>
+ </dd>
+ </dl>
+ </div>
+</div>
+
+
+<div class="toggleable closed">
+ <a href="#" onclick="return toggleDiv(this)"><img src=
+ "{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px">
Android NDK, Revision 7b</a> <em>(February 2012)</em>
<div class="toggleme">
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 5413784..3aafea9e 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -40,8 +40,7 @@
<li>
<span class="heading">Android 3.0 Preview SDK</span>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/preview/start.html">Getting Started</a> <span
-class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>sdk/preview/start.html">Getting Started</a></li>
</ul>
</li><?cs
/if ?>
@@ -79,10 +78,9 @@
<ul>
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>sdk/android-4.0-highlights.html">
- <span class="en">Android 4.0.x Platform</span></a> <span class="new">new!</span></div>
+ <span class="en">Android 4.0.x Platform</span></a></div>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/android-4.0.3.html">Android 4.0.3 Platform</a>
- <span class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>sdk/android-4.0.3.html">Android 4.0.3 Platform</a></li>
<li><a href="<?cs var:toroot ?>sdk/android-4.0.html">Android 4.0 Platform</a> </li>
</ul>
</li>
@@ -153,11 +151,9 @@
</li>
</ul>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r17</a> <span
-class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r18</a></li>
<li><a href="<?cs var:toroot ?>sdk/win-usb.html">Google USB Driver, r4</a></li>
- <li><a href="<?cs var:toroot ?>sdk/compatibility-library.html">Support Package, r7</a>
- <span class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>sdk/compatibility-library.html">Support Package, r7</a></li>
</ul>
</li>
<li>
@@ -172,15 +168,14 @@
<span style="display:none" class="zh-TW"></span>
</span>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 17.0.0
+ <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 18.0.0
<span style="display:none" class="de"></span>
<span style="display:none" class="es"></span>
<span style="display:none" class="fr"></span>
<span style="display:none" class="it"></span>
<span style="display:none" class="ja"></span>
<span style="display:none" class="zh-CN"></span>
- <span style="display:none" class="zh-TW"></span></a> <span
-class="new">new!</span>
+ <span style="display:none" class="zh-TW"></span></a>
</li>
</ul>
</li>
@@ -196,7 +191,7 @@
<span style="display:none" class="zh-TW"></span>
</span>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r7b</a>
+ <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r7c</a>
<span class="new">new!</span>
</li>
<li><a href="<?cs var:toroot ?>sdk/ndk/overview.html">What is the NDK?</a></li>
diff --git a/docs/html/sdk/tools-notes.jd b/docs/html/sdk/tools-notes.jd
index dea0c38..f4e9d4d 100644
--- a/docs/html/sdk/tools-notes.jd
+++ b/docs/html/sdk/tools-notes.jd
@@ -68,6 +68,48 @@
<a href="#" onclick="return toggleDiv(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px"
width="9px" />
+ SDK Tools, Revision 18</a> <em>(April 2012)</em>
+
+ <div class="toggleme">
+ <p class="caution"><strong>Important:</strong> To download the new Android
+ 4.0 system components from the Android SDK Manager, you must first update the
+ SDK tools to revision 14 or later and restart the Android SDK Manager. If you do not,
+ the Android 4.0 system components will not be available for download.</p>
+
+ <dl>
+ <dt>Dependencies:</dt>
+ <dd>
+ <ul>
+ <li>Android SDK Platform-tools revision 9 or later.</li>
+ <li>If you are developing in Eclipse with ADT, note that the SDK Tools r18 is designed for
+ use with ADT 18.0.0 and later. If you haven't already, we highly recommend updating your
+ <a href="{@docRoot}sdk/eclipse-adt.html">ADT Plugin</a> to 18.0.0.</li>
+ <li>If you are developing outside Eclipse, you must have
+ <a href="http://ant.apache.org/">Apache Ant</a> 1.8 or later.</li>
+ </ul>
+ </dd>
+ <dt>General notes:</dt>
+ <dd>
+ <ul>
+ <li>Updated the SdkController app to encapsulate both sensor and multitouch emulation
+ functionality.</li>
+ </ul>
+ </dd>
+ <dt>Bug fixes:</dt>
+ <dd>
+ <ul>
+ <li>Fixed Ant issues where some jar libraries in the {@code libs/} folder are not picked up
+in some cases.</li>
+ </ul>
+ </dd>
+ </dl>
+ </div>
+</div>
+
+<div class="toggleable closed">
+ <a href="#" onclick="return toggleDiv(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px"
+ width="9px" />
SDK Tools, Revision 17</a> <em>(March 2012)</em>
<div class="toggleme">
@@ -96,12 +138,13 @@
<li>Added support for hardware accelerated graphics rendering. This feature requires an
API Level 15, Revision 3 or later system image.
(<a href="{@docRoot}guide/developing/devices/emulator.html#accel-graphics">more info</a>)
- <p class="note"><strong>Note:</strong> As of the SDK Tools Revision 17 release, the
-API Level 15, Revision 3 system image is not yet available.</p>
</li>
<li>Added support for running Android x86 system images in virtualization mode on
Windows and Mac OS X.
-(<a href="{@docRoot}guide/developing/devices/emulator.html#accel-vm">more info</a>)</li>
+(<a href="{@docRoot}guide/developing/devices/emulator.html#accel-vm">more info</a>)
+ <p class="note"><strong>Note:</strong> Use the Android SDK Manager to download and
+install x86 system images. Android x86 system images are not available for all API levels.</p>
+ </li>
<li>Added experimental support for multi-touch input by enabing the emulator to receive
touch input from a USB-tethered physical Android device.
(<a href="http://tools.android.com/tips/hardware-emulation">more info</a>)</li>
diff --git a/docs/html/shareables/training/BitmapFun.zip b/docs/html/shareables/training/BitmapFun.zip
new file mode 100644
index 0000000..e7e71f9
--- /dev/null
+++ b/docs/html/shareables/training/BitmapFun.zip
Binary files differ
diff --git a/docs/html/shareables/training/LocationAware.zip b/docs/html/shareables/training/LocationAware.zip
new file mode 100644
index 0000000..46970cd
--- /dev/null
+++ b/docs/html/shareables/training/LocationAware.zip
Binary files differ
diff --git a/docs/html/training/accessibility/accessible-app.jd b/docs/html/training/accessibility/accessible-app.jd
new file mode 100644
index 0000000..f4087b8
--- /dev/null
+++ b/docs/html/training/accessibility/accessible-app.jd
@@ -0,0 +1,194 @@
+
+page.title=Developing Accessible Applications
+parent.title=Implementing Accessibility
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Developing an Accessibility Service
+next.link=service.html
+
+@jd:body
+
+
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#contentdesc">Add Content Descriptions</a></li>
+ <li><a href="#focus">Design for Focus Navigation</a></li>
+ <li><a href="#events">Fire Accessibility Events</a></li>
+ <li><a href="#testing">Test Your Application</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making
+ Applications Accessible</a></li>
+</ul>
+
+
+</div>
+</div>
+
+<p>Android has several accessibility-focused features baked into the platform,
+which make it easy to optimize your application for those with visual or
+physical disabilities. However, it's not always obvious what the correct
+optimizations are, or the easiest way to leverage the framework toward this
+purpose. This lesson shows you how to implement the strategies and platform
+features that make for a great accessibility-enabled Android application.</p>
+
+<h2 id="contentdesc">Add Content Descriptions</h2>
+<p>A well-designed user interface (UI) often has elements that don't require an explicit
+label to indicate their purpose to the user. A checkbox next to an item in a
+task list application has a fairly obvious purpose, as does a trash can in a file
+manager application. However, to your users with vision impairment, other UI
+cues are needed.</p>
+
+<p>Fortunately, it's easy to add labels to UI elements in your application that
+can be read out loud to your user by a speech-based accessibility service like <a
+ href="https://market.android.com/details?id=com.google.android.marvin.talkback">TalkBack</a>.
+If you have a label that's likely not to change during the lifecycle of the
+application (such as "Pause" or "Purchase"), you can add it via the XML layout,
+by setting a UI element's <a
+ href="{@docRoot}reference/android/view.View#attr_android:contentDescription">android:contentDescription</a> attribute, like in this
+example:</p>
+<pre>
+<Button
+ android:id=”@+id/pause_button”
+ android:src=”@drawable/pause”
+ android:contentDescription=”@string/pause”/>
+</pre>
+
+<p>However, there are plenty of situations where it's desirable to base the content
+description on some context, such as the state of a toggle button, or a piece
+selectable data like a list item. To edit the content description at runtime,
+use the {@link android.view.View#setContentDescription(CharSequence)
+setContentDescription()} method, like this:</p>
+
+<pre>
+String contentDescription = "Select " + strValues[position];
+label.setContentDescription(contentDescription);
+</pre>
+
+<p>This addition to your code is the simplest accessibility improvement you can make to your
+application, but one of the most useful. Try to add content descriptions
+wherever there's useful information, but avoid the web-developer pitfall of
+labelling <em>everything</em> with useless information. For instance, don't set
+an application icon's content description to "app icon". That just increases
+the noise a user needs to navigate in order to pull useful information from your
+interface.</p>
+
+<p>Try it out! Download <a
+ href="https://market.android.com/details?id=com.google.android.marvin.talkback">TalkBack</a>
+(an accessibility service published by Google) and enable it in <strong>Settings
+ > Accessibility > TalkBack</strong>. Then navigate around your own
+application and listen for the audible cues provided by TalkBack.</p>
+
+<h2 id="focus">Design for Focus Navigation</h2>
+<p>Your application should support more methods of navigation than the
+touch screen alone. Many Android devices come with navigation hardware other
+than the touchscreen, like a D-Pad, arrow keys, or a trackball. In addition,
+later Android releases also support connecting external devices like keyboards
+via USB or bluetooth.</p>
+
+<p>In order to enable this form of navigation, all navigational elements that
+the user should be able to navigate to need to be set as focusable. This
+modification can be
+done at runtime using the
+{@link android.view.View#setFocusable View.setFocusable()} method on that UI
+control, or by setting the <a
+ href="{@docRoot}android.view.View#attr_android:focusable">{@code
+ android:focusable}</a>
+attrubute in your XML layout files.</p>
+
+<p>Also, each UI control has 4 attributes,
+<a href="{@docRoot}reference/android/view/View#attr_android:nextFocusUp">{@code
+ android:nextFocusUp}</a>,
+<a
+ href="{@docRoot}reference/android/view/View#attr_android:nextFocusDown">{@code
+ android:nextFocusDown}</a>,
+<a
+ href="{@docRoot}reference/android/view/View#attr_android:nextFocusLeft">{@code
+ android:nextFocusLeft}</a>,
+and <a
+ href="{@docRoot}reference/android/view/View#attr_android:nextFocusRight">{@code
+ android:nextFocusRight}</a>,
+which you can use to designate
+the next view to receive focus when the user navigates in that direction. While
+the platform determines navigation sequences automatically based on layout
+proximity, you can use these attributes to override that sequence if it isn't
+appropriate in your application. </p>
+
+<p>For instance, here's how you represent a button and label, both
+focusable, such that pressing down takes you from the button to the text view, and
+pressing up would take you back to the button.</p>
+
+
+<pre>
+<Button android:id="@+id/doSomething"
+ android:focusable="true"
+ android:nextFocusDown=”@id/label”
+ ... />
+<TextView android:id="@+id/label"
+ android:focusable=”true”
+ android:text="@string/labelText"
+ android:nextFocusUp=”@id/doSomething”
+ ... />
+</pre>
+
+<p>Verify that your application works intuitively in these situations. The
+easiest way is to simply run your application in the Android emulator, and
+navigate around the UI with the emulator's arrow keys, using the OK button as a
+replacement for touch to select UI controls.</p>
+
+<h2 id="events">Fire Accessibility Events</h2>
+<p>If you're using the view components in the Android framework, an
+{@link android.view.accessibility.AccessibilityEvent} is created whenever you
+select an item or change focus in your UI. These events are examined by the
+accessibility service, enabling it to provide features like text-to-speech to
+the user.</p>
+
+<p>If you write a custom view, make sure it fires events at the appropriate
+times. Generate events by calling {@link
+android.view.View#sendAccessibilityEvent(int)}, with a parameter representing
+the type of event that occurred. A complete list of the event types currently
+supported can be found in the {@link
+android.view.accessibility.AccessibilityEvent} reference documentation.
+
+<p>As an example, if you want to extend an image view such that you can write
+captions by typing on the keyboard when it has focus, it makes sense to fire an
+{@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}
+event, even though that's not normally built into image views. The code to
+generate that event would look like this:</p>
+<pre>
+public void onTextChanged(String before, String after) {
+ ...
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
+ }
+ ...
+}
+</pre>
+
+<h2 id="testing">Test Your Application</h2>
+<p>Be sure to test the accessibility functionality as you add it to your
+application. In order to test the content descriptions and Accessibility
+events, install and enable an accessibility service. One option is <a
+ href="https://play.google.com/store/details?id=com.google.android.marvin.talkback">Talkback</a>,
+a free, open source screen reader available on Google Play. With the service
+enabled, test all the navigation flows through your application and listen to
+the spoken feedback.</p>
+
+<p>Also, attempt to navigate your application using a directional controller,
+instead of the touch screen. You can use a physical device with a d-pad or
+trackball if one is available. If not, use the Android emulator and it's
+simulated keyboard controls.</p>
+
+<p>Between the service providing feedback and the directional navigation through
+your application, you should get a sense of what your application is like to
+navigate without any visual cues. Fix problem areas as they appear, and you'll
+end up with with a more accessible Android application.</p>
diff --git a/docs/html/training/accessibility/index.jd b/docs/html/training/accessibility/index.jd
new file mode 100644
index 0000000..d5178a9
--- /dev/null
+++ b/docs/html/training/accessibility/index.jd
@@ -0,0 +1,56 @@
+page.title=Implementing Accessibility
+
+trainingnavtop=true
+startpage=true
+next.title=Developing Accessible Applications
+next.link=accessible-app.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+<ul>
+ <li>Android 2.0 (API Level 5) or higher</li>
+Playback</a></li>
+</ul>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>When it comes to reaching as wide a userbase as possible, it's important to
+pay attention to accessibility in your Android application. Cues in your user
+interface that may work for a majority of users, such as a visible change in
+state when a button is pressed, can be less optimal if the user is visually
+impaired.</p>
+
+<p>This class shows you how to make the most of the accessibility features
+built into the Android framework. It covers how to optimize your app for
+accessibility, leveraging platform features like focus navigation and content
+descriptions. It also covers how to build accessibility services, that can
+facilitate user interaction with <strong>any</strong> Android application, not
+just your own.</p>
+
+<h2>Lessons</h2>
+
+<dl>
+ <dt><b><a href="accessible-app.html">Developing Accessible Applications</a></b></dt>
+ <dd>Learn to make your Android application accessible. Allow for easy
+ navigation with a keyboard or directional pad, set labels and fire events
+ that can be interpreted by an accessibility service to facilitate a smooth
+ user experience.</dd>
+
+ <dt><b><a href="service.html">Developing Accessibility Services</a></b></dt>
+ <dd>Develop an accessibility service that listens for accessibility events,
+ mines those events for information like event type and content descriptions,
+ and uses that information to communicate with the user. The example will
+ use a text-to-speech engine to speak to the user.</dd>
+
+</dl>
+
diff --git a/docs/html/training/accessibility/service.jd b/docs/html/training/accessibility/service.jd
new file mode 100644
index 0000000..f62506b
--- /dev/null
+++ b/docs/html/training/accessibility/service.jd
@@ -0,0 +1,286 @@
+
+page.title=Developing an Accessibility Service
+parent.title=Implementing Accessibility
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Developing Accessible Applications
+previous.link=accessible-app.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#create">Create Your Accessibility Service</a></li>
+ <li><a href="#configure">Configure Your Accessibility Service</a></li>
+ <li><a href="#events">Respond to AccessibilityEvents</a></li>
+ <li><a href="#query">Query the View Heirarchy for More Context</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/topics/ui/accessibility/services.html">Building
+ Accessibility Services</a></li>
+</ul>
+
+</div>
+</div>
+
+
+<p>Accessibility services are a feature of the Android framework designed to
+provide alternative navigation feedback to the user on behalf of applications
+installed on Android devices. An accessibility service can communicate to the
+user on the application's behalf, such as converting text to speech, or haptic
+feedback when a user is hovering on an important area of the screen. This
+lesson covers how to create an accessibility service, process information
+received from the application, and report that information back to the
+user.</p>
+
+
+<h2 id="create">Create Your Accessibility Service</h2>
+<p>An accessibility service can be bundled with a normal application, or created
+as a standalone Android project. The steps to creating the service are the same
+in either situation. Within your project, create a class that extends {@link
+android.accessibilityservice.AccessibilityService}.</p>
+
+<pre>
+package com.example.android.apis.accessibility;
+
+import android.accessibilityservice.AccessibilityService;
+
+public class MyAccessibilityService extends AccessibilityService {
+...
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ }
+
+ @Override
+ public void onInterrupt() {
+ }
+
+...
+}
+</pre>
+
+<p>Like any other service, you also declare it in the manifest file.
+Remember to specify that it handles the {@code android.accessibilityservice} intent,
+so that the service is called when applications fire an
+{@link android.view.accessibility.AccessibilityEvent}.</p>
+
+<pre>
+<application ...>
+...
+<service android:name=".MyAccessibilityService">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService" />
+ </intent-filter>
+ . . .
+</service>
+...
+</application>
+</pre>
+
+<p>If you created a new project for this service, and don't plan on having an
+application, you can remove the starter Activity class (usually called MainActivity.java) from your source. Remember to
+also remove the corresponding activity element from your manifest.</p>
+
+<h2 id="configure">Configure Your Accessibility Service</h2>
+<p>Setting the configuration variables for your accessibility service tells the
+system how and when you want it to run. Which event types would you like to
+respond to? Should the service be active for all applications, or only specific
+package names? What different feedback types does it use?</p>
+
+<p>You have two options for how to set these variables. The
+backwards-compatible option is to set them in code, using {@link
+android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}.
+To do that, override the {@link
+android.accessibilityservice.AccessibilityService#onServiceConnected()} method
+and configure your service in there.</p>
+
+<pre>
+@Override
+public void onServiceConnected() {
+ // Set the type of events that this service wants to listen to. Others
+ // won't be passed to this service.
+ info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
+ AccessibilityEvent.TYPE_VIEW_FOCUSED;
+
+ // If you only want this service to work with specific applications, set their
+ // package names here. Otherwise, when the service is activated, it will listen
+ // to events from all applications.
+ info.packageNames = new String[]
+ {"com.example.android.myFirstApp", "com.example.android.mySecondApp"};
+
+ // Set the type of feedback your service will provide.
+ info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
+
+ // Default services are invoked only if no package-specific ones are present
+ // for the type of AccessibilityEvent generated. This service *is*
+ // application-specific, so the flag isn't necessary. If this was a
+ // general-purpose service, it would be worth considering setting the
+ // DEFAULT flag.
+
+ // info.flags = AccessibilityServiceInfo.DEFAULT;
+
+ info.notificationTimeout = 100;
+
+ this.setServiceInfo(info);
+
+}
+</pre>
+
+<p>Starting with Android 4.0, there is a second option available: configure the
+service using an XML file. Certain configuration options like
+{@link android.R.attr#canRetrieveWindowContent} are only available if you
+configure your service using XML. The same configuration options above, defined
+using XML, would look like this:</p>
+
+<pre>
+<accessibility-service
+ android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
+ android:packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp"
+ android:accessibilityFeedbackType="feedbackSpoken"
+ android:notificationTimeout="100"
+ android:settingsActivity="com.example.android.apis.accessibility.TestBackActivity"
+ android:canRetrieveWindowContent="true"
+/>
+</pre>
+
+<p>If you go the XML route, be sure to reference it in your manifest, by adding
+a <a
+href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data></a> tag to
+your service declaration, pointing at the XML file. If you stored your XML file
+in {@code res/xml/serviceconfig.xml}, the new tag would look like this:</p>
+
+<pre>
+<service android:name=".MyAccessibilityService">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService" />
+ </intent-filter>
+ <meta-data android:name="android.accessibilityservice"
+ android:resource="@xml/serviceconfig" />
+</service>
+</pre>
+
+<h2 id="events">Respond to AccessibilityEvents</h2>
+<p>Now that your service is set up to run and listen for events, write some code
+so it knows what to do when an {@link
+android.view.accessibility.AccessibilityEvent} actually arrives! Start by
+overriding the {@link
+android.accessibilityservice.AccessibilityService#onAccessibilityEvent} method.
+In that method, use {@link
+android.view.accessibility.AccessibilityEvent#getEventType} to determine the
+type of event, and {@link
+android.view.accessibility.AccessibilityEvent#getContentDescription} to extract
+any label text associated with the fiew that fired the event.</pre>
+
+<pre>
+@Override
+public void onAccessibilityEvent(AccessibilityEvent event) {
+ final int eventType = event.getEventType();
+ String eventText = null;
+ switch(eventType) {
+ case AccessibilityEvent.TYPE_VIEW_CLICKED:
+ eventText = "Focused: ";
+ break;
+ case AccessibilityEvent.TYPE_VIEW_FOCUSED:
+ eventText = "Focused: ";
+ break;
+ }
+
+ eventText = eventText + event.getContentDescription();
+
+ // Do something nifty with this text, like speak the composed string
+ // back to the user.
+ speakToUser(eventText);
+ ...
+}
+</pre>
+
+<h2 id="query">Query the View Heirarchy for More Context</h2>
+<p>This step is optional, but highly useful. One of the new features in Android
+4.0 (API Level 14) is the ability for an
+{@link android.accessibilityservice.AccessibilityService} to query the view
+hierarchy, collecting information about the the UI component that generated an event, and
+its parent and children. In order to do this, make sure that you set the
+following line in your XML configuration:</p>
+<pre>
+android:canRetrieveWindowContent="true"
+</pre>
+<p>Once that's done, get an {@link
+android.view.accessibility.AccessibilityNodeInfo} object using {@link
+android.view.accessibility.AccessibilityEvent#getSource}. This call only
+returns an object if the window where the event originated is still the active
+window. If not, it will return null, so <em>behave accordingly</em>. The
+following example is a snippet of code that, when it receives an event, does
+the following:
+<ol>
+ <li>Immediately grab the parent of the view where the event originated</li>
+ <li>In that view, look for a label and a check box as children views</li>
+ <li>If it finds them, create a string to report to the user, indicating
+ the label and whether it was checked or not.</li>
+ <li>If at any point a null value is returned while traversing the view
+ hierarchy, the method quietly gives up.</li>
+</ol>
+
+<pre>
+
+// Alternative onAccessibilityEvent, that uses AccessibilityNodeInfo
+
+@Override
+public void onAccessibilityEvent(AccessibilityEvent event) {
+
+ AccessibilityNodeInfo source = event.getSource();
+ if (source == null) {
+ return;
+ }
+
+ // Grab the parent of the view that fired the event.
+ AccessibilityNodeInfo rowNode = getListItemNodeInfo(source);
+ if (rowNode == null) {
+ return;
+ }
+
+ // Using this parent, get references to both child nodes, the label and the checkbox.
+ AccessibilityNodeInfo labelNode = rowNode.getChild(0);
+ if (labelNode == null) {
+ rowNode.recycle();
+ return;
+ }
+
+ AccessibilityNodeInfo completeNode = rowNode.getChild(1);
+ if (completeNode == null) {
+ rowNode.recycle();
+ return;
+ }
+
+ // Determine what the task is and whether or not it's complete, based on
+ // the text inside the label, and the state of the check-box.
+ if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) {
+ rowNode.recycle();
+ return;
+ }
+
+ CharSequence taskLabel = labelNode.getText();
+ final boolean isComplete = completeNode.isChecked();
+ String completeStr = null;
+
+ if (isComplete) {
+ completeStr = getString(R.string.checked);
+ } else {
+ completeStr = getString(R.string.not_checked);
+ }
+ String reportStr = taskLabel + completeStr;
+ speakToUser(reportStr);
+}
+
+</pre>
+
+<p>Now you have a complete, functioning accessibility service. Try configuring
+how it interacts with the user, by adding Android's <a
+ href="http://developer.android.com/resources/articles/tts.html">text-to-speech
+ engine</a>, or using a {@link android.os.Vibrator} to provide haptic
+feedback!</p>
diff --git a/docs/html/training/displaying-bitmaps/cache-bitmap.jd b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
new file mode 100644
index 0000000..94abe21
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
@@ -0,0 +1,337 @@
+page.title=Caching Bitmaps
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Displaying Bitmaps in Your UI
+next.link=display-bitmap.html
+previous.title=Processing Bitmaps Off the UI Thread
+previous.link=process-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#memory-cache">Use a Memory Cache</a></li>
+ <li><a href="#disk-cache">Use a Disk Cache</a></li>
+ <li><a href="#config-changes">Handle Configuration Changes</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+ <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Loading a single bitmap into your user interface (UI) is straightforward, however things get more
+complicated if you need to load a larger set of images at once. In many cases (such as with
+components like {@link android.widget.ListView}, {@link android.widget.GridView} or {@link
+android.support.v4.view.ViewPager }), the total number of images on-screen combined with images that
+might soon scroll onto the screen are essentially unlimited.</p>
+
+<p>Memory usage is kept down with components like this by recycling the child views as they move
+off-screen. The garbage collector also frees up your loaded bitmaps, assuming you don't keep any
+long lived references. This is all good and well, but in order to keep a fluid and fast-loading UI
+you want to avoid continually processing these images each time they come back on-screen. A memory
+and disk cache can often help here, allowing components to quickly reload processed images.</p>
+
+<p>This lesson walks you through using a memory and disk bitmap cache to improve the responsiveness
+and fluidity of your UI when loading multiple bitmaps.</p>
+
+<h2 id="memory-cache">Use a Memory Cache</h2>
+
+<p>A memory cache offers fast access to bitmaps at the cost of taking up valuable application
+memory. The {@link android.util.LruCache} class (also available in the <a
+href="{@docRoot}reference/android/support/v4/util/LruCache.html">Support Library</a> for use back
+to API Level 4) is particularly well suited to the task of caching bitmaps, keeping recently
+referenced objects in a strong referenced {@link java.util.LinkedHashMap} and evicting the least
+recently used member before the cache exceeds its designated size.</p>
+
+<p class="note"><strong>Note:</strong> In the past, a popular memory cache implementation was a
+{@link java.lang.ref.SoftReference} or {@link java.lang.ref.WeakReference} bitmap cache, however
+this is not recommended. Starting from Android 2.3 (API Level 9) the garbage collector is more
+aggressive with collecting soft/weak references which makes them fairly ineffective. In addition,
+prior to Android 3.0 (API Level 11), the backing data of a bitmap was stored in native memory which
+is not released in a predictable manner, potentially causing an application to briefly exceed its
+memory limits and crash.</p>
+
+<p>In order to choose a suitable size for a {@link android.util.LruCache}, a number of factors
+should be taken into consideration, for example:</p>
+
+<ul>
+ <li>How memory intensive is the rest of your activity and/or application?</li>
+ <li>How many images will be on-screen at once? How many need to be available ready to come
+ on-screen?</li>
+ <li>What is the screen size and density of the device? An extra high density screen (xhdpi) device
+ like <a href="http://www.android.com/devices/detail/galaxy-nexus">Galaxy Nexus</a> will need a
+ larger cache to hold the same number of images in memory compared to a device like <a
+ href="http://www.android.com/devices/detail/nexus-s">Nexus S</a> (hdpi).</li>
+ <li>What dimensions and configuration are the bitmaps and therefore how much memory will each take
+ up?</li>
+ <li>How frequently will the images be accessed? Will some be accessed more frequently than others?
+ If so, perhaps you may want to keep certain items always in memory or even have multiple {@link
+ android.util.LruCache} objects for different groups of bitmaps.</li>
+ <li>Can you balance quality against quantity? Sometimes it can be more useful to store a larger
+ number of lower quality bitmaps, potentially loading a higher quality version in another
+ background task.</li>
+</ul>
+
+<p>There is no specific size or formula that suits all applications, it's up to you to analyze your
+usage and come up with a suitable solution. A cache that is too small causes additional overhead with
+no benefit, a cache that is too large can once again cause {@code java.lang.OutOfMemory} exceptions
+and leave the rest of your app little memory to work with.</p>
+
+<p>Here’s an example of setting up a {@link android.util.LruCache} for bitmaps:</p>
+
+<pre>
+private LruCache<String, Bitmap> mMemoryCache;
+
+@Override
+protected void onCreate(Bundle savedInstanceState) {
+ ...
+ // Get memory class of this device, exceeding this amount will throw an
+ // OutOfMemory exception.
+ final int memClass = ((ActivityManager) context.getSystemService(
+ Context.ACTIVITY_SERVICE)).getMemoryClass();
+
+ // Use 1/8th of the available memory for this memory cache.
+ final int cacheSize = 1024 * 1024 * memClass / 8;
+
+ mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
+ @Override
+ protected int sizeOf(String key, Bitmap bitmap) {
+ // The cache size will be measured in bytes rather than number of items.
+ return bitmap.getByteCount();
+ }
+ };
+ ...
+}
+
+public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
+ if (getBitmapFromMemCache(key) == null) {
+ mMemoryCache.put(key, bitmap);
+ }
+}
+
+public Bitmap getBitmapFromMemCache(String key) {
+ return mMemoryCache.get(key);
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> In this example, one eighth of the application memory is
+allocated for our cache. On a normal/hdpi device this is a minimum of around 4MB (32/8). A full
+screen {@link android.widget.GridView} filled with images on a device with 800x480 resolution would
+use around 1.5MB (800*480*4 bytes), so this would cache a minimum of around 2.5 pages of images in
+memory.</p>
+
+<p>When loading a bitmap into an {@link android.widget.ImageView}, the {@link android.util.LruCache}
+is checked first. If an entry is found, it is used immediately to update the {@link
+android.widget.ImageView}, otherwise a background thread is spawned to process the image:</p>
+
+<pre>
+public void loadBitmap(int resId, ImageView imageView) {
+ final String imageKey = String.valueOf(resId);
+
+ final Bitmap bitmap = getBitmapFromMemCache(imageKey);
+ if (bitmap != null) {
+ mImageView.setImageBitmap(bitmap);
+ } else {
+ mImageView.setImageResource(R.drawable.image_placeholder);
+ BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
+ task.execute(resId);
+ }
+}
+</pre>
+
+<p>The <a href="process-bitmap.html#BitmapWorkerTask">{@code BitmapWorkerTask}</a> also needs to be
+updated to add entries to the memory cache:</p>
+
+<pre>
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+ ...
+ // Decode image in background.
+ @Override
+ protected Bitmap doInBackground(Integer... params) {
+ final Bitmap bitmap = decodeSampledBitmapFromResource(
+ getResources(), params[0], 100, 100));
+ addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
+ return bitmap;
+ }
+ ...
+}
+</pre>
+
+<h2 id="disk-cache">Use a Disk Cache</h2>
+
+<p>A memory cache is useful in speeding up access to recently viewed bitmaps, however you cannot
+rely on images being available in this cache. Components like {@link android.widget.GridView} with
+larger datasets can easily fill up a memory cache. Your application could be interrupted by another
+task like a phone call, and while in the background it might be killed and the memory cache
+destroyed. Once the user resumes, your application it has to process each image again.</p>
+
+<p>A disk cache can be used in these cases to persist processed bitmaps and help decrease loading
+times where images are no longer available in a memory cache. Of course, fetching images from disk
+is slower than loading from memory and should be done in a background thread, as disk read times can
+be unpredictable.</p>
+
+<p class="note"><strong>Note:</strong> A {@link android.content.ContentProvider} might be a more
+appropriate place to store cached images if they are accessed more frequently, for example in an
+image gallery application.</p>
+
+<p>Included in the sample code of this class is a basic {@code DiskLruCache} implementation.
+However, a more robust and recommended {@code DiskLruCache} solution is included in the Android 4.0
+source code ({@code libcore/luni/src/main/java/libcore/io/DiskLruCache.java}). Back-porting this
+class for use on previous Android releases should be fairly straightforward (a <a
+href="http://www.google.com/search?q=disklrucache">quick search</a> shows others who have already
+implemented this solution).</p>
+
+<p>Here’s updated example code that uses the simple {@code DiskLruCache} included in the sample
+application of this class:</p>
+
+<pre>
+private DiskLruCache mDiskCache;
+private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
+private static final String DISK_CACHE_SUBDIR = "thumbnails";
+
+@Override
+protected void onCreate(Bundle savedInstanceState) {
+ ...
+ // Initialize memory cache
+ ...
+ File cacheDir = getCacheDir(this, DISK_CACHE_SUBDIR);
+ mDiskCache = DiskLruCache.openCache(this, cacheDir, DISK_CACHE_SIZE);
+ ...
+}
+
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+ ...
+ // Decode image in background.
+ @Override
+ protected Bitmap doInBackground(Integer... params) {
+ final String imageKey = String.valueOf(params[0]);
+
+ // Check disk cache in background thread
+ Bitmap bitmap = getBitmapFromDiskCache(imageKey);
+
+ if (bitmap == null) { // Not found in disk cache
+ // Process as normal
+ final Bitmap bitmap = decodeSampledBitmapFromResource(
+ getResources(), params[0], 100, 100));
+ }
+
+ // Add final bitmap to caches
+ addBitmapToCache(String.valueOf(imageKey, bitmap);
+
+ return bitmap;
+ }
+ ...
+}
+
+public void addBitmapToCache(String key, Bitmap bitmap) {
+ // Add to memory cache as before
+ if (getBitmapFromMemCache(key) == null) {
+ mMemoryCache.put(key, bitmap);
+ }
+
+ // Also add to disk cache
+ if (!mDiskCache.containsKey(key)) {
+ mDiskCache.put(key, bitmap);
+ }
+}
+
+public Bitmap getBitmapFromDiskCache(String key) {
+ return mDiskCache.get(key);
+}
+
+// Creates a unique subdirectory of the designated app cache directory. Tries to use external
+// but if not mounted, falls back on internal storage.
+public static File getCacheDir(Context context, String uniqueName) {
+ // Check if media is mounted or storage is built-in, if so, try and use external cache dir
+ // otherwise use internal cache dir
+ final String cachePath = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
+ || !Environment.isExternalStorageRemovable() ?
+ context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();
+
+ return new File(cachePath + File.separator + uniqueName);
+}
+</pre>
+
+<p>While the memory cache is checked in the UI thread, the disk cache is checked in the background
+thread. Disk operations should never take place on the UI thread. When image processing is
+complete, the final bitmap is added to both the memory and disk cache for future use.</p>
+
+<h2 id="config-changes">Handle Configuration Changes</h2>
+
+<p>Runtime configuration changes, such as a screen orientation change, cause Android to destroy and
+restart the running activity with the new configuration (For more information about this behavior,
+see <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a>).
+You want to avoid having to process all your images again so the user has a smooth and fast
+experience when a configuration change occurs.</p>
+
+<p>Luckily, you have a nice memory cache of bitmaps that you built in the <a
+href="#memory-cache">Use a Memory Cache</a> section. This cache can be passed through to the new
+activity instance using a {@link android.app.Fragment} which is preserved by calling {@link
+android.app.Fragment#setRetainInstance setRetainInstance(true)}). After the activity has been
+recreated, this retained {@link android.app.Fragment} is reattached and you gain access to the
+existing cache object, allowing images to be quickly fetched and re-populated into the {@link
+android.widget.ImageView} objects.</p>
+
+<p>Here’s an example of retaining a {@link android.util.LruCache} object across configuration
+changes using a {@link android.app.Fragment}:</p>
+
+<pre>
+private LruCache<String, Bitmap> mMemoryCache;
+
+@Override
+protected void onCreate(Bundle savedInstanceState) {
+ ...
+ RetainFragment mRetainFragment =
+ RetainFragment.findOrCreateRetainFragment(getFragmentManager());
+ mMemoryCache = RetainFragment.mRetainedCache;
+ if (mMemoryCache == null) {
+ mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
+ ... // Initialize cache here as usual
+ }
+ mRetainFragment.mRetainedCache = mMemoryCache;
+ }
+ ...
+}
+
+class RetainFragment extends Fragment {
+ private static final String TAG = "RetainFragment";
+ public LruCache<String, Bitmap> mRetainedCache;
+
+ public RetainFragment() {}
+
+ public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
+ RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
+ if (fragment == null) {
+ fragment = new RetainFragment();
+ }
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ <strong>setRetainInstance(true);</strong>
+ }
+}
+</pre>
+
+<p>To test this out, try rotating a device both with and without retaining the {@link
+android.app.Fragment}. You should notice little to no lag as the images populate the activity almost
+instantly from memory when you retain the cache. Any images not found in the memory cache are
+hopefully available in the disk cache, if not, they are processed as usual.</p>
diff --git a/docs/html/training/displaying-bitmaps/display-bitmap.jd b/docs/html/training/displaying-bitmaps/display-bitmap.jd
new file mode 100644
index 0000000..7a93313
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/display-bitmap.jd
@@ -0,0 +1,400 @@
+page.title=Displaying Bitmaps in Your UI
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Caching Bitmaps
+previous.link=cache-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#viewpager">Load Bitmaps into a ViewPager Implementation</a></li>
+ <li><a href="#gridview">Load Bitmaps into a GridView Implementation</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}design/patterns/swipe-views.html">Android Design: Swipe Views</a></li>
+ <li><a href="{@docRoot}design/building-blocks/grid-lists.html">Android Design: Grid Lists</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+ <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p></p>
+
+<p>This lesson brings together everything from previous lessons, showing you how to load multiple
+bitmaps into {@link android.support.v4.view.ViewPager} and {@link android.widget.GridView}
+components using a background thread and bitmap cache, while dealing with concurrency and
+configuration changes.</p>
+
+<h2 id="viewpager">Load Bitmaps into a ViewPager Implementation</h2>
+
+<p>The <a href="{@docRoot}design/patterns/swipe-views.html">swipe view pattern</a> is an excellent
+way to navigate the detail view of an image gallery. You can implement this pattern using a {@link
+android.support.v4.view.ViewPager} component backed by a {@link
+android.support.v4.view.PagerAdapter}. However, a more suitable backing adapter is the subclass
+{@link android.support.v4.app.FragmentStatePagerAdapter} which automatically destroys and saves
+state of the {@link android.app.Fragment Fragments} in the {@link android.support.v4.view.ViewPager}
+as they disappear off-screen, keeping memory usage down.</p>
+
+<p class="note"><strong>Note:</strong> If you have a smaller number of images and are confident they
+all fit within the application memory limit, then using a regular {@link
+android.support.v4.view.PagerAdapter} or {@link android.support.v4.app.FragmentPagerAdapter} might
+be more appropriate.</p>
+
+<p>Here’s an implementation of a {@link android.support.v4.view.ViewPager} with {@link
+android.widget.ImageView} children. The main activity holds the {@link
+android.support.v4.view.ViewPager} and the adapter:</p>
+
+<pre>
+public class ImageDetailActivity extends FragmentActivity {
+ public static final String EXTRA_IMAGE = "extra_image";
+
+ private ImagePagerAdapter mAdapter;
+ private ViewPager mPager;
+
+ // A static dataset to back the ViewPager adapter
+ public final static Integer[] imageResIds = new Integer[] {
+ R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
+ R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
+ R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.image_detail_pager); // Contains just a ViewPager
+
+ mAdapter = new ImagePagerAdapter(getSupportFragmentManager(), imageResIds.length);
+ mPager = (ViewPager) findViewById(R.id.pager);
+ mPager.setAdapter(mAdapter);
+ }
+
+ public static class ImagePagerAdapter extends FragmentStatePagerAdapter {
+ private final int mSize;
+
+ public ImagePagerAdapter(FragmentManager fm, int size) {
+ super(fm);
+ mSize = size;
+ }
+
+ @Override
+ public int getCount() {
+ return mSize;
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ return ImageDetailFragment.newInstance(position);
+ }
+ }
+}
+</pre>
+
+<p>The details {@link android.app.Fragment} holds the {@link android.widget.ImageView} children:</p>
+
+<pre>
+public class ImageDetailFragment extends Fragment {
+ private static final String IMAGE_DATA_EXTRA = "resId";
+ private int mImageNum;
+ private ImageView mImageView;
+
+ static ImageDetailFragment newInstance(int imageNum) {
+ final ImageDetailFragment f = new ImageDetailFragment();
+ final Bundle args = new Bundle();
+ args.putInt(IMAGE_DATA_EXTRA, imageNum);
+ f.setArguments(args);
+ return f;
+ }
+
+ // Empty constructor, required as per Fragment docs
+ public ImageDetailFragment() {}
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mImageNum = getArguments() != null ? getArguments().getInt(IMAGE_DATA_EXTRA) : -1;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // image_detail_fragment.xml contains just an ImageView
+ final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
+ mImageView = (ImageView) v.findViewById(R.id.imageView);
+ return v;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ final int resId = ImageDetailActivity.imageResIds[mImageNum];
+ <strong>mImageView.setImageResource(resId);</strong> // Load image into ImageView
+ }
+}
+</pre>
+
+<p>Hopefully you noticed the issue with this implementation; The images are being read from
+resources on the UI thread which can lead to an application hanging and being force closed. Using an
+{@link android.os.AsyncTask} as described in the <a href="process-bitmap.html">Processing Bitmaps Off
+the UI Thread</a> lesson, it’s straightforward to move image loading and processing to a background
+thread:</p>
+
+<pre>
+public class ImageDetailActivity extends FragmentActivity {
+ ...
+
+ public void loadBitmap(int resId, ImageView imageView) {
+ mImageView.setImageResource(R.drawable.image_placeholder);
+ BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
+ task.execute(resId);
+ }
+
+ ... // include <a href="process-bitmap.html#BitmapWorkerTask">{@code BitmapWorkerTask}</a> class
+}
+
+public class ImageDetailFragment extends Fragment {
+ ...
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ if (ImageDetailActivity.class.isInstance(getActivity())) {
+ final int resId = ImageDetailActivity.imageResIds[mImageNum];
+ // Call out to ImageDetailActivity to load the bitmap in a background thread
+ ((ImageDetailActivity) getActivity()).loadBitmap(resId, mImageView);
+ }
+ }
+}
+</pre>
+
+<p>Any additional processing (such as resizing or fetching images from the network) can take place
+in the <a href="process-bitmap.html#BitmapWorkerTask">{@code BitmapWorkerTask}</a> without affecting
+responsiveness of the main UI. If the background thread is doing more than just loading an image
+directly from disk, it can also be beneficial to add a memory and/or disk cache as described in the
+lesson <a href="cache-bitmap.html#memory-cache">Caching Bitmaps</a>. Here's the additional
+modifications for a memory cache:</p>
+
+<pre>
+public class ImageDetailActivity extends FragmentActivity {
+ ...
+ private LruCache<String, Bitmap> mMemoryCache;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ ...
+ // initialize LruCache as per <a href="cache-bitmap.html#memory-cache">Use a Memory Cache</a> section
+ }
+
+ public void loadBitmap(int resId, ImageView imageView) {
+ final String imageKey = String.valueOf(resId);
+
+ final Bitmap bitmap = mMemoryCache.get(imageKey);
+ if (bitmap != null) {
+ mImageView.setImageBitmap(bitmap);
+ } else {
+ mImageView.setImageResource(R.drawable.image_placeholder);
+ BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
+ task.execute(resId);
+ }
+ }
+
+ ... // include updated BitmapWorkerTask from <a href="cache-bitmap.html#memory-cache">Use a Memory Cache</a> section
+}
+</pre>
+
+<p>Putting all these pieces together gives you a responsive {@link
+android.support.v4.view.ViewPager} implementation with minimal image loading latency and the ability
+to do as much or as little background processing on your images as needed.</p>
+
+<h2 id="gridview">Load Bitmaps into a GridView Implementation</h2>
+
+<p>The <a href="{@docRoot}design/building-blocks/grid-lists.html">grid list building block</a> is
+useful for showing image data sets and can be implemented using a {@link android.widget.GridView}
+component in which many images can be on-screen at any one time and many more need to be ready to
+appear if the user scrolls up or down. When implementing this type of control, you must ensure the
+UI remains fluid, memory usage remains under control and concurrency is handled correctly (due to
+the way {@link android.widget.GridView} recycles its children views).</p>
+
+<p>To start with, here is a standard {@link android.widget.GridView} implementation with {@link
+android.widget.ImageView} children placed inside a {@link android.app.Fragment}:</p>
+
+<pre>
+public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
+ private ImageAdapter mAdapter;
+
+ // A static dataset to back the GridView adapter
+ public final static Integer[] imageResIds = new Integer[] {
+ R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
+ R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
+ R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};
+
+ // Empty constructor as per Fragment docs
+ public ImageGridFragment() {}
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mAdapter = new ImageAdapter(getActivity());
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final View v = inflater.inflate(R.layout.image_grid_fragment, container, false);
+ final GridView mGridView = (GridView) v.findViewById(R.id.gridView);
+ mGridView.setAdapter(mAdapter);
+ mGridView.setOnItemClickListener(this);
+ return v;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+ final Intent i = new Intent(getActivity(), ImageDetailActivity.class);
+ i.putExtra(ImageDetailActivity.EXTRA_IMAGE, position);
+ startActivity(i);
+ }
+
+ private class ImageAdapter extends BaseAdapter {
+ private final Context mContext;
+
+ public ImageAdapter(Context context) {
+ super();
+ mContext = context;
+ }
+
+ @Override
+ public int getCount() {
+ return imageResIds.length;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return imageResIds[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup container) {
+ ImageView imageView;
+ if (convertView == null) { // if it's not recycled, initialize some attributes
+ imageView = new ImageView(mContext);
+ imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ imageView.setLayoutParams(new GridView.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ } else {
+ imageView = (ImageView) convertView;
+ }
+ <strong>imageView.setImageResource(imageResIds[position]);</strong> // Load image into ImageView
+ return imageView;
+ }
+ }
+}
+</pre>
+
+<p>Once again, the problem with this implementation is that the image is being set in the UI thread.
+While this may work for small, simple images (due to system resource loading and caching), if any
+additional processing needs to be done, your UI grinds to a halt.</p>
+
+<p>The same asynchronous processing and caching methods from the previous section can be implemented
+here. However, you also need to wary of concurrency issues as the {@link android.widget.GridView}
+recycles its children views. To handle this, use the techniques discussed in the <a
+href="process-bitmap#concurrency">Processing Bitmaps Off the UI Thread</a> lesson. Here is the updated
+solution:</p>
+
+<pre>
+public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
+ ...
+
+ private class ImageAdapter extends BaseAdapter {
+ ...
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup container) {
+ ...
+ <strong>loadBitmap(imageResIds[position], imageView)</strong>
+ return imageView;
+ }
+ }
+
+ public void loadBitmap(int resId, ImageView imageView) {
+ if (cancelPotentialWork(resId, imageView)) {
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable =
+ new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
+ imageView.setImageDrawable(asyncDrawable);
+ task.execute(resId);
+ }
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap,
+ BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference =
+ new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+ }
+
+ public static boolean cancelPotentialWork(int data, ImageView imageView) {
+ final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+ if (bitmapWorkerTask != null) {
+ final int bitmapData = bitmapWorkerTask.data;
+ if (bitmapData != data) {
+ // Cancel previous task
+ bitmapWorkerTask.cancel(true);
+ } else {
+ // The same work is already in progress
+ return false;
+ }
+ }
+ // No task associated with the ImageView, or an existing task was cancelled
+ return true;
+ }
+
+ private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+ if (imageView != null) {
+ final Drawable drawable = imageView.getDrawable();
+ if (drawable instanceof AsyncDrawable) {
+ final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+ return asyncDrawable.getBitmapWorkerTask();
+ }
+ }
+ return null;
+ }
+
+ ... // include updated <a href="process-bitmap.html#BitmapWorkerTaskUpdated">{@code BitmapWorkerTask}</a> class
+</pre>
+
+<p class="note"><strong>Note:</strong> The same code can easily be adapted to work with {@link
+android.widget.ListView} as well.</p>
+
+<p>This implementation allows for flexibility in how the images are processed and loaded without
+impeding the smoothness of the UI. In the background task you can load images from the network or
+resize large digital camera photos and the images appear as the tasks finish processing.</p>
+
+<p>For a full example of this and other concepts discussed in this lesson, please see the included
+sample application.</p>
diff --git a/docs/html/training/displaying-bitmaps/index.jd b/docs/html/training/displaying-bitmaps/index.jd
new file mode 100644
index 0000000..6755c24
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/index.jd
@@ -0,0 +1,78 @@
+page.title=Displaying Bitmaps Efficiently
+
+trainingnavtop=true
+startpage=true
+next.title=Loading Large Bitmaps Efficiently
+next.link=load-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+<ul>
+ <li>Android 2.1 (API Level 7) or higher</li>
+ <li><a href="{@docRoot}sdk/compatibility-library.html">Support Library</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+ <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>This class covers some common techniques for processing and loading {@link
+android.graphics.Bitmap} objects in a way that keeps your user interface (UI) components responsive
+and avoids exceeding your application memory limit. If you're not careful, bitmaps can quickly
+consume your available memory budget leading to an application crash due to the dreaded
+exception:<br />{@code java.lang.OutofMemoryError: bitmap size exceeds VM budget}.</p>
+
+<p>There are a number of reasons why loading bitmaps in your Android application is tricky:</p>
+
+<ul>
+ <li>Mobile devices typically have constrained system resources. Android devices can have as little
+ as 16MB of memory available to a single application. The <a
+ href="http://source.android.com/compatibility/downloads.html">Android Compatibility Definition
+ Document</a> (CDD), <i>Section 3.7. Virtual Machine Compatibility</i> gives the required minimum
+ application memory for various screen sizes and densities. Applications should be optimized to
+ perform under this minimum memory limit. However, keep in mind many devices are configured with
+ higher limits.</li>
+ <li>Bitmaps take up a lot of memory, especially for rich images like photographs. For example, the
+ camera on the <a href="http://www.google.com/nexus/">Galaxy Nexus</a> takes photos up to 2592x1936
+ pixels (5 megapixels). If the bitmap configuration used is {@link
+ android.graphics.Bitmap.Config ARGB_8888} (the default from the Android 2.3 onward) then loading
+ this image into memory takes about 19MB of memory (2592*1936*4 bytes), immediately exhausting the
+ per-app limit on some devices.</li>
+ <li>Android app UI’s frequently require several bitmaps to be loaded at once. Components such as
+ {@link android.widget.ListView}, {@link android.widget.GridView} and {@link
+ android.support.v4.view.ViewPager} commonly include multiple bitmaps on-screen at once with many
+ more potentially off-screen ready to show at the flick of a finger.</li>
+</ul>
+
+<h2>Lessons</h2>
+
+<dl>
+ <dt><b><a href="load-bitmap.html">Loading Large Bitmaps Efficiently</a></b></dt>
+ <dd>This lesson walks you through decoding large bitmaps without exceeding the per application
+ memory limit.</dd>
+
+ <dt><b><a href="process-bitmap.html">Processing Bitmaps Off the UI Thread</a></b></dt>
+ <dd>Bitmap processing (resizing, downloading from a remote source, etc.) should never take place
+ on the main UI thread. This lesson walks you through processing bitmaps in a background thread
+ using {@link android.os.AsyncTask} and explains how to handle concurrency issues.</dd>
+
+ <dt><b><a href="cache-bitmap.html">Caching Bitmaps</a></b></dt>
+ <dd>This lesson walks you through using a memory and disk bitmap cache to improve the
+ responsiveness and fluidity of your UI when loading multiple bitmaps.</dd>
+
+ <dt><b><a href="display-bitmap.html">Displaying Bitmaps in Your UI</a></b></dt>
+ <dd>This lesson brings everything together, showing you how to load multiple bitmaps into
+ components like {@link android.support.v4.view.ViewPager} and {@link android.widget.GridView}
+ using a background thread and bitmap cache.</dd>
+
+</dl>
\ No newline at end of file
diff --git a/docs/html/training/displaying-bitmaps/load-bitmap.jd b/docs/html/training/displaying-bitmaps/load-bitmap.jd
new file mode 100644
index 0000000..c0a5709
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/load-bitmap.jd
@@ -0,0 +1,165 @@
+page.title=Loading Large Bitmaps Efficiently
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Processing Bitmaps Off the UI Thread
+next.link=process-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#read-bitmap">Read Bitmap Dimensions and Type</a></li>
+ <li><a href="#load-bitmap">Load a Scaled Down Version into Memory</a></li>
+</ol>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+ <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Images come in all shapes and sizes. In many cases they are larger than required for a typical
+application user interface (UI). For example, the system Gallery application displays photos taken
+using your Android devices's camera which are typically much higher resolution than the screen
+density of your device.</p>
+
+<p>Given that you are working with limited memory, ideally you only want to load a lower resolution
+version in memory. The lower resolution version should match the size of the UI component that
+displays it. An image with a higher resolution does not provide any visible benefit, but still takes
+up precious memory and incurs additional performance overhead due to additional on the fly
+scaling.</p>
+
+<p>This lesson walks you through decoding large bitmaps without exceeding the per application
+memory limit by loading a smaller subsampled version in memory.</p>
+
+<h2 id="read-bitmap">Read Bitmap Dimensions and Type</h2>
+
+<p>The {@link android.graphics.BitmapFactory} class provides several decoding methods ({@link
+android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
+decodeByteArray()}, {@link
+android.graphics.BitmapFactory#decodeFile(java.lang.String,android.graphics.BitmapFactory.Options)
+decodeFile()}, {@link
+android.graphics.BitmapFactory#decodeResource(android.content.res.Resources,int,android.graphics.BitmapFactory.Options)
+decodeResource()}, etc.) for creating a {@link android.graphics.Bitmap} from various sources. Choose
+the most appropriate decode method based on your image data source. These methods attempt to
+allocate memory for the constructed bitmap and therefore can easily result in an {@code OutOfMemory}
+exception. Each type of decode method has additional signatures that let you specify decoding
+options via the {@link android.graphics.BitmapFactory.Options} class. Setting the {@link
+android.graphics.BitmapFactory.Options#inJustDecodeBounds} property to {@code true} while decoding
+avoids memory allocation, returning {@code null} for the bitmap object but setting {@link
+android.graphics.BitmapFactory.Options#outWidth}, {@link
+android.graphics.BitmapFactory.Options#outHeight} and {@link
+android.graphics.BitmapFactory.Options#outMimeType}. This technique allows you to read the
+dimensions and type of the image data prior to construction (and memory allocation) of the
+bitmap.</p>
+
+<pre>
+BitmapFactory.Options options = new BitmapFactory.Options();
+options.inJustDecodeBounds = true;
+BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
+int imageHeight = options.outHeight;
+int imageWidth = options.outWidth;
+String imageType = options.outMimeType;
+</pre>
+
+<p>To avoid {@code java.lang.OutOfMemory} exceptions, check the dimensions of a bitmap before
+decoding it, unless you absolutely trust the source to provide you with predictably sized image data
+that comfortably fits within the available memory.</p>
+
+<h2 id="load-bitmap">Load a Scaled Down Version into Memory</h2>
+
+<p>Now that the image dimensions are known, they can be used to decide if the full image should be
+loaded into memory or if a subsampled version should be loaded instead. Here are some factors to
+consider:</p>
+
+<ul>
+ <li>Estimated memory usage of loading the full image in memory.</li>
+ <li>Amount of memory you are willing to commit to loading this image given any other memory
+ requirements of your application.</li>
+ <li>Dimensions of the target {@link android.widget.ImageView} or UI component that the image
+ is to be loaded into.</li>
+ <li>Screen size and density of the current device.</li>
+</ul>
+
+<p>For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be
+displayed in a 128x96 pixel thumbnail in an {@link android.widget.ImageView}.</p>
+
+<p>To tell the decoder to subsample the image, loading a smaller version into memory, set {@link
+android.graphics.BitmapFactory.Options#inSampleSize} to {@code true} in your {@link
+android.graphics.BitmapFactory.Options} object. For example, an image with resolution 2048x1536 that
+is decoded with an {@link android.graphics.BitmapFactory.Options#inSampleSize} of 4 produces a
+bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full
+image (assuming a bitmap configuration of {@link android.graphics.Bitmap.Config ARGB_8888}). Here’s
+a method to calculate a the sample size value based on a target width and height:</p>
+
+<pre>
+public static int calculateInSampleSize(
+ BitmapFactory.Options options, int reqWidth, int reqHeight) {
+ // Raw height and width of image
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > reqHeight || width > reqWidth) {
+ if (width > height) {
+ inSampleSize = Math.round((float)height / (float)reqHeight);
+ } else {
+ inSampleSize = Math.round((float)width / (float)reqWidth);
+ }
+ }
+ return inSampleSize;
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> Using powers of 2 for {@link
+android.graphics.BitmapFactory.Options#inSampleSize} values is faster and more efficient for the
+decoder. However, if you plan to cache the resized versions in memory or on disk, it’s usually still
+worth decoding to the most appropriate image dimensions to save space.</p>
+
+<p>To use this method, first decode with {@link
+android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code true}, pass the options
+through and then decode again using the new {@link
+android.graphics.BitmapFactory.Options#inSampleSize} value and {@link
+android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code false}:</p>
+
+<a name="decodeSampledBitmapFromResource"></a>
+<pre>
+public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
+ int reqWidth, int reqHeight) {
+
+ // First decode with inJustDecodeBounds=true to check dimensions
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeResource(res, resId, options);
+
+ // Calculate inSampleSize
+ options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
+
+ // Decode bitmap with inSampleSize set
+ options.inJustDecodeBounds = false;
+ return BitmapFactory.decodeResource(res, resId, options);
+}
+</pre>
+
+<p>This method makes it easy to load a bitmap of arbitrarily large size into an {@link
+android.widget.ImageView} that displays a 100x100 pixel thumbnail, as shown in the following example
+code:</p>
+
+<pre>
+mImageView.setImageBitmap(
+ decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
+</pre>
+
+<p>You can follow a similar process to decode bitmaps from other sources, by substituting the
+appropriate {@link
+android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
+BitmapFactory.decode*} method as needed.</p>
\ No newline at end of file
diff --git a/docs/html/training/displaying-bitmaps/process-bitmap.jd b/docs/html/training/displaying-bitmaps/process-bitmap.jd
new file mode 100644
index 0000000..c1450b4
--- /dev/null
+++ b/docs/html/training/displaying-bitmaps/process-bitmap.jd
@@ -0,0 +1,239 @@
+page.title=Processing Bitmaps Off the UI Thread
+parent.title=Displaying Bitmaps Efficiently
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Caching Bitmaps
+next.link=cache-bitmap.html
+previous.title=Loading Large Bitmaps Efficiently
+previous.link=load-bitmap.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#async-task">Use an AsyncTask</a></li>
+ <li><a href="#concurrency">Handle Concurrency</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/practices/design/responsiveness.html">Designing for Responsiveness</a></li>
+ <li><a
+ href="http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html">Multithreading
+ for Performance</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+ <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
+ <p class="filename">BitmapFun.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>The {@link
+android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
+BitmapFactory.decode*} methods, discussed in the <a href="load-bitmap.html">Load Large Bitmaps
+Efficiently</a> lesson, should not be executed on the main UI thread if the source data is read from
+disk or a network location (or really any source other than memory). The time this data takes to
+load is unpredictable and depends on a variety of factors (speed of reading from disk or network,
+size of image, power of CPU, etc.). If one of these tasks blocks the UI thread, the system flags
+your application as non-responsive and the user has the option of closing it (see <a
+href="{@docRoot}guide/practices/design/responsiveness.html">Designing for Responsiveness</a> for
+more information).</p>
+
+<p>This lesson walks you through processing bitmaps in a background thread using
+{@link android.os.AsyncTask} and shows you how to handle concurrency issues.</p>
+
+<h2 id="async-task">Use an AsyncTask</h2>
+
+<p>The {@link android.os.AsyncTask} class provides an easy way to execute some work in a background
+thread and publish the results back on the UI thread. To use it, create a subclass and override the
+provided methods. Here’s an example of loading a large image into an {@link
+android.widget.ImageView} using {@link android.os.AsyncTask} and <a
+href="load-bitmap.html#decodeSampledBitmapFromResource">{@code
+decodeSampledBitmapFromResource()}</a>: </p>
+
+<a name="BitmapWorkerTask"></a>
+<pre>
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+ private final WeakReference<ImageView> imageViewReference;
+ private int data = 0;
+
+ public BitmapWorkerTask(ImageView imageView) {
+ // Use a WeakReference to ensure the ImageView can be garbage collected
+ imageViewReference = new WeakReference<ImageView>(imageView);
+ }
+
+ // Decode image in background.
+ @Override
+ protected Bitmap doInBackground(Integer... params) {
+ data = params[0];
+ return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
+ }
+
+ // Once complete, see if ImageView is still around and set bitmap.
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (imageViewReference != null && bitmap != null) {
+ final ImageView imageView = imageViewReference.get();
+ if (imageView != null) {
+ imageView.setImageBitmap(bitmap);
+ }
+ }
+ }
+}
+</pre>
+
+<p>The {@link java.lang.ref.WeakReference} to the {@link android.widget.ImageView} ensures that the
+{@link android.os.AsyncTask} does not prevent the {@link android.widget.ImageView} and anything it
+references from being garbage collected. There’s no guarantee the {@link android.widget.ImageView}
+is still around when the task finishes, so you must also check the reference in {@link
+android.os.AsyncTask#onPostExecute(Result) onPostExecute()}. The {@link android.widget.ImageView}
+may no longer exist, if for example, the user navigates away from the activity or if a
+configuration change happens before the task finishes.</p>
+
+<p>To start loading the bitmap asynchronously, simply create a new task and execute it:</p>
+
+<pre>
+public void loadBitmap(int resId, ImageView imageView) {
+ BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ task.execute(resId);
+}
+</pre>
+
+<h2 id="concurrency">Handle Concurrency</h2>
+
+<p>Common view components such as {@link android.widget.ListView} and {@link
+android.widget.GridView} introduce another issue when used in conjunction with the {@link
+android.os.AsyncTask} as demonstrated in the previous section. In order to be efficient with memory,
+these components recycle child views as the user scrolls. If each child view triggers an {@link
+android.os.AsyncTask}, there is no guarantee that when it completes, the associated view has not
+already been recycled for use in another child view. Furthermore, there is no guarantee that the
+order in which asynchronous tasks are started is the order that they complete.</p>
+
+<p>The blog post <a
+href="http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html">Multithreading
+for Performance</a> further discusses dealing with concurrency, and offers a solution where the
+{@link android.widget.ImageView} stores a reference to the most recent {@link android.os.AsyncTask}
+which can later be checked when the task completes. Using a similar method, the {@link
+android.os.AsyncTask} from the previous section can be extended to follow a similar pattern.</p>
+
+<p>Create a dedicated {@link android.graphics.drawable.Drawable} subclass to store a reference
+back to the worker task. In this case, a {@link android.graphics.drawable.BitmapDrawable} is used so
+that a placeholder image can be displayed in the {@link android.widget.ImageView} while the task
+completes:</p>
+
+<a name="AsyncDrawable"></a>
+<pre>
+static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ public AsyncDrawable(Resources res, Bitmap bitmap,
+ BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference =
+ new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
+ }
+
+ public BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+}
+</pre>
+
+<p>Before executing the <a href="#BitmapWorkerTask">{@code BitmapWorkerTask}</a>, you create an <a
+href="#AsyncDrawable">{@code AsyncDrawable}</a> and bind it to the target {@link
+android.widget.ImageView}:</p>
+
+<pre>
+public void loadBitmap(int resId, ImageView imageView) {
+ if (cancelPotentialWork(resId, imageView)) {
+ final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+ final AsyncDrawable asyncDrawable =
+ new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
+ imageView.setImageDrawable(asyncDrawable);
+ task.execute(resId);
+ }
+}
+</pre>
+
+<p>The {@code cancelPotentialWork} method referenced in the code sample above checks if another
+running task is already associated with the {@link android.widget.ImageView}. If so, it attempts to
+cancel the previous task by calling {@link android.os.AsyncTask#cancel cancel()}. In a small number
+of cases, the new task data matches the existing task and nothing further needs to happen. Here is
+the implementation of {@code cancelPotentialWork}:</p>
+
+<pre>
+public static boolean cancelPotentialWork(int data, ImageView imageView) {
+ final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+ if (bitmapWorkerTask != null) {
+ final int bitmapData = bitmapWorkerTask.data;
+ if (bitmapData != data) {
+ // Cancel previous task
+ bitmapWorkerTask.cancel(true);
+ } else {
+ // The same work is already in progress
+ return false;
+ }
+ }
+ // No task associated with the ImageView, or an existing task was cancelled
+ return true;
+}
+</pre>
+
+<p>A helper method, {@code getBitmapWorkerTask()}, is used above to retrieve the task associated
+with a particular {@link android.widget.ImageView}:</p>
+
+<pre>
+private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+ if (imageView != null) {
+ final Drawable drawable = imageView.getDrawable();
+ if (drawable instanceof AsyncDrawable) {
+ final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+ return asyncDrawable.getBitmapWorkerTask();
+ }
+ }
+ return null;
+}
+</pre>
+
+<p>The last step is updating {@code onPostExecute()} in <a href="#BitmapWorkerTask">{@code
+BitmapWorkerTask}</a> so that it checks if the task is cancelled and if the current task matches the
+one associated with the {@link android.widget.ImageView}:</p>
+
+<a name="BitmapWorkerTaskUpdated"></a>
+<pre>
+class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+ ...
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ <strong>if (isCancelled()) {
+ bitmap = null;
+ }</strong>
+
+ if (imageViewReference != null && bitmap != null) {
+ final ImageView imageView = imageViewReference.get();
+ <strong>final BitmapWorkerTask bitmapWorkerTask =
+ getBitmapWorkerTask(imageView);</strong>
+ if (<strong>this == bitmapWorkerTask &&</strong> imageView != null) {
+ imageView.setImageBitmap(bitmap);
+ }
+ }
+ }
+}
+</pre>
+
+<p>This implementation is now suitable for use in {@link android.widget.ListView} and {@link
+android.widget.GridView} components as well as any other components that recycle their child
+views. Simply call {@code loadBitmap} where you normally set an image to your {@link
+android.widget.ImageView}. For example, in a {@link android.widget.GridView} implementation this
+would be in the {@link android.widget.Adapter#getView getView()} method of the backing adapter.</p>
\ No newline at end of file
diff --git a/docs/html/training/location/currentlocation.jd b/docs/html/training/location/currentlocation.jd
new file mode 100644
index 0000000..4692530
--- /dev/null
+++ b/docs/html/training/location/currentlocation.jd
@@ -0,0 +1,155 @@
+page.title=Obtaining the Current Location
+parent.title=Making Your App Location Aware
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Using the Location Manager
+previous.link=locationmanager.html
+next.title=Displaying the Location Address
+next.link=geocoding.html
+
+
+@jd:body
+
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="currentlocation.html#TaskSetupLocationListener">Set Up the Location Listener</a></li>
+ <li><a href="currentlocation.html#TaskHandleLocationUpdates">Handle Multiple Sources of Location Updates</a></li>
+ <li><a href="currentlocation.html#TaskGetLastKnownLocation">Use getLastKnownLocation() Wisely</a></li>
+ <li><a href="currentlocation.html#TaskTerminateUpdates">Terminate Location Updates</a></li>
+</ol>
+
+<h2>You should also read</h2>
+
+<ul>
+ <li><a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/LocationAware.zip" class="button">Download
+ the sample app</a>
+<p class="filename">LocationAware.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>After setting up your application to work with {@link android.location.LocationManager}, you can begin to obtain location updates.</p>
+
+<h2 id="TaskSetupLocationListener">Set Up the Location Listener</h2>
+
+<p>The {@link android.location.LocationManager} class exposes a number of methods for applications to receive location updates. In its simplest form, you register an event listener, identify the location manager from which you'd like to receive location updates, and specify the minimum time and distance intervals at which to receive location updates. The {@link android.location.LocationListener#onLocationChanged(android.location.Location) onLocationChanged()} callback will be invoked with the frequency that correlates with time and distance intervals.</p>
+
+<p>
+In the sample code snippet below, the location listener is set up to receive notifications at least every 10 seconds and if the device moves by more than 10 meters. The other callback methods notify the application any status change coming from the location provider.
+</p>
+
+<pre>
+private final LocationListener listener = new LocationListener() {
+
+ @Override
+ public void onLocationChanged(Location location) {
+ // A new location update is received. Do something useful with it. In this case,
+ // we're sending the update to a handler which then updates the UI with the new
+ // location.
+ Message.obtain(mHandler,
+ UPDATE_LATLNG,
+ location.getLatitude() + ", " +
+ location.getLongitude()).sendToTarget();
+
+ ...
+ }
+ ...
+};
+
+mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
+ 10000, // 10-second interval.
+ 10, // 10 meters.
+ listener);
+</pre>
+
+<h2 id="TaskHandleLocationUpdates">Handle Multiple Sources of Location Updates</h2>
+
+<p>Generally speaking, a location provider with greater accuracy (GPS) requires a longer fix time than one with lower accuracy (network-based). If you want to display location data as quickly as possible and update it as more accurate data becomes available, a common practice is to register a location listener with both GPS and network providers. In the {@link android.location.LocationListener#onLocationChanged(android.location.Location) onLocationChanged()} callback, you'll receive location updates from multiple location providers that may have different timestamps and varying levels of accuracy. You'll need to incorporate logic to disambiguate the location providers and discard updates that are stale and less accurate. The code snippet below demonstrates a sample implementation of this logic.</p>
+
+<pre>
+private static final int TWO_MINUTES = 1000 * 60 * 2;
+
+/** Determines whether one Location reading is better than the current Location fix
+ * @param location The new Location that you want to evaluate
+ * @param currentBestLocation The current Location fix, to which you want to compare the new one
+ */
+protected boolean isBetterLocation(Location location, Location currentBestLocation) {
+ if (currentBestLocation == null) {
+ // A new location is always better than no location
+ return true;
+ }
+
+ // Check whether the new location fix is newer or older
+ long timeDelta = location.getTime() - currentBestLocation.getTime();
+ boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
+ boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
+ boolean isNewer = timeDelta > 0;
+
+ // If it's been more than two minutes since the current location, use the new location
+ // because the user has likely moved
+ if (isSignificantlyNewer) {
+ return true;
+ // If the new location is more than two minutes older, it must be worse
+ } else if (isSignificantlyOlder) {
+ return false;
+ }
+
+ // Check whether the new location fix is more or less accurate
+ int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
+ boolean isLessAccurate = accuracyDelta > 0;
+ boolean isMoreAccurate = accuracyDelta < 0;
+ boolean isSignificantlyLessAccurate = accuracyDelta > 200;
+
+ // Check if the old and new location are from the same provider
+ boolean isFromSameProvider = isSameProvider(location.getProvider(),
+ currentBestLocation.getProvider());
+
+ // Determine location quality using a combination of timeliness and accuracy
+ if (isMoreAccurate) {
+ return true;
+ } else if (isNewer && !isLessAccurate) {
+ return true;
+ } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
+ return true;
+ }
+ return false;
+}
+
+/** Checks whether two providers are the same */
+private boolean isSameProvider(String provider1, String provider2) {
+ if (provider1 == null) {
+ return provider2 == null;
+ }
+ return provider1.equals(provider2);
+}
+</pre>
+
+<h2 id="TaskGetLastKnownLocation">Use getLastKnownLocation() Wisely</h2>
+
+<p>The setup time for getting a reasonable location fix may not be acceptable for certain applications. You should consider calling the {@link android.location.LocationManager#getLastKnownLocation(java.lang.String) getLastKnownLocation()} method which simply queries Android for the last location update previously received by any location providers. Keep in mind that the returned location may be stale. You should check the timestamp and accuracy of the returned location and decide whether it is useful for your application. If you elect to discard the location update returned from {@link android.location.LocationManager#getLastKnownLocation(java.lang.String) getLastKnownLocation()} and wait for fresh updates from the location provider(s), you should consider displaying an appropriate message before location data is received.</p>
+
+<h2 id="TaskTerminateUpdates">Terminate Location Updates</h2>
+
+<p>When you are done with using location data, you should terminate location update to reduce unnecessary consumption of power and network bandwidth. For example, if the user navigates away from an activity where location updates are displayed, you should stop location update by calling {@link android.location.LocationManager#removeUpdates(android.location.LocationListener) removeUpdates()} in {@link android.app.Activity#onStop()}. ({@link android.app.Activity#onStop()} is called when the activity is no longer visible. If you want to learn more about activity lifecycle, read up on the <a href="/training/basic-activity-lifecycle/stopping.html">Starting and Stopping an Activity</a> lesson.</p>
+
+<pre>
+protected void onStop() {
+ super.onStop();
+ mLocationManager.removeUpdates(listener);
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> For applications that need to continuously receive and process location updates like a near-real time mapping application, it is best to incorporate the location update logic in a background service and make use of the system notification bar to make the user aware that location data is being used.</p>
\ No newline at end of file
diff --git a/docs/html/training/location/geocoding.jd b/docs/html/training/location/geocoding.jd
new file mode 100644
index 0000000..6364976
--- /dev/null
+++ b/docs/html/training/location/geocoding.jd
@@ -0,0 +1,98 @@
+page.title=Displaying the Location Address
+parent.title=Making Your App Location Aware
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Obtaining the Current Location
+previous.link=currentlocation.html
+
+@jd:body
+
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="geocoding.html#TaskReverseGeocoding">Perform Reverse Geocoding</a></li>
+</ol>
+
+<h2>You should also read</h2>
+
+<ul>
+ <li><a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/LocationAware.zip" class="button">Download
+ the sample app</a>
+<p class="filename">LocationAware.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>As shown in previous lessons, location updates are received in the form of latitude and longitude coordinates. While this format is useful for calculating distance or displaying a pushpin on a map, the decimal numbers make no sense to most end users. If you need to display a location to user, it is much more preferable to display the address instead.</p>
+
+<h2 id="TaskReverseGeocoding">Perform Reverse Geocoding</h2>
+
+<p>Reverse-geocoding is the process of translating latitude longitude coordinates to a human-readable address. The {@link android.location.Geocoder} API is available for this purpose. Note that behind the scene, the API is dependent on a web service. If such service is unavailable on the device, the API will throw a "Service not Available exception" or return an empty list of addresses. A helper method called {@link android.location.Geocoder#isPresent()} was added in Android 2.3 (API level 9) to check for the existence of the service.</p>
+
+<p>The following code snippet demonstrates the use of the {@link android.location.Geocoder} API to perform reverse-geocoding. Since the {@link android.location.Geocoder#getFromLocation(double, double, int) getFromLocation()} method is synchronous, you should not invoke it from the UI thread, hence an {@link android.os.AsyncTask} is used in the snippet.</p>
+
+<pre>
+private final LocationListener listener = new LocationListener() {
+
+ public void onLocationChanged(Location location) {
+ // Bypass reverse-geocoding if the Geocoder service is not available on the
+ // device. The isPresent() convenient method is only available on Gingerbread or above.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD && Geocoder.isPresent()) {
+ // Since the geocoding API is synchronous and may take a while. You don't want to lock
+ // up the UI thread. Invoking reverse geocoding in an AsyncTask.
+ (new ReverseGeocodingTask(this)).execute(new Location[] {location});
+ }
+ }
+ ...
+};
+
+// AsyncTask encapsulating the reverse-geocoding API. Since the geocoder API is blocked,
+// we do not want to invoke it from the UI thread.
+private class ReverseGeocodingTask extends AsyncTask<Location, Void, Void> {
+ Context mContext;
+
+ public ReverseGeocodingTask(Context context) {
+ super();
+ mContext = context;
+ }
+
+ @Override
+ protected Void doInBackground(Location... params) {
+ Geocoder geocoder = new Geocoder(mContext, Locale.getDefault());
+
+ Location loc = params[0];
+ List<Address> addresses = null;
+ try {
+ // Call the synchronous getFromLocation() method by passing in the lat/long values.
+ addresses = geocoder.getFromLocation(loc.getLatitude(), loc.getLongitude(), 1);
+ } catch (IOException e) {
+ e.printStackTrace();
+ // Update UI field with the exception.
+ Message.obtain(mHandler, UPDATE_ADDRESS, e.toString()).sendToTarget();
+ }
+ if (addresses != null &s;&s; addresses.size() > 0) {
+ Address address = addresses.get(0);
+ // Format the first line of address (if available), city, and country name.
+ String addressText = String.format("%s, %s, %s",
+ address.getMaxAddressLineIndex() > 0 ? address.getAddressLine(0) : "",
+ address.getLocality(),
+ address.getCountryName());
+ // Update the UI via a message handler.
+ Message.obtain(mHandler, UPDATE_ADDRESS, addressText).sendToTarget();
+ }
+ return null;
+ }
+}
+</pre>
\ No newline at end of file
diff --git a/docs/html/training/location/index.jd b/docs/html/training/location/index.jd
new file mode 100644
index 0000000..48cfbc3
--- /dev/null
+++ b/docs/html/training/location/index.jd
@@ -0,0 +1,51 @@
+page.title=Making Your App Location Aware
+
+trainingnavtop=true
+startpage=true
+next.title=Using the Location Manager
+next.link=locationmanager.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+
+<ul>
+ <li>Android 1.0 or higher (2.3+ for the sample app)</li>
+</ul>
+
+<h2>You should also read</h2>
+
+<ul>
+ <li><a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/LocationAware.zip" class="button">Download
+ the sample app</a>
+<p class="filename">LocationAware.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Users bring their mobile devices with them almost everywhere. One of the unique features available to mobile applications is location awareness. Knowing the location and using the information wisely can bring a more contextual experience to your users.</p>
+
+<p>This class teaches you how to incorporate location based services in your Android application. You'll learn a number of methods to receive location updates and related best practices.</p>
+
+<h2>Lessons</h2>
+
+<dl>
+ <dt><b><a href="locationmanager.html">Using the Location Manager</a></b></dt>
+ <dd>Learn how to set up your application before it can receive location updates in Android.</dd>
+
+ <dt><b><a href="currentlocation.html">Obtaining the Current Location</a></b></dt>
+ <dd>Learn how to work with underlying location technologies available on the platform to obtain current location.</dd>
+
+ <dt><b><a href="geocoding.html">Displaying a Location Address</a></b></dt>
+ <dd>Learn how to translate location coordinates into addresses that are readable to users.</dd>
+</dl>
diff --git a/docs/html/training/location/locationmanager.jd b/docs/html/training/location/locationmanager.jd
new file mode 100644
index 0000000..5da1205
--- /dev/null
+++ b/docs/html/training/location/locationmanager.jd
@@ -0,0 +1,90 @@
+page.title=Using the Location Manager
+parent.title=Making Your App Location Aware
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Obtaining the Current Location
+next.link=currentlocation.html
+
+@jd:body
+
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="locationmanager.html#TaskDeclarePermissions">Declare Proper Permissions in Android Manifest</a></li>
+ <li><a href="locationmanager.html#TaskGetLocationManagerRef">Get a Reference to LocationManager</a></li>
+ <li><a href="locationmanager.html#TaskPickLocationProvider">Pick a Location Provider</a></li>
+</ol>
+
+<h2>You should also read</h2>
+
+<ul>
+ <li><a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a></li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/LocationAware.zip" class="button">Download
+ the sample app</a>
+<p class="filename">LocationAware.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>Before your application can begin receiving location updates, it needs to perform some simple steps to set up access. In this lesson, you'll learn what these steps entail.</p>
+
+<h2 id="TaskDeclarePermissions">Declare Proper Permissions in Android Manifest</h2>
+
+<p>The first step of setting up location update access is to declare proper permissions in the manifest. If permissions are missing, the application will get a {@link java.lang.SecurityException} at runtime.</p>
+
+<p>Depending on the {@link android.location.LocationManager} methods used, either {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission is needed. For example, you need to declare the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} permission if your application uses a network-based location provider only. The more accurate GPS requires the {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+Note that declaring the {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission implies {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} already.</p>
+
+<p>Also, if a network-based location provider is used in the application, you'll need to declare the internet permission as well.</p>
+
+<pre>
+<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+<uses-permission android:name="android.permission.INTERNET" />
+</pre>
+
+<h2 id="TaskGetLocationManagerRef">Get a Reference to LocationManager</h2>
+
+<p>{@link android.location.LocationManager} is the main class through which your application can access location services on Android. Similar to other system services, a reference can be obtained from calling the {@link android.content.Context#getSystemService(java.lang.String) getSystemService()} method. If your application intends to receive location updates in the foreground (within an {@link android.app.Activity}), you should usually perform this step in the {@link android.app.Activity#onCreate(android.os.Bundle) onCreate()} method.</p>
+
+<pre>
+LocationManager locationManager =
+ (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
+</pre>
+
+<h2 id="TaskPickLocationProvider">Pick a Location Provider</h2>
+
+<p>While not required, most modern Android-powered devices can receive location updates through multiple underlying technologies, which are abstracted to an application as {@link android.location.LocationProvider} objects. Location providers may have different performance characteristics in terms of time-to-fix, accuracy, monetary cost, power consumption, and so on. Generally, a location provider with a greater accuracy, like the GPS, requires a longer fix time than a less accurate one, such as a network-based location provider.</p>
+
+<p>Depending on your application's use case, you have to choose a specific location provider, or multiple providers, based on similar tradeoffs. For example, a points of interest check-in application would require higher location accuracy than say, a retail store locator where a city level location fix would suffice. The snippet below asks for a provider backed by the GPS.</p>
+
+<pre>
+LocationProvider provider =
+ locationManager.getProvider(LocationManager.GPS_PROVIDER);
+</pre>
+
+<p>Alternatively, you can provide some input criteria such as accuracy, power requirement, monetary cost, and so on, and let Android decide a closest match location provider. The snippet below asks for a location provider with fine accuracy and no monetary cost. Note that the criteria may not resolve to any providers, in which case a null will be returned. Your application should be prepared to gracefully handle the situation.</p>
+
+<pre>
+// Retrieve a list of location providers that have fine accuracy, no monetary cost, etc
+Criteria criteria = new Criteria();
+criteria.setAccuracy(Criteria.ACCURACY_FINE);
+criteria.setCostAllowed(false);
+...
+String providerName = locManager.getBestProvider(criteria, true);
+
+// If no suitable provider is found, null is returned.
+if (providerName != null) {
+ ...
+}
+</pre>
diff --git a/docs/html/training/tv/index.jd b/docs/html/training/tv/index.jd
new file mode 100644
index 0000000..ae13c4a
--- /dev/null
+++ b/docs/html/training/tv/index.jd
@@ -0,0 +1,52 @@
+page.title=Designing for TV
+
+trainingnavtop=true
+startpage=true
+next.title=Optimizing layouts for TV
+next.link=optimizing-layouts-tv.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+<h2>Dependencies and prerequisites</h2>
+<ul>
+ <li>Android 2.0 (API Level 5) or higher</li>
+</ul>
+
+</div>
+</div>
+<p>
+ Smart TVs powered by Android bring your favorite Android apps to the best screen in your house.
+ Thousands of apps in the Google Play Store are already optimized for TVs. This class shows how
+ you can optimize your Android app for TVs, including how to build a layout that
+ works great when the user is ten feet away and navigating with a remote control.
+</p>
+
+<h2>Lessons</h2>
+
+<dl>
+ <dt><b><a href="optimizing-layouts-tv.html">Optimizing Layouts for TV</a></b></dt>
+ <dd>Shows you how to optimize app layouts for TV screens, which have some unique characteristics such as:
+ <ul>
+ <li>permanent "landscape" mode</li>
+ <li>high-resolution displays</li>
+ <li>"10 foot UI" environment.</li>
+ </ul>
+ </dd>
+
+ <dt><b><a href="optimizing-navigation-tv.html">Optimizing Navigation for TV</a></b></dt>
+ <dd>Shows you how to design navigation for TVs, including:
+ <ul>
+ <li>handling D-pad navigation</li>
+ <li>providing navigational feedback</li>
+ <li>providing easily-accessible controls on the screen.</li>
+ </ul>
+ </dd>
+
+ <dt><b><a href="unsupported-features-tv.html">Handling features not supported on TV</a></b></dt>
+ <dd>Lists the hardware features that are usually not available on TVs. This lesson also shows you how to
+ provide alternatives for missing features or check for missing features and disable code at run time.</dd>
+</dl>
\ No newline at end of file
diff --git a/docs/html/training/tv/optimizing-layouts-tv.jd b/docs/html/training/tv/optimizing-layouts-tv.jd
new file mode 100644
index 0000000..6eac6d3
--- /dev/null
+++ b/docs/html/training/tv/optimizing-layouts-tv.jd
@@ -0,0 +1,246 @@
+page.title=Optimizing Layouts for TV
+parent.title=Designing for TV
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Optimizing Navigation for TV
+next.link=optimizing-navigation-tv.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#DesignLandscapeLayouts">Design Landscape Layouts</a></li>
+ <li><a href="#MakeTextControlsEasyToSee">Make Text and Controls Easy to See</a></li>
+ <li><a href="#DesignForLargeScreens">Design for High-Density Large Screens</a></li>
+ <li><a href="#HandleLargeBitmaps">Handle Large Bitmaps in Your Application</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>
+When your application is running on a television set, you should assume that the user is sitting about
+ten feet away from the screen. This user environment is referred to as the
+<a href="http://en.wikipedia.org/wiki/10-foot_user_interface">10-foot UI</a>. To provide your
+users with a usable and enjoyable experience, you should style and lay out your UI accordingly..
+</p>
+<p>
+This lesson shows you how to optimize layouts for TV by:
+</p>
+<ul>
+ <li>Providing appropriate layout resources for landscape mode.</li>
+ <li>Ensuring that text and controls are large enough to be visible from a distance.</li>
+ <li>Providing high resolution bitmaps and icons for HD TV screens.</li>
+</ul>
+
+<h2 id="DesignLandscapeLayouts">Design Landscape Layouts</h2>
+
+<p>
+TV screens are always in landscape orientation. Follow these tips to build landscape layouts optimized for TV screens:
+</p>
+<ul>
+ <li>Put on-screen navigational controls on the left or right side of the screen and save the
+ vertical space for content.</li>
+ <li>Create UIs that are divided into sections, by using <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a>
+ and use view groups like {@link android.widget.GridView} instead
+ of {@link android.widget.ListView} to make better use of the
+ horizontal screen space.</li>
+ <li>Use view groups such as {@link android.widget.RelativeLayout}
+ or {@link android.widget.LinearLayout} to arrange views.
+ This allows the Android system to adjust the position of the views to the size, alignment,
+ aspect ratio, and pixel density of the TV screen.</li>
+ <li>Add sufficient margins between layout controls to avoid a cluttered UI.</li>
+</ul>
+
+<p>
+For example, the following layout is optimized for TV:
+</p>
+
+<img src="{@docRoot}images/training/panoramio-grid.png" />
+
+<p>
+In this layout, the controls are on the lefthand side. The UI is displayed within a
+{@link android.widget.GridView}, which is well-suited to landscape orientation.
+In this layout both GridView and Fragment have the width and height set
+dynamically, so they can adjust to the screen resolution. Controls are added to the left side Fragment programatically at runtime.
+The layout file for this UI is {@code res/layout-land-large/photogrid_tv.xml}.
+(This layout file is placed in {@code layout-land-large} because TVs have large screens with landscape orientation. For details refer to
+<a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>.)</p>
+
+res/layout-land-large/photogrid_tv.xml
+<pre>
+<RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+ <fragment
+ android:id="@+id/leftsidecontrols"
+ android:layout_width="0dip"
+ android:layout_marginLeft="5dip"
+ android:layout_height="match_parent" />
+
+ <GridView
+ android:id="@+id/gridview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+</RelativeLayout>
+</pre>
+
+<p>
+To set up action bar items on the left side of the screen, you can also include the <a
+href="http://code.google.com/p/googletv-android-samples/source/browse/#git%2FLeftNavBarLibrary">
+Left navigation bar library</a> in your application to set up action items on the left side
+of the screen, instead of creating a custom Fragment to add controls:
+</p>
+
+<pre>
+LeftNavBar bar = (LeftNavBarService.instance()).getLeftNavBar(this);
+</pre>
+
+<p>
+When you have an activity in which the content scrolls vertically, always use a left navigation bar;
+otherwise, your users have to scroll to the top of the content to switch between the content view and
+the ActionBar. Look at the
+<a href="http://code.google.com/p/googletv-android-samples/source/browse/#git%2FLeftNavBarDemo">
+Left navigation bar sample app</a> to see how to simple it is to include the left navigation bar in your app.
+</p>
+
+<h2 id="MakeTextControlsEasyToSee">Make Text and Controls Easy to See</h2>
+<p>
+The text and controls in a TV application's UI should be easily visible and navigable from a distance.
+Follow these tips to make them easier to see from a distance :
+</p>
+
+<ul>
+ <li>Break text into small chunks that users can quickly scan.</li>
+ <li>Use light text on a dark background. This style is easier to read on a TV.</li>
+ <li>Avoid lightweight fonts or fonts that have both very narrow and very broad strokes. Use simple sans-serif
+ fonts and use anti-aliasing to increase readability.</li>
+ <li>Use Android's standard font sizes:
+ <pre>
+ <TextView
+ android:id="@+id/atext"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+ </pre></li>
+ <li>Ensure that all your view widgets are large enough to be clearly visible to someone sitting 10 feet away
+ from the screen (this distance is greater for very large screens). The best way to do this is to use
+ layout-relative sizing rather than absolute sizing, and density-independent pixel units instead of absolute
+ pixel units. For example, to set the width of a widget, use wrap_content instead of a pixel measurement,
+ and to set the margin for a widget, use dip instead of px values.
+ </li>
+</ul>
+<p>
+
+</p>
+
+<h2 id="DesignForLargeScreens">Design for High-Density Large Screens</h2>
+
+<p>
+The common HDTV display resolutions are 720p, 1080i, and 1080p. Design your UI for 1080p, and then
+allow the Android system to downscale your UI to 720p if necessary. In general, downscaling (removing pixels)
+does not degrade the UI (Notice that the converse is not true; you should avoid upscaling because it degrades
+UI quality).
+</p>
+
+<p>
+To get the best scaling results for images, provide them as <a href="{@docRoot}guide/developing/tools/draw9patch.html">
+9-patch image</a> elements if possible.
+If you provide low quality or small images in your layouts, they will appear pixelated, fuzzy, or grainy. This
+is not a good experience for the user. Instead, use high-quality images.
+</p>
+
+<p>
+For more information on optimizing apps for large screens see <a href="{@docRoot}training/multiscreen/index.html">
+Designing for multiple screens</a>.
+</p>
+
+<h2 id="HandleLargeBitmaps">Design to Handle Large Bitmaps</h2>
+
+<p>
+The Android system has a limited amount of memory, so downloading and storing high-resolution images can often
+cause out-of-memory errors in your app. To avoid this, follow these tips:
+</p>
+
+<ul>
+ <li>Load images only when they're displayed on the screen. For example, when displaying multiple images in
+ a {@link android.widget.GridView} or
+ {@link android.widget.Gallery}, only load an image when
+ {@link android.widget.Adapter#getView(int, View, ViewGroup) getView()}
+ is called on the View's {@link android.widget.Adapter}.
+ </li>
+ <li>Call {@link android.graphics.Bitmap#recycle()} on
+ {@link android.graphics.Bitmap} views that are no longer needed.
+ </li>
+ <li>Use {@link java.lang.ref.WeakReference} for storing references
+ to {@link android.graphics.Bitmap} objects in a in-memory
+ <a href="{@link java.util.Collection}.</li>
+ <li>If you fetch images from the network, use {@link android.os.AsyncTask}
+ to fetch them and store them on the SD card for faster access.
+ Never do network transactions on the application's UI thread.
+ </li>
+ <li>Scale down really large images to a more appropriate size as you download them; otherwise, downloading the image
+ itself may cause an "Out of Memory" exception. Here is sample code that scales down images while downloading:
+
+ <pre>
+ // Get the source image's dimensions
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ // This does not download the actual image, just downloads headers.
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
+ // The actual width of the image.
+ int srcWidth = options.outWidth;
+ // The actual height of the image.
+ int srcHeight = options.outHeight;
+
+ // Only scale if the source is bigger than the width of the destination view.
+ if(desiredWidth > srcWidth)
+ desiredWidth = srcWidth;
+
+ // Calculate the correct inSampleSize/scale value. This helps reduce memory use. It should be a power of 2.
+ int inSampleSize = 1;
+ while(srcWidth / 2 > desiredWidth){
+ srcWidth /= 2;
+ srcHeight /= 2;
+ inSampleSize *= 2;
+ }
+
+ float desiredScale = (float) desiredWidth / srcWidth;
+
+ // Decode with inSampleSize
+ options.inJustDecodeBounds = false;
+ options.inDither = false;
+ options.inSampleSize = inSampleSize;
+ options.inScaled = false;
+ // Ensures the image stays as a 32-bit ARGB_8888 image.
+ // This preserves image quality.
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+
+ Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
+
+ // Resize
+ Matrix matrix = new Matrix();
+ matrix.postScale(desiredScale, desiredScale);
+ Bitmap scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0,
+ sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
+ sampledSrcBitmap = null;
+
+ // Save
+ FileOutputStream out = new FileOutputStream(LOCAL_PATH_TO_STORE_IMAGE);
+ scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
+ scaledBitmap = null;
+ </pre>
+ </li> </ul>
\ No newline at end of file
diff --git a/docs/html/training/tv/optimizing-navigation-tv.jd b/docs/html/training/tv/optimizing-navigation-tv.jd
new file mode 100644
index 0000000..8b5878e
--- /dev/null
+++ b/docs/html/training/tv/optimizing-navigation-tv.jd
@@ -0,0 +1,206 @@
+page.title=Optimizing Navigation for TV
+parent.title=Designing for TV
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Optimizing Layouts for TV
+previous.link=optimizing-layouts-tv.html
+next.title=Handling features not supported on TV
+next.link=unsupported-features-tv.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#HandleDpadNavigation">Handle D-pad Navigation</a></li>
+ <li><a href="#HandleFocusSelection">Provide Clear Visual Indication for Focus and Selection</a></li>
+ <li><a href="#DesignForEasyNavigation">Design for Easy Navigation</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>
+An important aspect of the user experience when operating a TV is the direct human interface: a remote control.
+As you optimize your Android application for TVs, you should pay special attention to how the user actually navigates
+around your application when using a remote control instead of a touchscreen.
+</p>
+<p>
+This lesson shows you how to optimize navigation for TV by:
+</p>
+
+<ul>
+ <li>Ensuring all layout controls are D-pad navigable.</li>
+ <li>Providing highly obvious feedback for UI navigation.</li>
+ <li>Placing layout controls for easy access.</li>
+</ul>
+
+<h2 id="HandleDpadNavigation">Handle D-pad Navigation</h2>
+
+<p>
+On a TV, users navigate with controls on a TV remote, using either a D-pad or arrow keys.
+This limits movement to up, down, left, and right.
+To build a great TV-optimized app, you must provide a navigation scheme in which the user can
+quickly learn how to navigate your app using the remote.
+</p>
+
+<p>
+When you design navigation for D-pad, follow these guidelines:
+</p>
+
+<ul>
+ <li>Ensure that the D-pad can navigate to all the visible controls on the screen.</li>
+ <li>For scrolling lists with focus, D-pad up/down keys scroll the list and Enter key selects an item in the list. Ensure that users can
+ select an element in the list and that the list still scrolls when an element is selected.</li>
+ <li>Ensure that movement between controls is straightforward and predictable.</li>
+</ul>
+
+<p>
+Android usually handles navigation order between layout elements automatically, so you don't need to do anything extra. If the screen layout
+makes navigation difficult, or if you want users to move through the layout in a specific way, you can set up explicit navigation for your
+controls.
+For example, for an {@code android.widget.EditText}, to define the next control to receive focus, use:
+<pre>
+<EditText android:id="@+id/LastNameField" android:nextFocusDown="@+id/FirstNameField"\>
+</pre>
+The following table lists all of the available navigation attributes:
+</p>
+
+<table>
+<tr>
+<th>Attribute</th>
+<th>Function</th>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusDown}</td>
+<td>Defines the next view to receive focus when the user navigates down.</td>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusLeft}</td>
+<td>Defines the next view to receive focus when the user navigates left.</td>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusRight}</td>
+<td>Defines the next view to receive focus when the user navigates right.</td>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusUp}</td>
+<td>Defines the next view to receive focus when the user navigates up.</td>
+</tr>
+</table>
+
+<p>
+To use one of these explicit navigation attributes, set the value to the ID (android:id value) of another widget in the layout. You should set
+up the navigation order as a loop, so that the last control directs focus back to the first one.
+</p>
+
+<p>
+Note: You should only use these attributes to modify the navigation order if the default order that the system applies does not work well.
+</p>
+
+<h2 id="HandleFocusSelection">Provide Clear Visual Indication for Focus and Selection</h2>
+
+<p>
+Use appropriate color highlights for all navigable and selectable elements in the UI. This makes it easy for users to know whether the control
+is currently focused or selected when they navigate with a D-pad. Also, use uniform highlight scheme across your application.
+</p>
+
+<p>
+Android provides <a href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">Drawable State List Resources</a> to implement highlights
+for selected and focused controls. For example:
+</p>
+
+res/drawable/button.xml:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true"
+ android:drawable="@drawable/button_pressed" /> <!-- pressed -->
+ <item android:state_focused="true"
+ android:drawable="@drawable/button_focused" /> <!-- focused -->
+ <item android:state_hovered="true"
+ android:drawable="@drawable/button_focused" /> <!-- hovered -->
+ <item android:drawable="@drawable/button_normal" /> <!-- default -->
+</selector>
+</pre>
+
+<p>
+This layout XML applies the above state list drawable to a {@link android.widget.Button}:
+</p>
+<pre>
+<Button
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:background="@drawable/button" />
+</pre>
+
+<p>
+Provide sufficient padding within the focusable and selectable controls so that the highlights around them are clearly visible.
+</p>
+
+<h2 id="DesignForEasyNavigation">Design for Easy Navigation</h2>
+
+<p>
+Users should be able to navigate to any UI control with a couple of D-pad clicks. Navigation should be easy and intuitive to
+understand. For any non-intuitive actions, provide users with written help, using a dialog triggered by a help button or action bar icon.
+</p>
+
+<p>
+Predict the next screen that the user will want to navigate to and provide one click navigation to it. If the current screen UI is very sparse,
+consider making it a multi pane screen. Use fragments for making multi-pane screens. For example, consider the multi-pane UI below with continent names
+on the left and list of cool places in each continent on the right.
+</p>
+
+<img src="{@docRoot}images/training/cool-places.png" alt="" />
+
+<p>
+The above UI consists of three Fragments - <code>left_side_action_controls</code>, <code>continents</code> and
+<code>places</code> - as shown in its layout
+xml file below. Such multi-pane UIs make D-pad navigation easier and make good use of the horizontal screen space for
+TVs.
+</p>
+res/layout/cool_places.xml
+<pre>
+<LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ >
+ <fragment
+ android:id="@+id/left_side_action_controls"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="10dip"
+ android:layout_weight="0.2"/>
+ <fragment
+ android:id="@+id/continents"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="10dip"
+ android:layout_weight="0.2"/>
+
+ <fragment
+ android:id="@+id/places"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="10dip"
+ android:layout_weight="0.6"/>
+
+</LinearLayout>
+</pre>
+
+<p>
+Also, notice in the UI layout above action controls are on the left hand side of a vertically scrolling list to make
+them easily accessible using D-pad.
+In general, for layouts with horizontally scrolling components, place action controls on left or right hand side and
+vice versa for vertically scrolling components.
+</p>
+
diff --git a/docs/html/training/tv/unsupported-features-tv.jd b/docs/html/training/tv/unsupported-features-tv.jd
new file mode 100644
index 0000000..6b0f8c8
--- /dev/null
+++ b/docs/html/training/tv/unsupported-features-tv.jd
@@ -0,0 +1,156 @@
+page.title=Handling Features Not Supported on TV
+parent.title=Designing for TV
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Optimizing Navigation for TV
+previous.link=optimizing-navigation-tv.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#WorkaroundUnsupportedFeatures">Work Around Features Not Supported on TV</a></li>
+ <li><a href="#CheckAvailableFeatures">Check for Available Features at Runtime</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>
+TVs are much different from other Android-powered devices:
+</p>
+<ul>
+ <li>They're not mobile.</li>
+ <li>Out of habit, people use them for watching media with little or no interaction.</li>
+ <li>People interact with them from a distance.</li>
+</ul>
+
+<p>
+Because TVs have a different purpose from other devices, they usually don't have hardware features
+that other Android-powered devices often have. For this reason, the Android system does not
+support the following features for a TV device:
+<table>
+<tr>
+<th>Hardware</th>
+<th>Android feature descriptor</th>
+</tr>
+<tr>
+<td>Camera</td>
+<td>android.hardware.camera</td>
+</tr>
+<tr>
+<td>GPS</td>
+<td>android.hardware.location.gps</td>
+</tr>
+<tr>
+<td>Microphone</td>
+<td>android.hardware.microphone</td>
+</tr>
+<tr>
+<td>Near Field Communications (NFC)</td>
+<td>android.hardware.nfc</td>
+</tr>
+<tr>
+<td>Telephony</td>
+<td>android.hardware.telephony</td>
+</tr>
+<tr>
+<td>Touchscreen</td>
+<td>android.hardware.touchscreen</td>
+</tr>
+</table>
+</p>
+
+<p>
+This lesson shows you how to work around features that are not available on TV by:
+<ul>
+ <li>Providing work arounds for some non-supported features.</li>
+ <li>Checking for available features at runtime and conditionally activating/deactivating certain code
+ paths based on availability of those features.</li>
+</ul>
+</p>
+
+
+<h2 id="WorkaroundUnsupportedFeatures">Work Around Features Not Supported on TV</h2>
+
+<p>
+Android doesn't support touchscreen interaction for TV devices, most TVs don't have touch screens,
+and interacting with a TV using a touchscreen is not consistent with the 10 foot environment. For
+these reasons, users interact with Android-powered TVs using a remote. In consideration of this,
+ensure that every control in your app can be accessed with the D-pad. Refer back to the previous two lessons
+<a href="{@docRoot}training/tv/optimizing-layouts-tv">Optimizing Layouts for TV</a> and
+<a href="{@docRoot}training/tv/optimizing-navigation-tv">Optimize Navigation for TV</a> for more details
+on this topic. The Android system assumes that a device has a touchscreen, so if you want your application
+to run on a TV, you must <strong>explicitly</strong> disable the touchscreen requirement in your manifest file:
+<pre>
+<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
+</pre>
+</p>
+
+<p>
+Although a TV doesn't have a camera, you can still provide a photography-related application on a TV.
+For example, if you have an app that takes, views and edits photos, you can disable its picture-taking
+functionality for TVs and still allow users to view and even edit photos. The next section talks about how to
+deactivate or activate specific functions in the application based on runtime device type detection.
+</p>
+
+<p>
+Because TVs are stationary, indoor devices, they don't have built-in GPS. If your application uses location
+information, allow users to search for a location or use a "static" location provider to get
+a location from the zip code configured during the TV setup.
+<pre>
+LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
+Location location = locationManager.getLastKnownLocation("static");
+Geocoder geocoder = new Geocoder(this);
+Address address = null;
+
+try {
+ address = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1).get(0);
+ Log.d("Zip code", address.getPostalCode());
+
+} catch (IOException e) {
+ Log.e(TAG, "Geocoder error", e);
+}
+</pre>
+</p>
+
+<p>
+TVs usually don't support microphones, but if you have an application that uses voice control,
+you can create a mobile device app that takes voice input and then acts as a remote control for a TV.
+</p>
+
+<h2 id="CheckAvailableFeatures">Check for Available Features at Runtime</h2>
+
+<p>
+To check if a feature is available at runtime, call
+{@link android.content.pm.PackageManager#hasSystemFeature(String)}.
+ This method takes a single argument : a string corresponding to the
+feature you want to check. For example, to check for touchscreen, use
+{@link android.content.pm.PackageManager#hasSystemFeature(String)} with the argument
+{@link android.content.pm.PackageManager#FEATURE_TOUCHSCREEN}.
+</p>
+
+<p>
+The following code snippet demonstrates how to detect device type at runtime based on supported features:
+
+<pre>
+// Check if android.hardware.telephony feature is available.
+if (getPackageManager().hasSystemFeature("android.hardware.telephony")) {
+ Log.d("Mobile Test", "Running on phone");
+// Check if android.hardware.touchscreen feature is available.
+} else if (getPackageManager().hasSystemFeature("android.hardware.touchscreen")) {
+ Log.d("Tablet Test", "Running on devices that don't support telphony but have a touchscreen.");
+} else {
+ Log.d("TV Test", "Running on a TV!");
+}
+</pre>
+</p>
+
+<p>
+This is just one example of using runtime checks to deactivate app functionality that depends on features
+that aren't available on TVs.
+</p>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 6f939be..ed5b2f6 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -16,9 +16,12 @@
package android.graphics;
+import android.os.Debug;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DisplayMetrics;
+import android.util.Log;
+
import java.io.OutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
@@ -57,6 +60,7 @@
private final boolean mIsMutable;
private byte[] mNinePatchChunk; // may be null
+ private int[] mLayoutBounds; // may be null
private int mWidth = -1;
private int mHeight = -1;
private boolean mRecycled;
@@ -95,6 +99,19 @@
*/
/*package*/ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
int density) {
+ this(nativeBitmap, buffer, isMutable, ninePatchChunk, null, density);
+ }
+
+ /**
+ * @noinspection UnusedDeclaration
+ */
+ /* Private constructor that must received an already allocated native
+ bitmap int (pointer).
+
+ This can be called from JNI code.
+ */
+ /*package*/ Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
+ int[] layoutBounds, int density) {
if (nativeBitmap == 0) {
throw new RuntimeException("internal error: native bitmap is 0");
}
@@ -106,6 +123,7 @@
mIsMutable = isMutable;
mNinePatchChunk = ninePatchChunk;
+ mLayoutBounds = layoutBounds;
if (density >= 0) {
mDensity = density;
}
@@ -164,6 +182,16 @@
}
/**
+ * Sets the layout bounds as an array of left, top, right, bottom integers
+ * @param padding the array containing the padding values
+ *
+ * @hide
+ */
+ public void setLayoutBounds(int[] bounds) {
+ mLayoutBounds = bounds;
+ }
+
+ /**
* Free the native object associated with this bitmap, and clear the
* reference to the pixel data. This will not free the pixel data synchronously;
* it simply allows it to be garbage collected if there are no other references.
@@ -690,6 +718,14 @@
}
/**
+ * @hide
+ * @return the layout padding [left, right, top, bottom]
+ */
+ public int[] getLayoutBounds() {
+ return mLayoutBounds;
+ }
+
+ /**
* Specifies the known formats a bitmap can be compressed into
*/
public enum CompressFormat {
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index c5705f6..1599e40 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -424,6 +424,7 @@
throw new ArrayIndexOutOfBoundsException();
}
Bitmap bm = nativeDecodeByteArray(data, offset, length, opts);
+
if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
@@ -554,7 +555,6 @@
if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
return bm;
}
-
byte[] np = bm.getNinePatchChunk();
final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
if (opts.inScaled || isNinePatch) {
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index dcda67d..7e92973 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -40,14 +40,8 @@
// assigned in constructors, freed in finalizer
final int mNativeCanvas;
- /* Our native canvas can be either a raster, gl, or picture canvas.
- If we are raster, then mGL will be null, and mBitmap may or may not be
- present (our default constructor creates a raster canvas but no
- java-bitmap is). If we are a gl-based, then mBitmap will be null, and
- mGL will not be null. Thus both cannot be non-null, but its possible
- for both to be null.
- */
- private Bitmap mBitmap; // if not null, mGL must be null
+ // may be null
+ private Bitmap mBitmap;
// optional field set by the caller
private DrawFilter mDrawFilter;
@@ -66,7 +60,7 @@
// Used by native code
@SuppressWarnings({"UnusedDeclaration"})
- private int mSurfaceFormat;
+ private int mSurfaceFormat;
/**
* Flag for drawTextRun indicating left-to-right run direction.
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 043adae..86e824b 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -773,7 +773,13 @@
np = null;
pad = null;
}
- return drawableFromBitmap(res, bm, np, pad, srcName);
+ int[] layoutBounds = bm.getLayoutBounds();
+ Rect layoutBoundsRect = null;
+ if (layoutBounds != null) {
+ layoutBoundsRect = new Rect(layoutBounds[0], layoutBounds[1],
+ layoutBounds[2], layoutBounds[3]);
+ }
+ return drawableFromBitmap(res, bm, np, pad, layoutBoundsRect, srcName);
}
return null;
}
@@ -875,7 +881,7 @@
Bitmap bm = BitmapFactory.decodeFile(pathName);
if (bm != null) {
- return drawableFromBitmap(null, bm, null, null, pathName);
+ return drawableFromBitmap(null, bm, null, null, null, pathName);
}
return null;
@@ -956,10 +962,12 @@
}
private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np,
- Rect pad, String srcName) {
+ Rect pad, Rect layoutBounds, String srcName) {
if (np != null) {
- return new NinePatchDrawable(res, bm, np, pad, srcName);
+ NinePatchDrawable npd = new NinePatchDrawable(res, bm, np, pad, srcName);
+ npd.setLayoutBounds(layoutBounds);
+ return npd;
}
return new BitmapDrawable(res, bm);
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 18b8bc7..1272071 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -47,6 +47,7 @@
private NinePatchState mNinePatchState;
private NinePatch mNinePatch;
private Rect mPadding;
+ private Rect mLayoutBounds;
private Paint mPaint;
private boolean mMutated;
@@ -98,6 +99,13 @@
mNinePatchState.mTargetDensity = mTargetDensity;
}
+ /**
+ * @hide
+ */
+ void setLayoutBounds(Rect layoutBounds) {
+ mLayoutBounds = layoutBounds;
+ }
+
private void setNinePatchState(NinePatchState state, Resources res) {
mNinePatchState = state;
mNinePatch = state.mNinePatch;
@@ -258,7 +266,7 @@
}
options.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
- final Rect padding = new Rect();
+ final Rect padding = new Rect();
Bitmap bitmap = null;
try {
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 6b59b10..cd5300d 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -184,9 +184,9 @@
private int getIDSafe() {
if (mAdaptedAllocation != null) {
- return mAdaptedAllocation.getID();
+ return mAdaptedAllocation.getID(mRS);
}
- return getID();
+ return getID(mRS);
}
@@ -321,7 +321,7 @@
@Override
void updateFromNative() {
super.updateFromNative();
- int typeID = mRS.nAllocationGetType(getID());
+ int typeID = mRS.nAllocationGetType(getID(mRS));
if(typeID != 0) {
mType = new Type(typeID, mRS);
mType.updateFromNative();
@@ -365,13 +365,21 @@
* @hide
*
*/
- public void ioSendOutput() {
+ public void ioSend() {
if ((mUsage & USAGE_IO_OUTPUT) == 0) {
throw new RSIllegalArgumentException(
"Can only send buffer if IO_OUTPUT usage specified.");
}
mRS.validate();
- mRS.nAllocationIoSend(getID());
+ mRS.nAllocationIoSend(getID(mRS));
+ }
+
+ /**
+ * Delete once code is updated.
+ * @hide
+ */
+ public void ioSendOutput() {
+ ioSend();
}
/**
@@ -380,13 +388,13 @@
* @hide
*
*/
- public void ioGetInput() {
+ public void ioReceive() {
if ((mUsage & USAGE_IO_INPUT) == 0) {
throw new RSIllegalArgumentException(
"Can only receive if IO_INPUT usage specified.");
}
mRS.validate();
- mRS.nAllocationIoReceive(getID());
+ mRS.nAllocationIoReceive(getID(mRS));
}
/**
@@ -403,7 +411,7 @@
}
int i[] = new int[d.length];
for (int ct=0; ct < d.length; ct++) {
- i[ct] = d[ct].getID();
+ i[ct] = d[ct].getID(mRS);
}
copy1DRangeFromUnchecked(0, mCurrentCount, i);
}
@@ -563,7 +571,7 @@
mRS.validate();
validateBitmapSize(b);
validateBitmapFormat(b);
- mRS.nAllocationCopyFromBitmap(getID(), b);
+ mRS.nAllocationCopyFromBitmap(getID(mRS), b);
}
/**
@@ -644,7 +652,7 @@
* followup sync will be required.
*/
public void generateMipmaps() {
- mRS.nAllocationGenerateMipmaps(getID());
+ mRS.nAllocationGenerateMipmaps(getID(mRS));
}
/**
@@ -772,7 +780,7 @@
public void copy1DRangeFrom(int off, int count, Allocation data, int dataOff) {
mRS.nAllocationData2D(getIDSafe(), off, 0,
mSelectedLOD, mSelectedFace.mID,
- count, 1, data.getID(), dataOff, 0,
+ count, 1, data.getID(mRS), dataOff, 0,
data.mSelectedLOD, data.mSelectedFace.mID);
}
@@ -849,7 +857,7 @@
validate2DRange(xoff, yoff, w, h);
mRS.nAllocationData2D(getIDSafe(), xoff, yoff,
mSelectedLOD, mSelectedFace.mID,
- w, h, data.getID(), dataXoff, dataYoff,
+ w, h, data.getID(mRS), dataXoff, dataYoff,
data.mSelectedLOD, data.mSelectedFace.mID);
}
@@ -880,7 +888,7 @@
mRS.validate();
validateBitmapFormat(b);
validateBitmapSize(b);
- mRS.nAllocationCopyToBitmap(getID(), b);
+ mRS.nAllocationCopyToBitmap(getID(mRS), b);
}
/**
@@ -893,7 +901,7 @@
public void copyTo(byte[] d) {
validateIsInt8();
mRS.validate();
- mRS.nAllocationRead(getID(), d);
+ mRS.nAllocationRead(getID(mRS), d);
}
/**
@@ -906,7 +914,7 @@
public void copyTo(short[] d) {
validateIsInt16();
mRS.validate();
- mRS.nAllocationRead(getID(), d);
+ mRS.nAllocationRead(getID(mRS), d);
}
/**
@@ -919,7 +927,7 @@
public void copyTo(int[] d) {
validateIsInt32();
mRS.validate();
- mRS.nAllocationRead(getID(), d);
+ mRS.nAllocationRead(getID(mRS), d);
}
/**
@@ -932,7 +940,7 @@
public void copyTo(float[] d) {
validateIsFloat32();
mRS.validate();
- mRS.nAllocationRead(getID(), d);
+ mRS.nAllocationRead(getID(mRS), d);
}
/**
@@ -951,10 +959,10 @@
if ((mType.getY() > 0)|| (mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
throw new RSInvalidStateException("Resize only support for 1D allocations at this time.");
}
- mRS.nAllocationResize1D(getID(), dimX);
+ mRS.nAllocationResize1D(getID(mRS), dimX);
mRS.finish(); // Necessary because resize is fifoed and update is async.
- int typeID = mRS.nAllocationGetType(getID());
+ int typeID = mRS.nAllocationGetType(getID(mRS));
mType = new Type(typeID, mRS);
mType.updateFromNative();
updateCacheInfo(mType);
@@ -983,10 +991,10 @@
throw new RSInvalidStateException(
"Resize only support for 2D allocations at this time.");
}
- mRS.nAllocationResize2D(getID(), dimX, dimY);
+ mRS.nAllocationResize2D(getID(mRS), dimX, dimY);
mRS.finish(); // Necessary because resize is fifoed and update is async.
- int typeID = mRS.nAllocationGetType(getID());
+ int typeID = mRS.nAllocationGetType(getID(mRS));
mType = new Type(typeID, mRS);
mType.updateFromNative();
updateCacheInfo(mType);
@@ -1011,10 +1019,10 @@
*/
static public Allocation createTyped(RenderScript rs, Type type, MipmapControl mips, int usage) {
rs.validate();
- if (type.getID() == 0) {
+ if (type.getID(rs) == 0) {
throw new RSInvalidStateException("Bad Type");
}
- int id = rs.nAllocationCreateTyped(type.getID(), mips.mID, usage, 0);
+ int id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0);
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
}
@@ -1035,10 +1043,10 @@
static public Allocation createTyped(RenderScript rs, Type type, MipmapControl mips,
int usage, int pointer) {
rs.validate();
- if (type.getID() == 0) {
+ if (type.getID(rs) == 0) {
throw new RSInvalidStateException("Bad Type");
}
- int id = rs.nAllocationCreateTyped(type.getID(), mips.mID, usage, pointer);
+ int id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, pointer);
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
}
@@ -1093,7 +1101,7 @@
b.setX(count);
Type t = b.create();
- int id = rs.nAllocationCreateTyped(t.getID(), MipmapControl.MIPMAP_NONE.mID, usage, 0);
+ int id = rs.nAllocationCreateTyped(t.getID(rs), MipmapControl.MIPMAP_NONE.mID, usage, 0);
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
}
@@ -1160,7 +1168,7 @@
rs.validate();
Type t = typeFromBitmap(rs, b, mips);
- int id = rs.nAllocationCreateFromBitmap(t.getID(), mips.mID, b, usage);
+ int id = rs.nAllocationCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
if (id == 0) {
throw new RSRuntimeException("Load failed.");
}
@@ -1178,9 +1186,9 @@
throw new RSInvalidStateException("Allocation is not a surface texture.");
}
- int id = mRS.nAllocationGetSurfaceTextureID(getID());
+ int id = mRS.nAllocationGetSurfaceTextureID(getID(mRS));
SurfaceTexture st = new SurfaceTexture(id);
- mRS.nAllocationGetSurfaceTextureID2(getID(), st);
+ mRS.nAllocationGetSurfaceTextureID2(getID(mRS), st);
return st;
}
@@ -1203,7 +1211,7 @@
throw new RSInvalidStateException("Allocation is not USAGE_IO_OUTPUT.");
}
- mRS.nAllocationSetSurface(getID(), sur);
+ mRS.nAllocationSetSurface(getID(mRS), sur);
}
/**
@@ -1216,7 +1224,7 @@
}
Surface s = new Surface(st);
- mRS.nAllocationSetSurface(getID(), s);
+ mRS.nAllocationSetSurface(getID(mRS), s);
}
/**
@@ -1275,7 +1283,7 @@
tb.setMipmaps(mips == MipmapControl.MIPMAP_FULL);
Type t = tb.create();
- int id = rs.nAllocationCubeCreateFromBitmap(t.getID(), mips.mID, b, usage);
+ int id = rs.nAllocationCubeCreateFromBitmap(t.getID(rs), mips.mID, b, usage);
if(id == 0) {
throw new RSRuntimeException("Load failed for bitmap " + b + " element " + e);
}
diff --git a/graphics/java/android/renderscript/AllocationAdapter.java b/graphics/java/android/renderscript/AllocationAdapter.java
index d38f2df..85d86e5 100644
--- a/graphics/java/android/renderscript/AllocationAdapter.java
+++ b/graphics/java/android/renderscript/AllocationAdapter.java
@@ -30,7 +30,7 @@
mAdaptedAllocation = alloc;
}
- int getID() {
+ int getID(RenderScript rs) {
throw new RSInvalidStateException(
"This operation is not supported with adapters at this time.");
}
diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java
index 2e55c48..f464f9b 100644
--- a/graphics/java/android/renderscript/BaseObj.java
+++ b/graphics/java/android/renderscript/BaseObj.java
@@ -43,16 +43,22 @@
* Lookup the native object ID for this object. Primarily used by the
* generated reflected code.
*
+ * @param rs Context to verify against internal context for
+ * match.
*
* @return int
*/
- int getID() {
+ int getID(RenderScript rs) {
+ mRS.validate();
if (mDestroyed) {
throw new RSInvalidStateException("using a destroyed object.");
}
if (mID == 0) {
throw new RSRuntimeException("Internal error: Object id 0.");
}
+ if ((rs != null) && (rs != mRS)) {
+ throw new RSInvalidStateException("using object with mismatched context.");
+ }
return mID;
}
@@ -138,7 +144,7 @@
*/
void updateFromNative() {
mRS.validate();
- mName = mRS.nGetName(getID());
+ mName = mRS.nGetName(getID(mRS));
}
/**
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index 3d4951f..d75c951 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -778,7 +778,7 @@
// we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
int[] dataBuffer = new int[5];
- mRS.nElementGetNativeData(getID(), dataBuffer);
+ mRS.nElementGetNativeData(getID(mRS), dataBuffer);
mNormalized = dataBuffer[2] == 1 ? true : false;
mVectorSize = dataBuffer[3];
@@ -803,7 +803,7 @@
mOffsetInBytes = new int[numSubElements];
int[] subElementIds = new int[numSubElements];
- mRS.nElementGetSubElements(getID(), subElementIds, mElementNames, mArraySizes);
+ mRS.nElementGetSubElements(getID(mRS), subElementIds, mElementNames, mArraySizes);
for(int i = 0; i < numSubElements; i ++) {
mElements[i] = new Element(subElementIds[i], mRS);
mElements[i].updateFromNative();
@@ -1062,7 +1062,7 @@
int[] ids = new int[ein.length];
for (int ct = 0; ct < ein.length; ct++ ) {
- ids[ct] = ein[ct].getID();
+ ids[ct] = ein[ct].getID(mRS);
}
int id = mRS.nElementCreate2(ids, sin, asin);
return new Element(id, mRS, ein, sin, asin);
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index 2739a4b8..a215a57 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -143,7 +143,7 @@
public void addObj(BaseObj obj) {
if (obj != null) {
- addI32(obj.getID());
+ addI32(obj.getID(null));
} else {
addI32(0);
}
diff --git a/graphics/java/android/renderscript/FileA3D.java b/graphics/java/android/renderscript/FileA3D.java
index b5419a7..61793171 100644
--- a/graphics/java/android/renderscript/FileA3D.java
+++ b/graphics/java/android/renderscript/FileA3D.java
@@ -165,7 +165,7 @@
}
private void initEntries() {
- int numFileEntries = mRS.nFileA3DGetNumIndexEntries(getID());
+ int numFileEntries = mRS.nFileA3DGetNumIndexEntries(getID(mRS));
if(numFileEntries <= 0) {
return;
}
@@ -174,10 +174,10 @@
int[] ids = new int[numFileEntries];
String[] names = new String[numFileEntries];
- mRS.nFileA3DGetIndexEntries(getID(), numFileEntries, ids, names);
+ mRS.nFileA3DGetIndexEntries(getID(mRS), numFileEntries, ids, names);
for(int i = 0; i < numFileEntries; i ++) {
- mFileEntries[i] = new IndexEntry(mRS, i, getID(), names[i], EntryType.toEntryType(ids[i]));
+ mFileEntries[i] = new IndexEntry(mRS, i, getID(mRS), names[i], EntryType.toEntryType(ids[i]));
}
}
diff --git a/graphics/java/android/renderscript/Mesh.java b/graphics/java/android/renderscript/Mesh.java
index f641117..ffbb41d 100644
--- a/graphics/java/android/renderscript/Mesh.java
+++ b/graphics/java/android/renderscript/Mesh.java
@@ -137,15 +137,15 @@
@Override
void updateFromNative() {
super.updateFromNative();
- int vtxCount = mRS.nMeshGetVertexBufferCount(getID());
- int idxCount = mRS.nMeshGetIndexCount(getID());
+ int vtxCount = mRS.nMeshGetVertexBufferCount(getID(mRS));
+ int idxCount = mRS.nMeshGetIndexCount(getID(mRS));
int[] vtxIDs = new int[vtxCount];
int[] idxIDs = new int[idxCount];
int[] primitives = new int[idxCount];
- mRS.nMeshGetVertices(getID(), vtxIDs, vtxCount);
- mRS.nMeshGetIndices(getID(), idxIDs, primitives, idxCount);
+ mRS.nMeshGetVertices(getID(mRS), vtxIDs, vtxCount);
+ mRS.nMeshGetIndices(getID(mRS), idxIDs, primitives, idxCount);
mVertexBuffers = new Allocation[vtxCount];
mIndexBuffers = new Allocation[idxCount];
@@ -343,7 +343,7 @@
alloc = Allocation.createSized(mRS, entry.e, entry.size, mUsage);
}
vertexBuffers[ct] = alloc;
- vtx[ct] = alloc.getID();
+ vtx[ct] = alloc.getID(mRS);
}
for(int ct = 0; ct < mIndexTypes.size(); ct ++) {
@@ -354,7 +354,7 @@
} else if(entry.e != null) {
alloc = Allocation.createSized(mRS, entry.e, entry.size, mUsage);
}
- int allocID = (alloc == null) ? 0 : alloc.getID();
+ int allocID = (alloc == null) ? 0 : alloc.getID(mRS);
indexBuffers[ct] = alloc;
primitives[ct] = entry.prim;
@@ -483,12 +483,12 @@
for(int ct = 0; ct < mVertexTypeCount; ct ++) {
Entry entry = mVertexTypes[ct];
vertexBuffers[ct] = entry.a;
- vtx[ct] = entry.a.getID();
+ vtx[ct] = entry.a.getID(mRS);
}
for(int ct = 0; ct < mIndexTypes.size(); ct ++) {
Entry entry = (Entry)mIndexTypes.elementAt(ct);
- int allocID = (entry.a == null) ? 0 : entry.a.getID();
+ int allocID = (entry.a == null) ? 0 : entry.a.getID(mRS);
indexBuffers[ct] = entry.a;
primitives[ct] = entry.prim;
diff --git a/graphics/java/android/renderscript/Path.java b/graphics/java/android/renderscript/Path.java
index 83ae150..9c4d41b 100644
--- a/graphics/java/android/renderscript/Path.java
+++ b/graphics/java/android/renderscript/Path.java
@@ -67,7 +67,7 @@
public static Path createStaticPath(RenderScript rs, Primitive p, float quality, Allocation vtx) {
- int id = rs.nPathCreate(p.mID, false, vtx.getID(), 0, quality);
+ int id = rs.nPathCreate(p.mID, false, vtx.getID(rs), 0, quality);
Path newPath = new Path(id, rs, p, null, null, quality);
return newPath;
}
diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java
index 4d60ac8..104d1cd 100644
--- a/graphics/java/android/renderscript/Program.java
+++ b/graphics/java/android/renderscript/Program.java
@@ -134,11 +134,11 @@
throw new IllegalArgumentException("Slot ID out of range.");
}
if (a != null &&
- a.getType().getID() != mConstants[slot].getID()) {
+ a.getType().getID(mRS) != mConstants[slot].getID(mRS)) {
throw new IllegalArgumentException("Allocation type does not match slot type.");
}
- int id = a != null ? a.getID() : 0;
- mRS.nProgramBindConstants(getID(), slot, id);
+ int id = a != null ? a.getID(mRS) : 0;
+ mRS.nProgramBindConstants(getID(mRS), slot, id);
}
/**
@@ -159,8 +159,8 @@
throw new IllegalArgumentException("Cannot bind cubemap to 2d texture slot");
}
- int id = va != null ? va.getID() : 0;
- mRS.nProgramBindTexture(getID(), slot, id);
+ int id = va != null ? va.getID(mRS) : 0;
+ mRS.nProgramBindTexture(getID(mRS), slot, id);
}
/**
@@ -179,8 +179,8 @@
throw new IllegalArgumentException("Slot ID out of range.");
}
- int id = vs != null ? vs.getID() : 0;
- mRS.nProgramBindSampler(getID(), slot, id);
+ int id = vs != null ? vs.getID(mRS) : 0;
+ mRS.nProgramBindSampler(getID(mRS), slot, id);
}
diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java
index ebc15e5..fa6e2d4 100644
--- a/graphics/java/android/renderscript/ProgramFragment.java
+++ b/graphics/java/android/renderscript/ProgramFragment.java
@@ -64,15 +64,15 @@
for (int i=0; i < mInputCount; i++) {
tmp[idx++] = ProgramParam.INPUT.mID;
- tmp[idx++] = mInputs[i].getID();
+ tmp[idx++] = mInputs[i].getID(mRS);
}
for (int i=0; i < mOutputCount; i++) {
tmp[idx++] = ProgramParam.OUTPUT.mID;
- tmp[idx++] = mOutputs[i].getID();
+ tmp[idx++] = mOutputs[i].getID(mRS);
}
for (int i=0; i < mConstantCount; i++) {
tmp[idx++] = ProgramParam.CONSTANT.mID;
- tmp[idx++] = mConstants[i].getID();
+ tmp[idx++] = mConstants[i].getID(mRS);
}
for (int i=0; i < mTextureCount; i++) {
tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID;
diff --git a/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java b/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
index cd31db3..14f10f1 100644
--- a/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
+++ b/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java
@@ -52,15 +52,15 @@
for (int i=0; i < mInputCount; i++) {
tmp[idx++] = ProgramParam.INPUT.mID;
- tmp[idx++] = mInputs[i].getID();
+ tmp[idx++] = mInputs[i].getID(mRS);
}
for (int i=0; i < mOutputCount; i++) {
tmp[idx++] = ProgramParam.OUTPUT.mID;
- tmp[idx++] = mOutputs[i].getID();
+ tmp[idx++] = mOutputs[i].getID(mRS);
}
for (int i=0; i < mConstantCount; i++) {
tmp[idx++] = ProgramParam.CONSTANT.mID;
- tmp[idx++] = mConstants[i].getID();
+ tmp[idx++] = mConstants[i].getID(mRS);
}
for (int i=0; i < mTextureCount; i++) {
tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID;
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index a6cd15b..32c908e 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -121,15 +121,15 @@
for (int i=0; i < mInputCount; i++) {
tmp[idx++] = ProgramParam.INPUT.mID;
- tmp[idx++] = mInputs[i].getID();
+ tmp[idx++] = mInputs[i].getID(mRS);
}
for (int i=0; i < mOutputCount; i++) {
tmp[idx++] = ProgramParam.OUTPUT.mID;
- tmp[idx++] = mOutputs[i].getID();
+ tmp[idx++] = mOutputs[i].getID(mRS);
}
for (int i=0; i < mConstantCount; i++) {
tmp[idx++] = ProgramParam.CONSTANT.mID;
- tmp[idx++] = mConstants[i].getID();
+ tmp[idx++] = mConstants[i].getID(mRS);
}
for (int i=0; i < mTextureCount; i++) {
tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID;
diff --git a/graphics/java/android/renderscript/ProgramVertexFixedFunction.java b/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
index 9a43943..fac4c3d 100644
--- a/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
+++ b/graphics/java/android/renderscript/ProgramVertexFixedFunction.java
@@ -75,15 +75,15 @@
for (int i=0; i < mInputCount; i++) {
tmp[idx++] = ProgramParam.INPUT.mID;
- tmp[idx++] = mInputs[i].getID();
+ tmp[idx++] = mInputs[i].getID(mRS);
}
for (int i=0; i < mOutputCount; i++) {
tmp[idx++] = ProgramParam.OUTPUT.mID;
- tmp[idx++] = mOutputs[i].getID();
+ tmp[idx++] = mOutputs[i].getID(mRS);
}
for (int i=0; i < mConstantCount; i++) {
tmp[idx++] = ProgramParam.CONSTANT.mID;
- tmp[idx++] = mConstants[i].getID();
+ tmp[idx++] = mConstants[i].getID(mRS);
}
for (int i=0; i < mTextureCount; i++) {
tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID;
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index dffd400..03294b5 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -1000,7 +1000,7 @@
int safeID(BaseObj o) {
if(o != null) {
- return o.getID();
+ return o.getID(this);
}
return 0;
}
diff --git a/graphics/java/android/renderscript/Script.java b/graphics/java/android/renderscript/Script.java
index d00c428..4f59ae3 100644
--- a/graphics/java/android/renderscript/Script.java
+++ b/graphics/java/android/renderscript/Script.java
@@ -26,7 +26,7 @@
* @param slot
*/
protected void invoke(int slot) {
- mRS.nScriptInvoke(getID(), slot);
+ mRS.nScriptInvoke(getID(mRS), slot);
}
/**
@@ -37,9 +37,9 @@
*/
protected void invoke(int slot, FieldPacker v) {
if (v != null) {
- mRS.nScriptInvokeV(getID(), slot, v.getData());
+ mRS.nScriptInvokeV(getID(mRS), slot, v.getData());
} else {
- mRS.nScriptInvoke(getID(), slot);
+ mRS.nScriptInvoke(getID(mRS), slot);
}
}
@@ -58,17 +58,17 @@
}
int in_id = 0;
if (ain != null) {
- in_id = ain.getID();
+ in_id = ain.getID(mRS);
}
int out_id = 0;
if (aout != null) {
- out_id = aout.getID();
+ out_id = aout.getID(mRS);
}
byte[] params = null;
if (v != null) {
params = v.getData();
}
- mRS.nScriptForEach(getID(), slot, in_id, out_id, params);
+ mRS.nScriptForEach(getID(mRS), slot, in_id, out_id, params);
}
@@ -86,9 +86,9 @@
public void bindAllocation(Allocation va, int slot) {
mRS.validate();
if (va != null) {
- mRS.nScriptBindAllocation(getID(), va.getID(), slot);
+ mRS.nScriptBindAllocation(getID(mRS), va.getID(mRS), slot);
} else {
- mRS.nScriptBindAllocation(getID(), 0, slot);
+ mRS.nScriptBindAllocation(getID(mRS), 0, slot);
}
}
@@ -99,7 +99,7 @@
* @param v
*/
public void setVar(int index, float v) {
- mRS.nScriptSetVarF(getID(), index, v);
+ mRS.nScriptSetVarF(getID(mRS), index, v);
}
/**
@@ -109,7 +109,7 @@
* @param v
*/
public void setVar(int index, double v) {
- mRS.nScriptSetVarD(getID(), index, v);
+ mRS.nScriptSetVarD(getID(mRS), index, v);
}
/**
@@ -119,7 +119,7 @@
* @param v
*/
public void setVar(int index, int v) {
- mRS.nScriptSetVarI(getID(), index, v);
+ mRS.nScriptSetVarI(getID(mRS), index, v);
}
/**
@@ -129,7 +129,7 @@
* @param v
*/
public void setVar(int index, long v) {
- mRS.nScriptSetVarJ(getID(), index, v);
+ mRS.nScriptSetVarJ(getID(mRS), index, v);
}
/**
@@ -139,7 +139,7 @@
* @param v
*/
public void setVar(int index, boolean v) {
- mRS.nScriptSetVarI(getID(), index, v ? 1 : 0);
+ mRS.nScriptSetVarI(getID(mRS), index, v ? 1 : 0);
}
/**
@@ -149,7 +149,7 @@
* @param o
*/
public void setVar(int index, BaseObj o) {
- mRS.nScriptSetVarObj(getID(), index, (o == null) ? 0 : o.getID());
+ mRS.nScriptSetVarObj(getID(mRS), index, (o == null) ? 0 : o.getID(mRS));
}
/**
@@ -159,13 +159,13 @@
* @param v
*/
public void setVar(int index, FieldPacker v) {
- mRS.nScriptSetVarV(getID(), index, v.getData());
+ mRS.nScriptSetVarV(getID(mRS), index, v.getData());
}
public void setTimeZone(String timeZone) {
mRS.validate();
try {
- mRS.nScriptSetTimeZone(getID(), timeZone.getBytes("UTF-8"));
+ mRS.nScriptSetTimeZone(getID(mRS), timeZone.getBytes("UTF-8"));
} catch (java.io.UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index 70d1de4..a707df2 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -180,7 +180,7 @@
// We have 6 integer to obtain mDimX; mDimY; mDimZ;
// mDimLOD; mDimFaces; mElement;
int[] dataBuffer = new int[6];
- mRS.nTypeGetNativeData(getID(), dataBuffer);
+ mRS.nTypeGetNativeData(getID(mRS), dataBuffer);
mDimX = dataBuffer[0];
mDimY = dataBuffer[1];
@@ -280,7 +280,8 @@
}
}
- int id = mRS.nTypeCreate(mElement.getID(), mDimX, mDimY, mDimZ, mDimMipmaps, mDimFaces);
+ int id = mRS.nTypeCreate(mElement.getID(mRS),
+ mDimX, mDimY, mDimZ, mDimMipmaps, mDimFaces);
Type t = new Type(id, mRS);
t.mElement = mElement;
t.mDimX = mDimX;
diff --git a/include/androidfw/KeycodeLabels.h b/include/androidfw/KeycodeLabels.h
index 9e4dfcb..1828062 100755
--- a/include/androidfw/KeycodeLabels.h
+++ b/include/androidfw/KeycodeLabels.h
@@ -235,6 +235,14 @@
{ "CALENDAR", 208 },
{ "MUSIC", 209 },
{ "CALCULATOR", 210 },
+ { "ZENKAKU_HANKAKU", 211 },
+ { "EISU", 212 },
+ { "MUHENKAN", 213 },
+ { "HENKAN", 214 },
+ { "KATAKANA_HIRAGANA", 215 },
+ { "YEN", 216 },
+ { "RO", 217 },
+ { "KANA", 218 },
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/private/hwui/DrawGlInfo.h b/include/private/hwui/DrawGlInfo.h
index abcf41d..8028bf3 100644
--- a/include/private/hwui/DrawGlInfo.h
+++ b/include/private/hwui/DrawGlInfo.h
@@ -44,20 +44,31 @@
float dirtyBottom;
/**
+ * Values used as the "what" parameter of the functor.
+ */
+ enum Mode {
+ // Indicates that the functor is called to perform a draw
+ kModeDraw,
+ // Indicates the the functor is called only to perform
+ // processing and that no draw should be attempted
+ kModeProcess
+ };
+
+ /**
* Values used by OpenGL functors to tell the framework
* what to do next.
*/
enum Status {
// The functor is done
- kStatusDone,
+ kStatusDone = 0x0,
// The functor is requesting a redraw (the clip rect
// used by the redraw is specified by DrawGlInfo.)
// The rest of the UI might redraw too.
- kStatusDraw,
+ kStatusDraw = 0x1,
// The functor needs to be invoked again but will
// not redraw. Only the functor is invoked again
// (unless another functor requests a redraw.)
- kStatusInvoke
+ kStatusInvoke = 0x2
};
}; // struct DrawGlInfo
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 3a3f8a5..9f2bacd 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -94,7 +94,8 @@
}
DisplayList::DisplayList(const DisplayListRenderer& recorder) :
- mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL) {
+ mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL),
+ mStaticMatrix(NULL), mAnimationMatrix(NULL) {
initFromDisplayListRenderer(recorder);
}
@@ -108,10 +109,10 @@
mTop = 0;
mRight = 0;
mBottom = 0;
- mApplicationScale = -1;
mClipChildren = true;
mAlpha = 1;
mMultipliedAlpha = 255;
+ mHasOverlappingRendering = true;
mTranslationX = 0;
mTranslationY = 0;
mRotation = 0;
@@ -143,18 +144,16 @@
sk_free((void*) mReader.base());
if (USE_DISPLAY_LIST_PROPERTIES) {
- if (mTransformMatrix) {
- delete mTransformMatrix;
- mTransformMatrix = NULL;
- }
- if (mTransformCamera) {
- delete mTransformCamera;
- mTransformCamera = NULL;
- }
- if (mTransformMatrix3D) {
- delete mTransformMatrix3D;
- mTransformMatrix3D = NULL;
- }
+ delete mTransformMatrix;
+ delete mTransformCamera;
+ delete mTransformMatrix3D;
+ delete mStaticMatrix;
+ delete mAnimationMatrix;
+ mTransformMatrix = NULL;
+ mTransformCamera = NULL;
+ mTransformMatrix3D = NULL;
+ mStaticMatrix = NULL;
+ mAnimationMatrix = NULL;
}
Caches& caches = Caches::getInstance();
@@ -667,12 +666,26 @@
void DisplayList::outputViewProperties(OpenGLRenderer& renderer, char* indent) {
if (USE_DISPLAY_LIST_PROPERTIES) {
updateMatrix();
- if (mApplicationScale >= 0) {
- ALOGD("%s%s %.2f, %.2f", (char*) indent, "Scale",
- mApplicationScale, mApplicationScale);
- }
if (mLeft != 0 || mTop != 0) {
- ALOGD("%s%s %d, %d", indent, "Translate", mLeft, mTop);
+ ALOGD("%s%s %d, %d", indent, "Translate (left, top)", mLeft, mTop);
+ }
+ if (mStaticMatrix) {
+ ALOGD("%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
+ indent, "ConcatMatrix (static)", mStaticMatrix,
+ mStaticMatrix->get(0), mStaticMatrix->get(1),
+ mStaticMatrix->get(2), mStaticMatrix->get(3),
+ mStaticMatrix->get(4), mStaticMatrix->get(5),
+ mStaticMatrix->get(6), mStaticMatrix->get(7),
+ mStaticMatrix->get(8));
+ }
+ if (mAnimationMatrix) {
+ ALOGD("%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
+ indent, "ConcatMatrix (animation)", mAnimationMatrix,
+ mAnimationMatrix->get(0), mAnimationMatrix->get(1),
+ mAnimationMatrix->get(2), mAnimationMatrix->get(3),
+ mAnimationMatrix->get(4), mAnimationMatrix->get(5),
+ mAnimationMatrix->get(6), mAnimationMatrix->get(7),
+ mAnimationMatrix->get(8));
}
if (mMatrixFlags != 0) {
if (mMatrixFlags == TRANSLATION) {
@@ -719,13 +732,29 @@
#endif
updateMatrix();
if (mLeft != 0 || mTop != 0) {
- DISPLAY_LIST_LOGD("%s%s %d, %d", indent, "Translate", mLeft, mTop);
+ DISPLAY_LIST_LOGD("%s%s %d, %d", indent, "Translate (left, top)", mLeft, mTop);
renderer.translate(mLeft, mTop);
}
- if (mApplicationScale >= 0) {
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f", (char*) indent, "Scale",
- mApplicationScale, mApplicationScale);
- renderer.scale(mApplicationScale, mApplicationScale);
+ if (mStaticMatrix) {
+ DISPLAY_LIST_LOGD(
+ "%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
+ indent, "ConcatMatrix (static)", mStaticMatrix,
+ mStaticMatrix->get(0), mStaticMatrix->get(1),
+ mStaticMatrix->get(2), mStaticMatrix->get(3),
+ mStaticMatrix->get(4), mStaticMatrix->get(5),
+ mStaticMatrix->get(6), mStaticMatrix->get(7),
+ mStaticMatrix->get(8));
+ renderer.concatMatrix(mStaticMatrix);
+ } else if (mAnimationMatrix) {
+ DISPLAY_LIST_LOGD(
+ "%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
+ indent, "ConcatMatrix (animation)", mAnimationMatrix,
+ mAnimationMatrix->get(0), mAnimationMatrix->get(1),
+ mAnimationMatrix->get(2), mAnimationMatrix->get(3),
+ mAnimationMatrix->get(4), mAnimationMatrix->get(5),
+ mAnimationMatrix->get(6), mAnimationMatrix->get(7),
+ mAnimationMatrix->get(8));
+ renderer.concatMatrix(mAnimationMatrix);
}
if (mMatrixFlags != 0) {
if (mMatrixFlags == TRANSLATION) {
@@ -744,18 +773,23 @@
}
}
if (mAlpha < 1 && !mCaching) {
- // TODO: should be able to store the size of a DL at record time and not
- // have to pass it into this call. In fact, this information might be in the
- // location/size info that we store with the new native transform data.
- int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
- if (mClipChildren) {
- flags |= SkCanvas::kClipToLayer_SaveFlag;
+ if (!mHasOverlappingRendering) {
+ DISPLAY_LIST_LOGD("%s%s %.2f", indent, "SetAlpha", mAlpha);
+ renderer.setAlpha(mAlpha);
+ } else {
+ // TODO: should be able to store the size of a DL at record time and not
+ // have to pass it into this call. In fact, this information might be in the
+ // location/size info that we store with the new native transform data.
+ int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
+ if (mClipChildren) {
+ flags |= SkCanvas::kClipToLayer_SaveFlag;
+ }
+ DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", indent, "SaveLayerAlpha",
+ (float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
+ mMultipliedAlpha, flags);
+ renderer.saveLayerAlpha(0, 0, mRight - mLeft, mBottom - mTop,
+ mMultipliedAlpha, flags);
}
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", indent, "SaveLayerAlpha",
- (float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
- mMultipliedAlpha, flags);
- renderer.saveLayerAlpha(0, 0, mRight - mLeft, mBottom - mTop,
- mMultipliedAlpha, flags);
}
if (mClipChildren) {
DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f", indent, "ClipRect", 0.0f, 0.0f,
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index fff1d7c6..fe0c94d 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -51,7 +51,7 @@
// Set to 1 to enable native processing of View properties. 0 by default. Eventually this
// will go away and we will always use this approach for accelerated apps.
-#define USE_DISPLAY_LIST_PROPERTIES 0
+#define USE_DISPLAY_LIST_PROPERTIES 1
#define TRANSLATION 0x0001
#define ROTATION 0x0002
@@ -156,14 +156,24 @@
}
}
- void setApplicationScale(float scale) {
- mApplicationScale = scale;
- }
-
void setClipChildren(bool clipChildren) {
mClipChildren = clipChildren;
}
+ void setStaticMatrix(SkMatrix* matrix) {
+ delete mStaticMatrix;
+ mStaticMatrix = new SkMatrix(*matrix);
+ }
+
+ void setAnimationMatrix(SkMatrix* matrix) {
+ delete mAnimationMatrix;
+ if (matrix) {
+ mAnimationMatrix = new SkMatrix(*matrix);
+ } else {
+ mAnimationMatrix = NULL;
+ }
+ }
+
void setAlpha(float alpha) {
if (alpha != mAlpha) {
mAlpha = alpha;
@@ -171,6 +181,10 @@
}
}
+ void setHasOverlappingRendering(bool hasOverlappingRendering) {
+ mHasOverlappingRendering = hasOverlappingRendering;
+ }
+
void setTranslationX(float translationX) {
if (translationX != mTranslationX) {
mTranslationX = translationX;
@@ -483,10 +497,10 @@
String8 mName;
// View properties
- float mApplicationScale;
bool mClipChildren;
float mAlpha;
int mMultipliedAlpha;
+ bool mHasOverlappingRendering;
float mTranslationX, mTranslationY;
float mRotation, mRotationX, mRotationY;
float mScaleX, mScaleY;
@@ -502,6 +516,8 @@
SkMatrix* mTransformMatrix;
Sk3DView* mTransformCamera;
SkMatrix* mTransformMatrix3D;
+ SkMatrix* mStaticMatrix;
+ SkMatrix* mAnimationMatrix;
bool mCaching;
};
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index d304b37..eea707e 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -69,10 +69,14 @@
void LayerCache::deleteLayer(Layer* layer) {
if (layer) {
- LAYER_LOGD("Destroying layer %dx%d", layer->getWidth(), layer->getHeight());
+ GLuint fbo = layer->getFbo();
+ LAYER_LOGD("Destroying layer %dx%d, fbo %d", layer->getWidth(), layer->getHeight(), fbo);
+
mSize -= layer->getWidth() * layer->getHeight() * 4;
- layer->deleteFbo();
+
+ if (fbo) Caches::getInstance().fboCache.put(fbo);
layer->deleteTexture();
+
delete layer;
}
}
@@ -174,6 +178,10 @@
victim->layer.getHeight());
}
+ layer->deferredUpdateScheduled = false;
+ layer->renderer = NULL;
+ layer->displayList = NULL;
+
LayerEntry entry(layer);
mCache.add(entry);
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index e320eb2..2a4e72b 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -309,6 +309,7 @@
if (fbo) {
flushLayer(layer);
Caches::getInstance().fboCache.put(fbo);
+ layer->setFbo(0);
}
if (!Caches::getInstance().layerCache.put(layer)) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index eb4b83b..39d2e39 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -166,6 +166,7 @@
void OpenGLRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) {
mCaches.clearGarbage();
+ mFunctors.clear();
mSnapshot = new Snapshot(mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -236,7 +237,39 @@
glBlendEquation(GL_FUNC_ADD);
}
-status_t OpenGLRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
+status_t OpenGLRenderer::invokeFunctors(Rect& dirty) {
+ status_t result = DrawGlInfo::kStatusDone;
+
+ Vector<Functor*> functors(mFunctors);
+ mFunctors.clear();
+
+ DrawGlInfo info;
+ info.clipLeft = 0;
+ info.clipTop = 0;
+ info.clipRight = 0;
+ info.clipBottom = 0;
+ info.isLayer = false;
+ memset(info.transform, 0, sizeof(float) * 16);
+
+ size_t count = functors.size();
+ for (size_t i = 0; i < count; i++) {
+ Functor* f = functors.itemAt(i);
+ result |= (*f)(DrawGlInfo::kModeProcess, &info);
+
+ if (result != DrawGlInfo::kStatusDone) {
+ Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
+ dirty.unionWith(localDirty);
+
+ if (result == DrawGlInfo::kStatusInvoke) {
+ mFunctors.push(f);
+ }
+ }
+ }
+
+ return result;
+}
+
+status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
interrupt();
if (mDirtyClip) {
setScissorFromClip();
@@ -261,11 +294,15 @@
info.isLayer = hasLayer();
getSnapshot()->transform->copyTo(&info.transform[0]);
- status_t result = (*functor)(0, &info);
+ status_t result = (*functor)(DrawGlInfo::kModeDraw, &info);
- if (result != 0) {
+ if (result != DrawGlInfo::kStatusDone) {
Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
dirty.unionWith(localDirty);
+
+ if (result == DrawGlInfo::kStatusInvoke) {
+ mFunctors.push(functor);
+ }
}
resume();
@@ -594,6 +631,9 @@
const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
if (fboLayer) {
+ // Detach the texture from the FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+
// Unbind current FBO and restore previous one
glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
}
@@ -634,13 +674,9 @@
// code path
// See LayerRenderer::destroyLayer(Layer*)
- // Detach the texture from the FBO
- glBindFramebuffer(GL_FRAMEBUFFER, current->fbo);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
- glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
-
// Put the FBO name back in the cache, if it doesn't fit, it will be destroyed
mCaches.fboCache.put(current->fbo);
+ layer->setFbo(0);
}
dirtyClip();
@@ -1063,6 +1099,7 @@
void OpenGLRenderer::setupDrawColor(int color, int alpha) {
mColorA = alpha / 255.0f;
+ mColorA *= mSnapshot->alpha;
// Second divide of a by 255 is an optimization, allowing us to simply multiply
// the rgb values by a instead of also dividing by 255
const float a = mColorA / 255.0f;
@@ -1291,18 +1328,18 @@
* are set up for each individual segment.
*/
void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
- GLvoid* lengthCoords, float boundaryWidthProportion) {
+ GLvoid* lengthCoords, float boundaryWidthProportion, int& widthSlot, int& lengthSlot) {
bool force = mCaches.unbindMeshBuffer();
mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
vertices, gAAVertexStride);
mCaches.resetTexCoordsVertexPointer();
mCaches.unbindIndicesBuffer();
- int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
+ widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
glEnableVertexAttribArray(widthSlot);
glVertexAttribPointer(widthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, widthCoords);
- int lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
+ lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
glEnableVertexAttribArray(lengthSlot);
glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords);
@@ -1311,7 +1348,12 @@
// Setting the inverse value saves computations per-fragment in the shader
int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth");
- glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
+ glUniform1f(inverseBoundaryWidthSlot, 1.0f / boundaryWidthProportion);
+}
+
+void OpenGLRenderer::finishDrawAALine(const int widthSlot, const int lengthSlot) {
+ glDisableVertexAttribArray(widthSlot);
+ glDisableVertexAttribArray(lengthSlot);
}
void OpenGLRenderer::finishDrawTexture() {
@@ -1683,13 +1725,18 @@
float width = right - left;
float height = bottom - top;
+ int widthSlot;
+ int lengthSlot;
+
float boundaryWidthProportion = (width != 0) ? (2 * boundarySizeX) / width : 0;
float boundaryHeightProportion = (height != 0) ? (2 * boundarySizeY) / height : 0;
- setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion);
+ setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
+ boundaryWidthProportion, widthSlot, lengthSlot);
+
int boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
int inverseBoundaryLengthSlot = mCaches.currentProgram->getUniform("inverseBoundaryLength");
glUniform1f(boundaryLengthSlot, boundaryHeightProportion);
- glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryHeightProportion));
+ glUniform1f(inverseBoundaryLengthSlot, (1.0f / boundaryHeightProportion));
if (!quickReject(left, top, right, bottom)) {
AAVertex::set(aaVertices++, left, bottom, 1, 1);
@@ -1699,6 +1746,8 @@
dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
+
+ finishDrawAALine(widthSlot, lengthSlot);
}
/**
@@ -1729,11 +1778,14 @@
// A stroke width of 0 has a special meaning in Skia:
// it draws a line 1 px wide regardless of current transform
bool isHairLine = paint->getStrokeWidth() == 0.0f;
+
float inverseScaleX = 1.0f;
float inverseScaleY = 1.0f;
bool scaled = false;
+
int alpha;
SkXfermode::Mode mode;
+
int generatedVerticesCount = 0;
int verticesCount = count;
if (count > 4) {
@@ -1753,10 +1805,13 @@
float m10 = mat->data[Matrix4::kSkewX];
float m11 = mat->data[Matrix4::kScaleX];
float m12 = mat->data[6];
+
float scaleX = sqrtf(m00 * m00 + m01 * m01);
float scaleY = sqrtf(m10 * m10 + m11 * m11);
+
inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
+
if (inverseScaleX != 1.0f || inverseScaleY != 1.0f) {
scaled = true;
}
@@ -1786,10 +1841,16 @@
// Expand boundary to enable AA calculations on the quad border
halfStrokeWidth += .5f;
}
+
+ int widthSlot;
+ int lengthSlot;
+
Vertex lines[verticesCount];
Vertex* vertices = &lines[0];
+
AAVertex wLines[verticesCount];
AAVertex* aaVertices = &wLines[0];
+
if (CC_UNLIKELY(!isAA)) {
setupDrawVertices(vertices);
} else {
@@ -1801,7 +1862,8 @@
// We will need to calculate the actual width proportion on each segment for
// scaled non-hairlines, since the boundary proportion may differ per-axis when scaled.
float boundaryWidthProportion = 1 / (2 * halfStrokeWidth);
- setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion);
+ setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
+ boundaryWidthProportion, widthSlot, lengthSlot);
}
AAVertex* prevAAVertex = NULL;
@@ -1811,10 +1873,12 @@
int inverseBoundaryLengthSlot = -1;
int boundaryWidthSlot = -1;
int inverseBoundaryWidthSlot = -1;
+
for (int i = 0; i < count; i += 4) {
// a = start point, b = end point
vec2 a(points[i], points[i + 1]);
vec2 b(points[i + 2], points[i + 3]);
+
float length = 0;
float boundaryLengthProportion = 0;
float boundaryWidthProportion = 0;
@@ -1831,6 +1895,7 @@
}
n *= wideningFactor;
}
+
if (scaled) {
n.x *= inverseScaleX;
n.y *= inverseScaleY;
@@ -1841,11 +1906,13 @@
extendedN /= 2;
extendedN.x *= inverseScaleX;
extendedN.y *= inverseScaleY;
+
float extendedNLength = extendedN.length();
// We need to set this value on the shader prior to drawing
boundaryWidthProportion = extendedNLength / (halfStrokeWidth + extendedNLength);
n += extendedN;
}
+
float x = n.x;
n.x = -n.y;
n.y = x;
@@ -1855,6 +1922,7 @@
vec2 abVector = (b - a);
length = abVector.length();
abVector.normalize();
+
if (scaled) {
abVector.x *= inverseScaleX;
abVector.y *= inverseScaleY;
@@ -1863,6 +1931,7 @@
} else {
boundaryLengthProportion = .5 / (length + 1);
}
+
abVector /= 2;
a -= abVector;
b += abVector;
@@ -1891,10 +1960,12 @@
Vertex::set(vertices++, p1.x, p1.y);
generatedVerticesCount += 2;
}
+
Vertex::set(vertices++, p1.x, p1.y);
Vertex::set(vertices++, p2.x, p2.y);
Vertex::set(vertices++, p4.x, p4.y);
Vertex::set(vertices++, p3.x, p3.y);
+
prevVertex = vertices - 1;
generatedVerticesCount += 4;
} else {
@@ -1907,14 +1978,17 @@
inverseBoundaryWidthSlot =
mCaches.currentProgram->getUniform("inverseBoundaryWidth");
}
+
glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
}
+
if (boundaryLengthSlot < 0) {
boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
inverseBoundaryLengthSlot =
mCaches.currentProgram->getUniform("inverseBoundaryLength");
}
+
glUniform1f(boundaryLengthSlot, boundaryLengthProportion);
glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryLengthProportion));
@@ -1928,21 +2002,29 @@
AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
generatedVerticesCount += 2;
}
+
AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
AAVertex::set(aaVertices++, p1.x, p1.y, 1, 0);
AAVertex::set(aaVertices++, p3.x, p3.y, 0, 1);
AAVertex::set(aaVertices++, p2.x, p2.y, 0, 0);
+
prevAAVertex = aaVertices - 1;
generatedVerticesCount += 4;
}
+
dirtyLayer(a.x == b.x ? left - 1 : left, a.y == b.y ? top - 1 : top,
a.x == b.x ? right: right, a.y == b.y ? bottom: bottom,
*mSnapshot->transform);
}
}
+
if (generatedVerticesCount > 0) {
glDrawArrays(GL_TRIANGLE_STRIP, 0, generatedVerticesCount);
}
+
+ if (isAA) {
+ finishDrawAALine(widthSlot, lengthSlot);
+ }
}
void OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
@@ -1988,10 +2070,12 @@
for (int i = 0; i < count; i += 2) {
TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
generatedVerticesCount++;
+
float left = points[i] - halfWidth;
float right = points[i] + halfWidth;
float top = points[i + 1] - halfWidth;
float bottom = points [i + 1] + halfWidth;
+
dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
}
@@ -2762,6 +2846,7 @@
*mode = SkXfermode::kSrcOver_Mode;
*alpha = 255;
}
+ *alpha *= mSnapshot->alpha;
}
SkXfermode::Mode OpenGLRenderer::getXfermode(SkXfermode* mode) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 3ba6202..b52d2b0 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -72,7 +72,8 @@
virtual void interrupt();
virtual void resume();
- virtual status_t callDrawGLFunction(Functor *functor, Rect& dirty);
+ ANDROID_API status_t invokeFunctors(Rect& dirty);
+ virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
ANDROID_API int getSaveCount() const;
virtual int save(int flags);
@@ -84,6 +85,10 @@
virtual int saveLayerAlpha(float left, float top, float right, float bottom,
int alpha, int flags);
+ virtual void setAlpha(float alpha) {
+ mSnapshot->alpha = alpha;
+ }
+
virtual void translate(float dx, float dy);
virtual void rotate(float degrees);
virtual void scale(float sx, float sy);
@@ -548,7 +553,8 @@
void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords);
void setupDrawVertices(GLvoid* vertices);
void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, GLvoid* lengthCoords,
- float strokeWidth);
+ float strokeWidth, int& widthSlot, int& lengthSlot);
+ void finishDrawAALine(const int widthSlot, const int lengthSlot);
void finishDrawTexture();
void accountForClear(SkXfermode::Mode mode);
@@ -602,8 +608,10 @@
// Various caches
Caches& mCaches;
- // List of rectagnles to clear after saveLayer() is invoked
+ // List of rectangles to clear after saveLayer() is invoked
Vector<Rect*> mLayers;
+ // List of functors to invoke after a frame is drawn
+ Vector<Functor*> mFunctors;
// Indentity matrix
const mat4 mIdentity;
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index de2c674..5d5961a 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -26,7 +26,7 @@
///////////////////////////////////////////////////////////////////////////////
Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0),
- invisible(false), empty(false) {
+ invisible(false), empty(false), alpha(1.0f) {
transform = &mTransformRoot;
clipRect = &mClipRectRoot;
@@ -41,7 +41,7 @@
Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags):
flags(0), previous(s), layer(NULL), fbo(s->fbo),
invisible(s->invisible), empty(false),
- viewport(s->viewport), height(s->height) {
+ viewport(s->viewport), height(s->height), alpha(s->alpha) {
clipRegion = NULL;
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index b2bc879..30b03fc 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -208,6 +208,17 @@
*/
Region* region;
+ /**
+ * Current alpha value. This value is 1 by default, but may be set by a DisplayList which
+ * has translucent rendering in a non-overlapping View. This value will be used by
+ * the renderer to set the alpha in the current color being used for ensuing drawing
+ * operations. The value is inherited by child snapshots because the same value should
+ * be applied to descendents of the current DisplayList (for example, a TextView contains
+ * the base alpha value which should be applied to the child DisplayLists used for drawing
+ * the actual text).
+ */
+ float alpha;
+
private:
void ensureClipRegion();
void copyClipRectFromRegion();
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 5cc24c0..4e112af 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -519,13 +519,34 @@
// start recording
synchronized(mRecordingStateLock) {
- if (native_start() == SUCCESS) {
+ if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) {
mRecordingState = RECORDSTATE_RECORDING;
}
}
}
+ /**
+ * Starts recording from the AudioRecord instance when the specified synchronization event
+ * occurs on the specified audio session.
+ * @throws IllegalStateException
+ * @param syncEvent event that triggers the capture.
+ * @see MediaSyncEvent
+ * @hide
+ */
+ public void startRecording(MediaSyncEvent syncEvent)
+ throws IllegalStateException {
+ if (mState != STATE_INITIALIZED) {
+ throw(new IllegalStateException("startRecording() called on an "
+ +"uninitialized AudioRecord."));
+ }
+ // start recording
+ synchronized(mRecordingStateLock) {
+ if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) {
+ mRecordingState = RECORDSTATE_RECORDING;
+ }
+ }
+ }
/**
* Stops recording.
@@ -787,7 +808,7 @@
private native final void native_release();
- private native final int native_start();
+ private native final int native_start(int syncEvent, int sessionId);
private native final void native_stop();
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 5f6a61d..f59848f 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -92,7 +92,7 @@
protected static final boolean DEBUG_RC = false;
/** How long to delay before persisting a change in volume/ringer mode. */
- private static final int PERSIST_DELAY = 3000;
+ private static final int PERSIST_DELAY = 500;
private Context mContext;
private ContentResolver mContentResolver;
@@ -126,6 +126,7 @@
private static final int MSG_RCDISPLAY_CLEAR = 13;
private static final int MSG_RCDISPLAY_UPDATE = 14;
private static final int MSG_SET_ALL_VOLUMES = 15;
+ private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 16;
// flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
@@ -501,6 +502,10 @@
System.MUTE_STREAMS_AFFECTED,
((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
+ boolean masterMute = System.getInt(cr, System.VOLUME_MASTER_MUTE, 0) == 1;
+ AudioSystem.setMasterMute(masterMute);
+ broadcastMasterMuteStatus(masterMute);
+
// Each stream will read its own persisted settings
// Broadcast the sticky intent
@@ -740,9 +745,14 @@
// UI update and Broadcast Intent
private void sendMasterMuteUpdate(boolean muted, int flags) {
mVolumePanel.postMasterMuteChanged(flags);
+ broadcastMasterMuteStatus(muted);
+ }
+ private void broadcastMasterMuteStatus(boolean muted) {
Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
long origCallerIdentityToken = Binder.clearCallingIdentity();
mContext.sendStickyBroadcast(intent);
Binder.restoreCallingIdentity(origCallerIdentityToken);
@@ -820,6 +830,9 @@
public void setMasterMute(boolean state, IBinder cb) {
if (state != AudioSystem.getMasterMute()) {
AudioSystem.setMasterMute(state);
+ // Post a persist master volume msg
+ sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
+ : 0, 0, null, PERSIST_DELAY);
sendMasterMuteUpdate(state, AudioManager.FLAG_SHOW_UI);
}
}
@@ -861,8 +874,9 @@
// Post a persist master volume msg
sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
- sendMasterVolumeUpdate(flags, oldVolume, newVolume);
}
+ // Send the volume update regardless whether there was a change.
+ sendMasterVolumeUpdate(flags, oldVolume, newVolume);
}
}
@@ -2550,6 +2564,11 @@
(float)msg.arg1 / (float)1000.0);
break;
+ case MSG_PERSIST_MASTER_VOLUME_MUTE:
+ Settings.System.putInt(mContentResolver, Settings.System.VOLUME_MASTER_MUTE,
+ msg.arg1);
+ break;
+
case MSG_PERSIST_RINGER_MODE:
// note that the value persisted is the current ringer mode, not the
// value of ringer mode as of the time the request was made to persist
@@ -3037,11 +3056,6 @@
adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
BluetoothProfile.A2DP);
}
-
- if (mUseMasterVolume) {
- // Send sticky broadcast for initial master mute state
- sendMasterMuteUpdate(false, 0);
- }
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
// a package is being removed, not replaced
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index b5e832c..18a00bc 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -311,6 +311,10 @@
public static final int FOR_DOCK = 3;
private static final int NUM_FORCE_USE = 4;
+ // usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t
+ public static final int SYNC_EVENT_NONE = 0;
+ public static final int SYNC_EVENT_PRESENTATION_COMPLETE = 1;
+
public static native int setDeviceConnectionState(int device, int state, String device_address);
public static native int getDeviceConnectionState(int device, String device_address);
public static native int setPhoneState(int state);
diff --git a/media/java/android/media/Crypto.java b/media/java/android/media/Crypto.java
new file mode 100644
index 0000000..43e34fb
--- /dev/null
+++ b/media/java/android/media/Crypto.java
@@ -0,0 +1,49 @@
+/*
+ * 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.media;
+
+/**
+ * Crypto class can be used in conjunction with MediaCodec to decode
+ * encrypted media data.
+ * @hide
+*/
+public final class Crypto {
+ public static final native boolean isCryptoSchemeSupported(byte[] uuid);
+
+ public Crypto(byte[] uuid, byte[] initData) {
+ native_setup(uuid, initData);
+ }
+
+ public final native boolean requiresSecureDecoderComponent(String mime);
+
+ @Override
+ protected void finalize() {
+ native_finalize();
+ }
+
+ public native final void release();
+ private static native final void native_init();
+ private native final void native_setup(byte[] uuid, byte[] initData);
+ private native final void native_finalize();
+
+ static {
+ System.loadLibrary("media_jni");
+ native_init();
+ }
+
+ private int mNativeContext;
+}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d06e302..66cea9d4 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -16,6 +16,7 @@
package android.media;
+import android.media.Crypto;
import android.view.Surface;
import java.nio.ByteBuffer;
import java.util.Map;
@@ -25,8 +26,7 @@
* encoder/decoder components.
* @hide
*/
-public class MediaCodec
-{
+final public class MediaCodec {
/** Per buffer metadata includes an offset and size specifying
the range of valid data in the associated codec buffer.
*/
@@ -83,7 +83,6 @@
public native final void release();
public static int CONFIGURE_FLAG_ENCODE = 1;
- public static int CONFIGURE_FLAG_SECURE = 2;
/** Configures a component.
* @param format A map of string/value pairs describing the input format
@@ -114,11 +113,14 @@
*
* @param surface Specify a surface on which to render the output of this
* decoder.
+ * @param crypto Specify a crypto object to facilitate secure decryption
+ * of the media data.
* @param flags Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
* component as an encoder.
*/
public void configure(
- Map<String, Object> format, Surface surface, int flags) {
+ Map<String, Object> format,
+ Surface surface, Crypto crypto, int flags) {
String[] keys = null;
Object[] values = null;
@@ -134,11 +136,12 @@
}
}
- native_configure(keys, values, surface, flags);
+ native_configure(keys, values, surface, crypto, flags);
}
private native final void native_configure(
- String[] keys, Object[] values, Surface surface, int flags);
+ String[] keys, Object[] values,
+ Surface surface, Crypto crypto, int flags);
/** After successfully configuring the component, call start. On return
* you can query the component for its input/output buffers.
diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java
new file mode 100644
index 0000000..b46ce96
--- /dev/null
+++ b/media/java/android/media/MediaCodecList.java
@@ -0,0 +1,51 @@
+/*
+ * 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.media;
+
+/**
+ * MediaCodecList class can be used to enumerate available codecs,
+ * find a codec supporting a given format and query the capabilities
+ * of a given codec.
+ * @hide
+*/
+final public class MediaCodecList {
+ public static native final int countCodecs();
+ public static native final String getCodecName(int index);
+ public static native final boolean isEncoder(int index);
+ public static native final String[] getSupportedTypes(int index);
+
+ public static final class CodecProfileLevel {
+ public int mProfile;
+ public int mLevel;
+ };
+
+ public static final class CodecCapabilities {
+ public CodecProfileLevel[] mProfileLevels;
+ public int[] mColorFormats;
+ };
+ public static native final CodecCapabilities getCodecCapabilities(
+ int index, String type);
+
+ private static native final void native_init();
+
+ private MediaCodecList() {}
+
+ static {
+ System.loadLibrary("media_jni");
+ native_init();
+ }
+}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 9ea3d0e..9c3b6a7 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -23,8 +23,7 @@
* MediaExtractor
* @hide
*/
-public class MediaExtractor
-{
+final public class MediaExtractor {
public MediaExtractor(String path) {
native_setup(path);
}
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 7f7e284..c9bec18 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -179,6 +179,7 @@
if (isWMAEnabled()) {
addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma", MtpConstants.FORMAT_WMA);
}
+ addFileType("OGG", FILE_TYPE_OGG, "audio/ogg", MtpConstants.FORMAT_OGG);
addFileType("OGG", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG);
addFileType("OGA", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG);
addFileType("AAC", FILE_TYPE_AAC, "audio/aac", MtpConstants.FORMAT_AAC);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index d92180d..c38f8f3 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1883,8 +1883,6 @@
return;
case MEDIA_ERROR:
- // For PV specific error values (msg.arg2) look in
- // opencore/pvmi/pvmf/include/pvmf_return_codes.h
Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
boolean error_was_handled = false;
if (mOnErrorListener != null) {
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 43df8a1..26089ad 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -75,7 +75,7 @@
* - the processDirectory() JNI method wraps the provided mediascanner client in a native
* 'MyMediaScannerClient' class, then calls processDirectory() on the native MediaScanner
* object (which got created when the Java MediaScanner was created).
- * - native MediaScanner.processDirectory() (currently part of opencore) calls
+ * - native MediaScanner.processDirectory() calls
* doProcessDirectory(), which recurses over the folder, and calls
* native MyMediaScannerClient.scanFile() for every file whose extension matches.
* - native MyMediaScannerClient.scanFile() calls back on Java MediaScannerClient.scanFile,
@@ -609,6 +609,10 @@
mCompilation = parseSubstring(value, 0, 0);
} else if (name.equalsIgnoreCase("isdrm")) {
mIsDrm = (parseSubstring(value, 0, 0) == 1);
+ } else if (name.equalsIgnoreCase("width")) {
+ mWidth = parseSubstring(value, 0, 0);
+ } else if (name.equalsIgnoreCase("height")) {
+ mHeight = parseSubstring(value, 0, 0);
} else {
//Log.v(TAG, "unknown tag: " + name + " (" + mProcessGenres + ")");
}
@@ -734,9 +738,11 @@
map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
+ String resolution = null;
if (mWidth > 0 && mHeight > 0) {
map.put(MediaStore.MediaColumns.WIDTH, mWidth);
map.put(MediaStore.MediaColumns.HEIGHT, mHeight);
+ resolution = mWidth + "x" + mHeight;
}
if (!mNoMedia) {
@@ -746,7 +752,9 @@
map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0
? mAlbum : MediaStore.UNKNOWN_STRING));
map.put(Video.Media.DURATION, mDuration);
- // FIXME - add RESOLUTION
+ if (resolution != null) {
+ map.put(Video.Media.RESOLUTION, resolution);
+ }
} else if (MediaFile.isImageFileType(mFileType)) {
// FIXME - add DESCRIPTION
} else if (MediaFile.isAudioFileType(mFileType)) {
diff --git a/media/java/android/media/MediaSyncEvent.java b/media/java/android/media/MediaSyncEvent.java
new file mode 100644
index 0000000..d2a0735
--- /dev/null
+++ b/media/java/android/media/MediaSyncEvent.java
@@ -0,0 +1,123 @@
+/*
+ * 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.media;
+
+/**
+ * The MediaSyncEvent class defines events that can be used to synchronize playback or capture
+ * actions between different players and recorders.
+ * <p>For instance, {@link AudioRecord#startRecording(MediaSyncEvent)} is used to start capture
+ * only when the playback on a particular audio session is complete.
+ * The audio session ID is retrieved from a player (e.g {@link MediaPlayer}, {@link AudioTrack} or
+ * {@link ToneGenerator}) by use of the getAudioSessionId() method.
+ * @hide
+ */
+public class MediaSyncEvent {
+
+ /**
+ * No sync event specified. When used with a synchronized playback or capture method, the
+ * behavior is equivalent to calling the corresponding non synchronized method.
+ */
+ public static final int SYNC_EVENT_NONE = AudioSystem.SYNC_EVENT_NONE;
+
+ /**
+ * The corresponding action is triggered only when the presentation is completed
+ * (meaning the media has been presented to the user) on the specified session.
+ * A synchronization of this type requires a source audio session ID to be set via
+ * {@link #setAudioSessionId(int) method.
+ */
+ public static final int SYNC_EVENT_PRESENTATION_COMPLETE =
+ AudioSystem.SYNC_EVENT_PRESENTATION_COMPLETE;
+
+
+ /**
+ * Creates a synchronization event of the sepcified type.
+ *
+ * <p>The type specifies which kind of event is monitored.
+ * For instance, event {@link #SYNC_EVENT_PRESENTATION_COMPLETE} corresponds to the audio being
+ * presented to the user on a particular audio session.
+ * @param type the synchronization event type.
+ * @return the MediaSyncEvent created.
+ * @throws java.lang.IllegalArgumentException
+ */
+ public static MediaSyncEvent createEvent(int eventType)
+ throws IllegalArgumentException {
+ if (!isValidType(eventType)) {
+ throw (new IllegalArgumentException(eventType
+ + "is not a valid MediaSyncEvent type."));
+ } else {
+ return new MediaSyncEvent(eventType);
+ }
+ }
+
+ private final int mType;
+ private int mAudioSession = 0;
+
+ private MediaSyncEvent(int eventType) {
+ mType = eventType;
+ }
+
+ /**
+ * Sets the event source audio session ID.
+ *
+ * <p>The audio session ID specifies on which audio session the synchronization event should be
+ * monitored.
+ * It is mandatory for certain event types (e.g. {@link #SYNC_EVENT_PRESENTATION_COMPLETE}).
+ * For instance, the audio session ID can be retrieved via
+ * {@link MediaPlayer#getAudioSessionId()} when monitoring an event on a particular MediaPlayer.
+ * @param audioSessionId the audio session ID of the event source being monitored.
+ * @return the MediaSyncEvent the method is called on.
+ * @throws java.lang.IllegalArgumentException
+ */
+ public MediaSyncEvent setAudioSessionId(int audioSessionId)
+ throws IllegalArgumentException {
+ if (audioSessionId > 0) {
+ mAudioSession = audioSessionId;
+ } else {
+ throw (new IllegalArgumentException(audioSessionId + " is not a valid session ID."));
+ }
+ return this;
+ }
+
+ /**
+ * Gets the synchronization event type.
+ *
+ * @return the synchronization event type.
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Gets the synchronization event audio session ID.
+ *
+ * @return the synchronization audio session ID. The returned audio session ID is 0 if it has
+ * not been set.
+ */
+ public int getAudioSessionId() {
+ return mAudioSession;
+ }
+
+ private static boolean isValidType(int type) {
+ switch (type) {
+ case SYNC_EVENT_NONE:
+ case SYNC_EVENT_PRESENTATION_COMPLETE:
+ return true;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/media/java/android/media/ToneGenerator.java b/media/java/android/media/ToneGenerator.java
index d232265..4907a13 100644
--- a/media/java/android/media/ToneGenerator.java
+++ b/media/java/android/media/ToneGenerator.java
@@ -875,6 +875,15 @@
private native final void native_finalize();
+ /**
+ * Returns the audio session ID.
+ *
+ * @return the ID of the audio session this ToneGenerator belongs to or 0 if an error
+ * occured.
+ * @hide
+ */
+ public native final int getAudioSessionId();
+
@Override
protected void finalize() { native_finalize(); }
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index fcc3b13..a3361d4 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,7 +2,9 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ android_media_Crypto.cpp \
android_media_MediaCodec.cpp \
+ android_media_MediaCodecList.cpp \
android_media_MediaExtractor.cpp \
android_media_MediaPlayer.cpp \
android_media_MediaRecorder.cpp \
diff --git a/media/jni/android_media_Crypto.cpp b/media/jni/android_media_Crypto.cpp
new file mode 100644
index 0000000..e1a60a1
--- /dev/null
+++ b/media/jni/android_media_Crypto.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Crypto-JNI"
+#include <utils/Log.h>
+
+#include "android_media_Crypto.h"
+
+#include "android_runtime/AndroidRuntime.h"
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <binder/IServiceManager.h>
+#include <media/ICrypto.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+struct fields_t {
+ jfieldID context;
+};
+
+static fields_t gFields;
+
+static sp<JCrypto> getCrypto(JNIEnv *env, jobject thiz) {
+ return (JCrypto *)env->GetIntField(thiz, gFields.context);
+}
+
+JCrypto::JCrypto(
+ JNIEnv *env, jobject thiz,
+ const uint8_t uuid[16], const void *initData, size_t initSize) {
+ mObject = env->NewWeakGlobalRef(thiz);
+
+ mCrypto = MakeCrypto(uuid, initData, initSize);
+}
+
+JCrypto::~JCrypto() {
+ mCrypto.clear();
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ env->DeleteWeakGlobalRef(mObject);
+ mObject = NULL;
+}
+
+// static
+sp<ICrypto> JCrypto::MakeCrypto() {
+ sp<IServiceManager> sm = defaultServiceManager();
+
+ sp<IBinder> binder =
+ sm->getService(String16("media.player"));
+
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+
+ if (service == NULL) {
+ return NULL;
+ }
+
+ sp<ICrypto> crypto = service->makeCrypto();
+
+ if (crypto == NULL || crypto->initCheck() != OK) {
+ return NULL;
+ }
+
+ return crypto;
+}
+
+// static
+sp<ICrypto> JCrypto::MakeCrypto(
+ const uint8_t uuid[16], const void *initData, size_t initSize) {
+ sp<ICrypto> crypto = MakeCrypto();
+
+ if (crypto == NULL) {
+ return NULL;
+ }
+
+ status_t err = crypto->createPlugin(uuid, initData, initSize);
+
+ if (err != OK) {
+ return NULL;
+ }
+
+ return crypto;
+}
+
+bool JCrypto::requiresSecureDecoderComponent(const char *mime) const {
+ if (mCrypto == NULL) {
+ return false;
+ }
+
+ return mCrypto->requiresSecureDecoderComponent(mime);
+}
+
+// static
+bool JCrypto::IsCryptoSchemeSupported(const uint8_t uuid[16]) {
+ sp<ICrypto> crypto = MakeCrypto();
+
+ if (crypto == NULL) {
+ return false;
+ }
+
+ return crypto->isCryptoSchemeSupported(uuid);
+}
+
+status_t JCrypto::initCheck() const {
+ return mCrypto == NULL ? NO_INIT : OK;
+}
+
+// static
+sp<ICrypto> JCrypto::GetCrypto(JNIEnv *env, jobject obj) {
+ jclass clazz = env->FindClass("android/media/Crypto");
+ CHECK(clazz != NULL);
+
+ if (!env->IsInstanceOf(obj, clazz)) {
+ return NULL;
+ }
+
+ sp<JCrypto> jcrypto = getCrypto(env, obj);
+
+ if (jcrypto == NULL) {
+ return NULL;
+ }
+
+ return jcrypto->mCrypto;
+}
+
+} // namespace android
+
+using namespace android;
+
+static sp<JCrypto> setCrypto(
+ JNIEnv *env, jobject thiz, const sp<JCrypto> &crypto) {
+ sp<JCrypto> old = (JCrypto *)env->GetIntField(thiz, gFields.context);
+ if (crypto != NULL) {
+ crypto->incStrong(thiz);
+ }
+ if (old != NULL) {
+ old->decStrong(thiz);
+ }
+ env->SetIntField(thiz, gFields.context, (int)crypto.get());
+
+ return old;
+}
+
+static void android_media_Crypto_release(JNIEnv *env, jobject thiz) {
+ setCrypto(env, thiz, NULL);
+}
+
+static void android_media_Crypto_native_init(JNIEnv *env) {
+ jclass clazz = env->FindClass("android/media/Crypto");
+ CHECK(clazz != NULL);
+
+ gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+ CHECK(gFields.context != NULL);
+}
+
+static void android_media_Crypto_native_setup(
+ JNIEnv *env, jobject thiz,
+ jbyteArray uuidObj, jbyteArray initDataObj) {
+ jsize uuidLength = env->GetArrayLength(uuidObj);
+
+ if (uuidLength != 16) {
+ jniThrowException(
+ env,
+ "java/lang/IllegalArgumentException",
+ NULL);
+ return;
+ }
+
+ jboolean isCopy;
+ jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
+
+ jsize initDataLength = env->GetArrayLength(initDataObj);
+ jbyte *initData = env->GetByteArrayElements(initDataObj, &isCopy);
+
+ sp<JCrypto> crypto = new JCrypto(
+ env, thiz, (const uint8_t *)uuid, initData, initDataLength);
+
+ status_t err = crypto->initCheck();
+
+ env->ReleaseByteArrayElements(initDataObj, initData, 0);
+ initData = NULL;
+
+ env->ReleaseByteArrayElements(uuidObj, uuid, 0);
+ uuid = NULL;
+
+ if (err != OK) {
+ jniThrowException(
+ env,
+ "java/io/IOException",
+ "Failed to instantiate crypto object.");
+ return;
+ }
+
+ setCrypto(env,thiz, crypto);
+}
+
+static void android_media_Crypto_native_finalize(
+ JNIEnv *env, jobject thiz) {
+ android_media_Crypto_release(env, thiz);
+}
+
+static jboolean android_media_Crypto_isCryptoSchemeSupported(
+ JNIEnv *env, jobject thiz, jbyteArray uuidObj) {
+ jsize uuidLength = env->GetArrayLength(uuidObj);
+
+ if (uuidLength != 16) {
+ jniThrowException(
+ env,
+ "java/lang/IllegalArgumentException",
+ NULL);
+ return false;
+ }
+
+ jboolean isCopy;
+ jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
+
+ bool result = JCrypto::IsCryptoSchemeSupported((const uint8_t *)uuid);
+
+ env->ReleaseByteArrayElements(uuidObj, uuid, 0);
+ uuid = NULL;
+
+ return result;
+}
+
+static jboolean android_media_Crypto_requiresSecureDecoderComponent(
+ JNIEnv *env, jobject thiz, jstring mimeObj) {
+ if (mimeObj == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return false;
+ }
+
+ sp<JCrypto> crypto = getCrypto(env, thiz);
+
+ if (crypto == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return false;
+ }
+
+ const char *mime = env->GetStringUTFChars(mimeObj, NULL);
+
+ if (mime == NULL) {
+ return false;
+ }
+
+ bool result = crypto->requiresSecureDecoderComponent(mime);
+
+ env->ReleaseStringUTFChars(mimeObj, mime);
+ mime = NULL;
+
+ return result;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "release", "()V", (void *)android_media_Crypto_release },
+ { "native_init", "()V", (void *)android_media_Crypto_native_init },
+
+ { "native_setup", "([B[B)V",
+ (void *)android_media_Crypto_native_setup },
+
+ { "native_finalize", "()V",
+ (void *)android_media_Crypto_native_finalize },
+
+ { "isCryptoSchemeSupported", "([B)Z",
+ (void *)android_media_Crypto_isCryptoSchemeSupported },
+
+ { "requiresSecureDecoderComponent", "(Ljava/lang/String;)Z",
+ (void *)android_media_Crypto_requiresSecureDecoderComponent },
+};
+
+int register_android_media_Crypto(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/media/Crypto", gMethods, NELEM(gMethods));
+}
+
diff --git a/media/jni/android_media_Crypto.h b/media/jni/android_media_Crypto.h
new file mode 100644
index 0000000..505725e
--- /dev/null
+++ b/media/jni/android_media_Crypto.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef _ANDROID_MEDIA_CRYPTO_H_
+#define _ANDROID_MEDIA_CRYPTO_H_
+
+#include "jni.h"
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ICrypto;
+
+struct JCrypto : public RefBase {
+ static bool IsCryptoSchemeSupported(const uint8_t uuid[16]);
+
+ JCrypto(JNIEnv *env, jobject thiz,
+ const uint8_t uuid[16], const void *initData, size_t initSize);
+
+ status_t initCheck() const;
+
+ bool requiresSecureDecoderComponent(const char *mime) const;
+
+ static sp<ICrypto> GetCrypto(JNIEnv *env, jobject obj);
+
+protected:
+ virtual ~JCrypto();
+
+private:
+ jweak mObject;
+ sp<ICrypto> mCrypto;
+
+ static sp<ICrypto> MakeCrypto();
+
+ static sp<ICrypto> MakeCrypto(
+ const uint8_t uuid[16], const void *initData, size_t initSize);
+
+ DISALLOW_EVIL_CONSTRUCTORS(JCrypto);
+};
+
+} // namespace android
+
+#endif // _ANDROID_MEDIA_CRYPTO_H_
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 04d7c22..217216a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -20,6 +20,7 @@
#include "android_media_MediaCodec.h"
+#include "android_media_Crypto.h"
#include "android_media_Utils.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_view_Surface.h"
@@ -29,6 +30,7 @@
#include <gui/Surface.h>
#include <gui/SurfaceTextureClient.h>
+#include <media/ICrypto.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -97,12 +99,13 @@
status_t JMediaCodec::configure(
const sp<AMessage> &format,
const sp<ISurfaceTexture> &surfaceTexture,
+ const sp<ICrypto> &crypto,
int flags) {
sp<SurfaceTextureClient> client;
if (surfaceTexture != NULL) {
client = new SurfaceTextureClient(surfaceTexture);
}
- return mCodec->configure(format, client, flags);
+ return mCodec->configure(format, client, crypto, flags);
}
status_t JMediaCodec::start() {
@@ -255,6 +258,7 @@
jobject thiz,
jobjectArray keys, jobjectArray values,
jobject jsurface,
+ jobject jcrypto,
jint flags) {
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
@@ -285,7 +289,12 @@
}
}
- err = codec->configure(format, surfaceTexture, flags);
+ sp<ICrypto> crypto;
+ if (jcrypto != NULL) {
+ crypto = JCrypto::GetCrypto(env, jcrypto);
+ }
+
+ err = codec->configure(format, surfaceTexture, crypto, flags);
throwExceptionAsNecessary(env, err);
}
@@ -387,7 +396,7 @@
if (codec == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
+ return 0;
}
size_t index;
@@ -512,7 +521,8 @@
{ "release", "()V", (void *)android_media_MediaCodec_release },
{ "native_configure",
- "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;I)V",
+ "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
+ "Landroid/media/Crypto;I)V",
(void *)android_media_MediaCodec_native_configure },
{ "start", "()V", (void *)android_media_MediaCodec_start },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 6b1257d..6bb4071 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -27,6 +27,7 @@
struct ALooper;
struct AMessage;
+struct ICrypto;
struct ISurfaceTexture;
struct MediaCodec;
@@ -40,6 +41,7 @@
status_t configure(
const sp<AMessage> &format,
const sp<ISurfaceTexture> &surfaceTexture,
+ const sp<ICrypto> &crypto,
int flags);
status_t start();
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
new file mode 100644
index 0000000..2b8f91e
--- /dev/null
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodec-JNI"
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaCodecList.h>
+
+#include "android_runtime/AndroidRuntime.h"
+#include "jni.h"
+#include "JNIHelp.h"
+
+using namespace android;
+
+static jint android_media_MediaCodecList_countCodecs(
+ JNIEnv *env, jobject thiz) {
+ return MediaCodecList::getInstance()->countCodecs();
+}
+
+static jstring android_media_MediaCodecList_getCodecName(
+ JNIEnv *env, jobject thiz, jint index) {
+ const char *name = MediaCodecList::getInstance()->getCodecName(index);
+
+ if (name == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return NULL;
+ }
+
+ return env->NewStringUTF(name);
+}
+
+static jboolean android_media_MediaCodecList_isEncoder(
+ JNIEnv *env, jobject thiz, jint index) {
+ return MediaCodecList::getInstance()->isEncoder(index);
+}
+
+static jarray android_media_MediaCodecList_getSupportedTypes(
+ JNIEnv *env, jobject thiz, jint index) {
+ Vector<AString> types;
+ status_t err =
+ MediaCodecList::getInstance()->getSupportedTypes(index, &types);
+
+ if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return NULL;
+ }
+
+ jclass clazz = env->FindClass("java/lang/String");
+ CHECK(clazz != NULL);
+
+ jobjectArray array = env->NewObjectArray(types.size(), clazz, NULL);
+
+ for (size_t i = 0; i < types.size(); ++i) {
+ jstring obj = env->NewStringUTF(types.itemAt(i).c_str());
+ env->SetObjectArrayElement(array, i, obj);
+ env->DeleteLocalRef(obj);
+ obj = NULL;
+ }
+
+ return array;
+}
+
+static jobject android_media_MediaCodecList_getCodecCapabilities(
+ JNIEnv *env, jobject thiz, jint index, jstring type) {
+ if (type == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return NULL;
+ }
+
+ const char *typeStr = env->GetStringUTFChars(type, NULL);
+
+ if (typeStr == NULL) {
+ // Out of memory exception already pending.
+ return NULL;
+ }
+
+ Vector<MediaCodecList::ProfileLevel> profileLevels;
+ Vector<uint32_t> colorFormats;
+
+ status_t err =
+ MediaCodecList::getInstance()->getCodecCapabilities(
+ index, typeStr, &profileLevels, &colorFormats);
+
+ env->ReleaseStringUTFChars(type, typeStr);
+ typeStr = NULL;
+
+ if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return NULL;
+ }
+
+ jclass capsClazz =
+ env->FindClass("android/media/MediaCodecList$CodecCapabilities");
+ CHECK(capsClazz != NULL);
+
+ jobject caps = env->AllocObject(capsClazz);
+
+ jclass profileLevelClazz =
+ env->FindClass("android/media/MediaCodecList$CodecProfileLevel");
+ CHECK(profileLevelClazz != NULL);
+
+ jobjectArray profileLevelArray =
+ env->NewObjectArray(profileLevels.size(), profileLevelClazz, NULL);
+
+ jfieldID profileField =
+ env->GetFieldID(profileLevelClazz, "mProfile", "I");
+
+ jfieldID levelField =
+ env->GetFieldID(profileLevelClazz, "mLevel", "I");
+
+ for (size_t i = 0; i < profileLevels.size(); ++i) {
+ const MediaCodecList::ProfileLevel &src = profileLevels.itemAt(i);
+
+ jobject profileLevelObj = env->AllocObject(profileLevelClazz);
+
+ env->SetIntField(profileLevelObj, profileField, src.mProfile);
+ env->SetIntField(profileLevelObj, levelField, src.mLevel);
+
+ env->SetObjectArrayElement(profileLevelArray, i, profileLevelObj);
+
+ env->DeleteLocalRef(profileLevelObj);
+ profileLevelObj = NULL;
+ }
+
+ jfieldID profileLevelsField = env->GetFieldID(
+ capsClazz,
+ "mProfileLevels",
+ "[Landroid/media/MediaCodecList$CodecProfileLevel;");
+
+ env->SetObjectField(caps, profileLevelsField, profileLevelArray);
+
+ env->DeleteLocalRef(profileLevelArray);
+ profileLevelArray = NULL;
+
+ jintArray colorFormatsArray = env->NewIntArray(colorFormats.size());
+
+ for (size_t i = 0; i < colorFormats.size(); ++i) {
+ jint val = colorFormats.itemAt(i);
+ env->SetIntArrayRegion(colorFormatsArray, i, 1, &val);
+ }
+
+ jfieldID colorFormatsField = env->GetFieldID(
+ capsClazz, "mColorFormats", "[I");
+
+ env->SetObjectField(caps, colorFormatsField, colorFormatsArray);
+
+ env->DeleteLocalRef(colorFormatsArray);
+ colorFormatsArray = NULL;
+
+ return caps;
+}
+
+static void android_media_MediaCodecList_native_init(JNIEnv *env) {
+}
+
+static JNINativeMethod gMethods[] = {
+ { "countCodecs", "()I", (void *)android_media_MediaCodecList_countCodecs },
+ { "getCodecName", "(I)Ljava/lang/String;",
+ (void *)android_media_MediaCodecList_getCodecName },
+ { "isEncoder", "(I)Z", (void *)android_media_MediaCodecList_isEncoder },
+ { "getSupportedTypes", "(I)[Ljava/lang/String;",
+ (void *)android_media_MediaCodecList_getSupportedTypes },
+
+ { "getCodecCapabilities",
+ "(ILjava/lang/String;)Landroid/media/MediaCodecList$CodecCapabilities;",
+ (void *)android_media_MediaCodecList_getCodecCapabilities },
+
+ { "native_init", "()V", (void *)android_media_MediaCodecList_native_init },
+};
+
+int register_android_media_MediaCodecList(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/media/MediaCodecList", gMethods, NELEM(gMethods));
+}
+
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 745e253..2e74ffd 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -879,8 +879,10 @@
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
+extern int register_android_media_Crypto(JNIEnv *env);
extern int register_android_media_MediaCodec(JNIEnv *env);
extern int register_android_media_MediaExtractor(JNIEnv *env);
+extern int register_android_media_MediaCodecList(JNIEnv *env);
extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
extern int register_android_media_MediaRecorder(JNIEnv *env);
extern int register_android_media_MediaScanner(JNIEnv *env);
@@ -962,6 +964,16 @@
goto bail;
}
+ if (register_android_media_MediaCodecList(env) < 0) {
+ ALOGE("ERROR: MediaCodec native registration failed");
+ goto bail;
+ }
+
+ if (register_android_media_Crypto(env) < 0) {
+ ALOGE("ERROR: MediaCodec native registration failed");
+ goto bail;
+ }
+
/* success -- return valid version number */
result = JNI_VERSION_1_4;
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index dd5e026..b698705 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -4,9 +4,9 @@
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.
@@ -16,7 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.mediaframeworktest">
-
+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
@@ -24,7 +24,7 @@
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
- <application>
+ <application>
<uses-library android:name="android.test.runner" />
<activity android:label="@string/app_name"
android:name="MediaFrameworkTest"
@@ -34,12 +34,18 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
- </application>
+ </application>
+
+ <instrumentation android:name=".CameraStressTestRunner"
+ android:targetPackage="com.android.mediaframeworktest"
+ android:label="Camera stress tests InstrumentationRunner">
+ </instrumentation>
+
<instrumentation android:name=".MediaFrameworkTestRunner"
android:targetPackage="com.android.mediaframeworktest"
android:label="MediaFramework tests InstrumentationRunner">
</instrumentation>
-
+
<instrumentation android:name=".MediaFrameworkPerfTestRunner"
android:targetPackage="com.android.mediaframeworktest"
android:label="MediaFramework Performance tests InstrumentationRunner">
@@ -49,7 +55,7 @@
android:targetPackage="com.android.mediaframeworktest"
android:label="MediaFramework unit tests InstrumentationRunner">
</instrumentation>
-
+
<instrumentation android:name=".MediaRecorderStressTestRunner"
android:targetPackage="com.android.mediaframeworktest"
android:label="MediaRecorder stress tests InstrumentationRunner">
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/CameraStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/CameraStressTestRunner.java
new file mode 100644
index 0000000..fa59fa4
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/CameraStressTestRunner.java
@@ -0,0 +1,38 @@
+/*
+ * 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.mediaframeworktest;
+
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import com.android.mediaframeworktest.stress.CameraStressTest;
+
+import junit.framework.TestSuite;
+
+public class CameraStressTestRunner extends InstrumentationTestRunner {
+
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(CameraStressTest.class);
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return CameraStressTestRunner.class.getClassLoader();
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java
index c501d3f..7be2707 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/mediaplayback/MediaPlayerApiTest.java
@@ -22,7 +22,7 @@
import com.android.mediaframeworktest.functional.CodecTest;
import android.content.Context;
-import android.test.ActivityInstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
@@ -33,25 +33,28 @@
/**
* Junit / Instrumentation test case for the media player api
*/
-public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFrameworkTest> {
- private boolean duratoinWithinTolerence = false;
- private String TAG = "MediaPlayerApiTest";
- private boolean isWMAEnable = false;
- private boolean isWMVEnable = false;
+public class MediaPlayerApiTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private boolean duratoinWithinTolerence = false;
+ private String TAG = "MediaPlayerApiTest";
+ private boolean isWMAEnable = false;
+ private boolean isWMVEnable = false;
- Context mContext;
+ Context mContext;
- public MediaPlayerApiTest() {
- super("com.android.mediaframeworktest", MediaFrameworkTest.class);
- isWMAEnable = MediaProfileReader.getWMAEnable();
- isWMVEnable = MediaProfileReader.getWMVEnable();
- }
+ public MediaPlayerApiTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ isWMAEnable = MediaProfileReader.getWMAEnable();
+ isWMVEnable = MediaProfileReader.getWMVEnable();
+ }
protected void setUp() throws Exception {
- super.setUp();
-
- }
-
+ //Insert a 2 second before launching the test activity. This is
+ //the workaround for the race condition of requesting the updated surface.
+ Thread.sleep(2000);
+ getActivity();
+ super.setUp();
+ }
+
public boolean verifyDuration(int duration, int expectedDuration){
if ((duration > expectedDuration * 1.1) || (duration < expectedDuration * 0.9))
return false;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
new file mode 100644
index 0000000..a9c6119
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
@@ -0,0 +1,280 @@
+/*
+ * 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.mediaframeworktest.stress;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.FileWriter;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import android.hardware.Camera;
+import android.hardware.Camera.PictureCallback;
+import android.hardware.Camera.ShutterCallback;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import com.android.mediaframeworktest.CameraStressTestRunner;
+
+import junit.framework.Assert;
+
+/**
+ * Junit / Instrumentation test case for the camera zoom api
+ *
+ * adb shell am instrument
+ * -e class com.android.mediaframeworktest.stress.CameraStressTest
+ * -w com.android.mediaframeworktest/.CameraStressTestRunner
+ */
+public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "CameraStressTest";
+ private Camera mCamera;
+
+ private static final int NUMBER_OF_ZOOM_LOOPS = 100;
+ private static final long WAIT_GENERIC = 3 * 1000; // 3 seconds
+ private static final long WAIT_TIMEOUT = 10 * 1000; // 10 seconds
+ private static final long WAIT_ZOOM_ANIMATION = 5 * 1000; // 5 seconds
+ private static final String CAMERA_STRESS_OUTPUT =
+ "/sdcard/cameraStressOutput.txt";
+ private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
+
+ private Thread mLooperThread;
+ private Handler mHandler;
+
+ public CameraStressTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ protected void setUp() throws Exception {
+ final Semaphore sem = new Semaphore(0);
+ mLooperThread = new Thread() {
+ @Override
+ public void run() {
+ Log.v(TAG, "starting looper");
+ Looper.prepare();
+ mHandler = new Handler();
+ sem.release();
+ Looper.loop();
+ Log.v(TAG, "quit looper");
+ }
+ };
+ mLooperThread.start();
+ if (!sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ fail("Failed to start the looper.");
+ }
+ getActivity();
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mHandler != null) {
+ mHandler.getLooper().quit();
+ mHandler = null;
+ }
+ if (mLooperThread != null) {
+ mLooperThread.join(WAIT_TIMEOUT);
+ if (mLooperThread.isAlive()) {
+ fail("Failed to stop the looper.");
+ }
+ mLooperThread = null;
+ }
+
+ super.tearDown();
+ }
+
+ private void runOnLooper(final Runnable command) throws InterruptedException {
+ final Semaphore sem = new Semaphore(0);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ command.run();
+ } finally {
+ sem.release();
+ }
+ }
+ });
+ if (!sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ fail("Failed to run the command on the looper.");
+ }
+ }
+
+ private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback {
+ public void onError(int error, android.hardware.Camera camera) {
+ if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
+ assertTrue("Camera test mediaserver died", false);
+ }
+ }
+ }
+
+ private ShutterCallback shutterCallback = new ShutterCallback() {
+ @Override
+ public void onShutter() {
+ Log.v(TAG, "Shutter");
+ }
+ };
+
+ private PictureCallback rawCallback = new PictureCallback() {
+ @Override
+ public void onPictureTaken(byte[] data, Camera camera) {
+ Log.v(TAG, "Raw picture taken");
+ }
+ };
+
+ private PictureCallback jpegCallback = new PictureCallback() {
+ @Override
+ public void onPictureTaken(byte[] data, Camera camera) {
+ FileOutputStream fos = null;
+
+ try {
+ Log.v(TAG, "JPEG picture taken");
+ fos = new FileOutputStream(String.format("%s/zoom-test-%d.jpg",
+ Environment.getExternalStorageDirectory(), System.currentTimeMillis()));
+ fos.write(data);
+ }
+ catch (FileNotFoundException e) {
+ Log.v(TAG, "File not found: " + e.toString());
+ }
+ catch (IOException e) {
+ Log.v(TAG, "Error accessing file: " + e.toString());
+ }
+ finally {
+ try {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ catch (IOException e) {
+ Log.v(TAG, "Error closing file: " + e.toString());
+ }
+ }
+ }
+ };
+
+ // Helper method for cleaning up pics taken during testStressCameraZoom
+ private void cleanupZoomImages() {
+ try {
+ File sdcard = Environment.getExternalStorageDirectory();
+ File[] zoomImages = null;
+
+ FilenameFilter filter = new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.startsWith("zoom-test-");
+ }
+ };
+
+ zoomImages = sdcard.listFiles(filter);
+
+ for (File f : zoomImages) {
+ f.delete();
+ }
+ }
+ catch (SecurityException e) {
+ Log.v(TAG, "Security manager access violation: " + e.toString());
+ }
+ }
+
+ // Test case for stressing the camera zoom in/out feature
+ @LargeTest
+ public void testStressCameraZoom() throws Exception {
+ SurfaceHolder mSurfaceHolder;
+ mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+ File stressOutFile = new File(CAMERA_STRESS_OUTPUT);
+ Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
+ output.write("Camera zoom stress:\n");
+ output.write("Total number of loops: " + NUMBER_OF_ZOOM_LOOPS + "\n");
+
+ try {
+ Log.v(TAG, "Start preview");
+ output.write("No of loop: ");
+
+ mCamera = Camera.open();
+ Camera.Parameters params = mCamera.getParameters();
+ mCamera.release();
+
+ if (!params.isSmoothZoomSupported() && !params.isZoomSupported()) {
+ Log.v(TAG, "Device camera does not support zoom");
+ assertTrue("Camera zoom stress test", false);
+ }
+ else {
+ Log.v(TAG, "Device camera does support zoom");
+
+ int nextZoomLevel = 0;
+
+ for (int i = 0; i < NUMBER_OF_ZOOM_LOOPS; i++) {
+ runOnLooper(new Runnable() {
+ @Override
+ public void run() {
+ mCamera = Camera.open();
+ }
+ });
+
+ mCamera.setErrorCallback(mCameraErrorCallback);
+ mCamera.setPreviewDisplay(mSurfaceHolder);
+ mCamera.startPreview();
+ Thread.sleep(WAIT_GENERIC);
+
+ params = mCamera.getParameters();
+ int currentZoomLevel = params.getZoom();
+
+ if (nextZoomLevel >= params.getMaxZoom()) {
+ nextZoomLevel = 0;
+ }
+ ++nextZoomLevel;
+
+ if (params.isSmoothZoomSupported()) {
+ mCamera.startSmoothZoom(nextZoomLevel);
+ }
+ else {
+ params.setZoom(nextZoomLevel);
+ mCamera.setParameters(params);
+ }
+ Log.v(TAG, "Zooming from " + currentZoomLevel + " to " + nextZoomLevel);
+
+ // sleep allows for zoom animation to finish
+ Thread.sleep(WAIT_ZOOM_ANIMATION);
+
+ // take picture
+ mCamera.takePicture(shutterCallback, rawCallback, jpegCallback);
+ Thread.sleep(WAIT_GENERIC);
+ mCamera.stopPreview();
+ mCamera.release();
+ output.write(" ," + i);
+ }
+ }
+
+ cleanupZoomImages();
+ }
+ catch (Exception e) {
+ assertTrue("Camera zoom stress test Exception", false);
+ Log.v(TAG, e.toString());
+ }
+ output.write("\n\n");
+ output.close();
+ }
+}
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
index aa27861..715ccba 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -66,7 +66,7 @@
android:layout_height="wrap_content"
android:textSize="@dimen/status_bar_recents_app_label_text_size"
android:fadingEdge="horizontal"
- android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
android:scrollHorizontally="true"
android:layout_alignLeft="@id/app_thumbnail"
android:layout_below="@id/app_thumbnail"
@@ -82,7 +82,7 @@
android:layout_height="wrap_content"
android:textSize="@dimen/status_bar_recents_app_description_text_size"
android:fadingEdge="horizontal"
- android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
android:scrollHorizontally="true"
android:layout_alignLeft="@id/app_thumbnail"
android:layout_below="@id/app_label"
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
index 180f022..dba1dd9 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -41,7 +41,7 @@
android:stackFromBottom="true"
android:fadingEdge="horizontal"
android:scrollbars="none"
- android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:fadingEdgeLength="@dimen/status_bar_recents_scroll_fading_edge_length"
android:layout_gravity="bottom|left"
android:orientation="horizontal"
android:clipToPadding="false"
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
index bc389f9..ca72530 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
@@ -35,7 +35,7 @@
android:layout_height="wrap_content"
android:textSize="@dimen/status_bar_recents_app_label_text_size"
android:fadingEdge="horizontal"
- android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
android:scrollHorizontally="true"
android:layout_alignParentLeft="true"
android:layout_alignTop="@id/app_icon"
@@ -89,7 +89,7 @@
android:layout_height="wrap_content"
android:textSize="@dimen/status_bar_recents_app_description_text_size"
android:fadingEdge="horizontal"
- android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
android:scrollHorizontally="true"
android:layout_alignParentLeft="true"
android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
index e6a077a..73c3da5 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
@@ -40,7 +40,7 @@
android:stackFromBottom="true"
android:fadingEdge="vertical"
android:scrollbars="none"
- android:fadingEdgeLength="@*android:dimen/status_bar_height"
+ android:fadingEdgeLength="@dimen/status_bar_recents_scroll_fading_edge_length"
android:layout_gravity="bottom|left"
android:clipToPadding="false"
android:clipChildren="false">
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
index 333fcda..7d639ec 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -28,7 +28,7 @@
android:layout_height="wrap_content"
android:textSize="@dimen/status_bar_recents_app_label_text_size"
android:fadingEdge="horizontal"
- android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
android:scrollHorizontally="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
@@ -86,7 +86,7 @@
android:layout_height="wrap_content"
android:textSize="@dimen/status_bar_recents_app_description_text_size"
android:fadingEdge="horizontal"
- android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+ android:fadingEdgeLength="@dimen/status_bar_recents_text_fading_edge_length"
android:scrollHorizontally="true"
android:layout_alignParentLeft="true"
android:layout_below="@id/recents_callout_line"
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index ba1cdfa..8c1ac55 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -53,8 +53,10 @@
<dimen name="status_bar_recents_app_label_width">97dip</dimen>
<!-- Left margin for application label -->
<dimen name="status_bar_recents_app_label_left_margin">16dip</dimen>
- <!-- Size of fading edge for scroll effect -->
- <dimen name="status_bar_recents_fading_edge_length">20dip</dimen>
+ <!-- Size of fading edge for text -->
+ <dimen name="status_bar_recents_text_fading_edge_length">20dip</dimen>
+ <!-- Size of fading edge for scrolling -->
+ <dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
<!-- Margin between recents container and glow on the right -->
<dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2c22e3c..4441ca6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -33,8 +33,10 @@
<dimen name="status_bar_recents_app_label_text_size">14dip</dimen>
<!-- Size of application description text -->
<dimen name="status_bar_recents_app_description_text_size">14dip</dimen>
- <!-- Size of fading edge for scroll effect -->
- <dimen name="status_bar_recents_fading_edge_length">20dip</dimen>
+ <!-- Size of fading edge for text -->
+ <dimen name="status_bar_recents_text_fading_edge_length">20dip</dimen>
+ <!-- Size of fading edge for scrolling -->
+ <dimen name="status_bar_recents_scroll_fading_edge_length">10dip</dimen>
<!-- Margin between recents container and glow on the right -->
<dimen name="status_bar_recents_right_glow_margin">100dip</dimen>
<!-- Amount to offset bottom of notification peek window from top of status bar. -->
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index ebed522..5d6e315 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -68,6 +68,7 @@
static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
private Context mContext;
private BaseStatusBar mBar;
+ private PopupMenu mPopup;
private View mRecentsScrim;
private View mRecentsNoApps;
private ViewGroup mRecentsContainer;
@@ -313,6 +314,10 @@
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
+ } else {
+ if (mPopup != null) {
+ mPopup.dismiss();
+ }
}
}
@@ -325,6 +330,7 @@
setVisibility(View.GONE);
}
if (mBar != null) {
+ // This will indirectly cause show(false, ...) to get called
mBar.animateCollapse();
}
}
@@ -729,10 +735,20 @@
getContext().startActivity(intent);
}
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mPopup != null) {
+ return true;
+ } else {
+ return super.onInterceptTouchEvent(ev);
+ }
+ }
+
public void handleLongPress(
final View selectedView, final View anchorView, final View thumbnailView) {
thumbnailView.setSelected(true);
- PopupMenu popup = new PopupMenu(mContext, anchorView == null ? selectedView : anchorView);
+ final PopupMenu popup =
+ new PopupMenu(mContext, anchorView == null ? selectedView : anchorView);
+ mPopup = popup;
popup.getMenuInflater().inflate(R.menu.recent_popup_menu, popup.getMenu());
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
@@ -756,6 +772,7 @@
popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
public void onDismiss(PopupMenu menu) {
thumbnailView.setSelected(false);
+ mPopup = null;
}
});
popup.show();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
index 5529d0c..6bd1826 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
@@ -37,15 +37,11 @@
public static final boolean OPTIMIZE_SW_RENDERED_RECENTS = true;
public static final boolean USE_DARK_FADE_IN_HW_ACCELERATED_MODE = true;
private View mScrollView;
- private LinearLayout mLinearLayout;
private RecentsCallback mCallback;
- private boolean mShowBackground = false;
private int mFadingEdgeLength;
- private Drawable.ConstantState mBackgroundDrawable;
private Context mContext;
private boolean mIsVertical;
- private boolean mFirstTime = true;
private boolean mSoftwareRendered = false;
private boolean mAttachedToWindow = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 23222f2..8d7afc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -19,6 +19,9 @@
import java.util.ArrayList;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -119,7 +122,7 @@
createAndAddWindows();
disable(switches[0]);
- setSystemUiVisibility(switches[1]);
+ setSystemUiVisibility(switches[1], 0xffffffff);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4]);
@@ -181,6 +184,25 @@
return vetoButton;
}
+
+ protected void applyLegacyRowBackground(StatusBarNotification sbn, View content) {
+ if (sbn.notification.contentView.getLayoutId() !=
+ com.android.internal.R.layout.notification_template_base) {
+ int version = 0;
+ try {
+ ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.pkg, 0);
+ version = info.targetSdkVersion;
+ } catch (NameNotFoundException ex) {
+ Slog.e(TAG, "Failed looking up ApplicationInfo for " + sbn.pkg, ex);
+ }
+ if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
+ content.setBackgroundResource(R.drawable.notification_row_legacy_bg);
+ } else {
+ content.setBackgroundResource(R.drawable.notification_row_bg);
+ }
+ }
+ }
+
public void dismissIntruder() {
// pass
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 37ab58a..f88a3cc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -89,7 +89,7 @@
public void disable(int state);
public void animateExpand();
public void animateCollapse();
- public void setSystemUiVisibility(int vis);
+ public void setSystemUiVisibility(int vis, int mask);
public void topAppWindowChanged(boolean visible);
public void setImeWindowStatus(IBinder token, int vis, int backDisposition);
public void setHardKeyboardStatus(boolean available, boolean enabled);
@@ -165,10 +165,10 @@
}
}
- public void setSystemUiVisibility(int vis) {
+ public void setSystemUiVisibility(int vis, int mask) {
synchronized (mList) {
mHandler.removeMessages(MSG_SET_SYSTEMUI_VISIBILITY);
- mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, 0, null).sendToTarget();
+ mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget();
}
}
@@ -279,7 +279,7 @@
}
break;
case MSG_SET_SYSTEMUI_VISIBILITY:
- mCallbacks.setSystemUiVisibility(msg.arg1);
+ mCallbacks.setSystemUiVisibility(msg.arg1, msg.arg2);
break;
case MSG_TOP_APP_WINDOW_CHANGED:
mCallbacks.topAppWindowChanged(msg.arg1 != 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 6574c7d..5582b0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -139,7 +139,7 @@
setLowProfile(false, false, false);
try {
- mBarService.setSystemUiVisibility(0);
+ mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
} catch (android.os.RemoteException ex) {
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 7c679b2..12c05ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -895,24 +895,6 @@
return true;
}
- void applyLegacyRowBackground(StatusBarNotification sbn, View content) {
- if (sbn.notification.contentView.getLayoutId() !=
- com.android.internal.R.layout.status_bar_latest_event_content) {
- int version = 0;
- try {
- ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.pkg, 0);
- version = info.targetSdkVersion;
- } catch (NameNotFoundException ex) {
- Slog.e(TAG, "Failed looking up ApplicationInfo for " + sbn.pkg, ex);
- }
- if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
- content.setBackgroundResource(R.drawable.notification_row_legacy_bg);
- } else {
- content.setBackgroundResource(R.drawable.notification_row_bg);
- }
- }
- }
-
StatusBarNotification removeNotificationViews(IBinder key) {
NotificationData.Entry entry = mNotificationData.remove(key);
if (entry == null) {
@@ -1471,12 +1453,13 @@
}
@Override // CommandQueue
- public void setSystemUiVisibility(int vis) {
- final int old = mSystemUiVisibility;
- final int diff = vis ^ old;
+ public void setSystemUiVisibility(int vis, int mask) {
+ final int oldVal = mSystemUiVisibility;
+ final int newVal = (oldVal&~mask) | (vis&mask);
+ final int diff = newVal ^ oldVal;
if (diff != 0) {
- mSystemUiVisibility = vis;
+ mSystemUiVisibility = newVal;
if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
final boolean lightsOut = (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE));
@@ -1495,9 +1478,9 @@
public void setLightsOn(boolean on) {
Log.v(TAG, "setLightsOn(" + on + ")");
if (on) {
- setSystemUiVisibility(mSystemUiVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
} else {
- setSystemUiVisibility(mSystemUiVisibility | View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 2491d18..7325a37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -594,7 +594,7 @@
mBarContents.setVisibility(View.VISIBLE);
try {
- mBarService.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
+ mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
} catch (RemoteException ex) {
// system process dead
}
@@ -1165,14 +1165,20 @@
}
@Override // CommandQueue
- public void setSystemUiVisibility(int vis) {
- if (vis != mSystemUiVisibility) {
- mSystemUiVisibility = vis;
+ public void setSystemUiVisibility(int vis, int mask) {
+ final int oldVal = mSystemUiVisibility;
+ final int newVal = (oldVal&~mask) | (vis&mask);
+ final int diff = newVal ^ oldVal;
- mHandler.removeMessages(MSG_HIDE_CHROME);
- mHandler.removeMessages(MSG_SHOW_CHROME);
- mHandler.sendEmptyMessage(0 == (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE)
- ? MSG_SHOW_CHROME : MSG_HIDE_CHROME);
+ if (diff != 0) {
+ mSystemUiVisibility = newVal;
+
+ if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
+ mHandler.removeMessages(MSG_HIDE_CHROME);
+ mHandler.removeMessages(MSG_SHOW_CHROME);
+ mHandler.sendEmptyMessage(0 == (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE)
+ ? MSG_SHOW_CHROME : MSG_HIDE_CHROME);
+ }
notifyUiVisibilityChanged();
}
@@ -1187,9 +1193,9 @@
Slog.v(TAG, "setLightsOn(" + on + ")");
if (on) {
- setSystemUiVisibility(mSystemUiVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
} else {
- setSystemUiVisibility(mSystemUiVisibility | View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
}
@@ -1880,24 +1886,6 @@
return true;
}
- void applyLegacyRowBackground(StatusBarNotification sbn, View content) {
- if (sbn.notification.contentView.getLayoutId() !=
- com.android.internal.R.layout.status_bar_latest_event_content) {
- int version = 0;
- try {
- ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.pkg, 0);
- version = info.targetSdkVersion;
- } catch (NameNotFoundException ex) {
- Slog.e(TAG, "Failed looking up ApplicationInfo for " + sbn.pkg, ex);
- }
- if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
- content.setBackgroundResource(R.drawable.notification_row_legacy_bg);
- } else {
- content.setBackgroundResource(R.drawable.notification_row_bg);
- }
- }
- }
-
public void clearAll() {
try {
mBarService.onClearAllNotifications();
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 301dbf5..c3c49b0 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2829,6 +2829,9 @@
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
+ // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
+ mDecor.makeOptionalFitsSystemWindows();
+
mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
if (mTitleView != null) {
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 6214086..fb1e106 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -230,6 +230,13 @@
static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
+ /**
+ * These are the system UI flags that, when changing, can cause the layout
+ * of the screen to change.
+ */
+ static final int SYSTEM_UI_CHANGING_LAYOUT =
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
+
// Useful scan codes.
private static final int SW_LID = 0x00;
private static final int BTN_MOUSE = 0x110;
@@ -391,6 +398,8 @@
// that area of the display from all other windows.
int mRestrictedScreenLeft, mRestrictedScreenTop;
int mRestrictedScreenWidth, mRestrictedScreenHeight;
+ // For applications requesting stable content insets, these are them.
+ int mStableLeft, mStableTop, mStableRight, mStableBottom;
// During layout, the current screen borders with all outer decoration
// (status bar, input method dock) accounted for.
int mCurLeft, mCurTop, mCurRight, mCurBottom;
@@ -1958,10 +1967,15 @@
// When the user taps down, we re-show the nav bar.
boolean changed = false;
synchronized (mLock) {
- // Any user activity always causes us to show the navigation controls,
- // if they had been hidden.
- int newVal = mResettingSystemUiFlags
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ // Any user activity always causes us to show the
+ // navigation controls, if they had been hidden.
+ // We also clear the low profile and only content
+ // flags so that tapping on the screen will atomically
+ // restore all currently hidden screen decorations.
+ int newVal = mResettingSystemUiFlags |
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LOW_PROFILE |
+ View.SYSTEM_UI_FLAG_FULLSCREEN;
if (mResettingSystemUiFlags != newVal) {
mResettingSystemUiFlags = newVal;
changed = true;
@@ -1969,14 +1983,15 @@
// We don't allow the system's nav bar to be hidden
// again for 1 second, to prevent applications from
// spamming us and keeping it from being shown.
- newVal = mForceClearedSystemUiFlags
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ newVal = mForceClearedSystemUiFlags |
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
if (mForceClearedSystemUiFlags != newVal) {
mForceClearedSystemUiFlags = newVal;
changed = true;
mHandler.postDelayed(new Runnable() {
@Override public void run() {
synchronized (mLock) {
+ // Clear flags.
mForceClearedSystemUiFlags &=
~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
@@ -2017,17 +2032,33 @@
public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset) {
final int fl = attrs.flags;
-
+
if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
== (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
- contentInset.set(mCurLeft, mCurTop,
- (mRestrictedScreenLeft+mRestrictedScreenWidth) - mCurRight,
- (mRestrictedScreenTop+mRestrictedScreenHeight) - mCurBottom);
- } else {
- contentInset.setEmpty();
+ int availRight, availBottom;
+ if ((attrs.systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
+ availRight = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
+ availBottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ } else {
+ availRight = mRestrictedScreenLeft + mRestrictedScreenWidth;
+ availBottom = mRestrictedScreenTop + mRestrictedScreenHeight;
+ }
+ if ((attrs.systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
+ contentInset.set(mStableLeft, mStableTop,
+ availRight - mStableRight, availBottom - mStableBottom);
+ } else if ((attrs.systemUiVisibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) == 0) {
+ contentInset.set(mCurLeft, mCurTop,
+ availRight - mCurRight, availBottom - mCurBottom);
+ } else {
+ contentInset.set(mCurLeft, mCurTop,
+ availRight - mCurRight, availBottom - mCurBottom);
+ }
+ return;
}
+ contentInset.setEmpty();
}
-
+
/** {@inheritDoc} */
public void beginLayoutLw(int displayWidth, int displayHeight, int displayRotation) {
mUnrestrictedScreenLeft = mUnrestrictedScreenTop = 0;
@@ -2036,10 +2067,10 @@
mRestrictedScreenLeft = mRestrictedScreenTop = 0;
mRestrictedScreenWidth = displayWidth;
mRestrictedScreenHeight = displayHeight;
- mDockLeft = mContentLeft = mCurLeft = 0;
- mDockTop = mContentTop = mCurTop = 0;
- mDockRight = mContentRight = mCurRight = displayWidth;
- mDockBottom = mContentBottom = mCurBottom = displayHeight;
+ mDockLeft = mContentLeft = mStableLeft = mCurLeft = 0;
+ mDockTop = mContentTop = mStableTop = mCurTop = 0;
+ mDockRight = mContentRight = mStableRight = mCurRight = displayWidth;
+ mDockBottom = mContentBottom = mStableBottom = mCurBottom = displayHeight;
mDockLayer = 0x10000000;
// start with the current dock rect, which will be (0,0,displayWidth,displayHeight)
@@ -2081,6 +2112,7 @@
// Portrait screen; nav bar goes on bottom.
mTmpNavigationFrame.set(0, displayHeight-mNavigationBarHeight,
displayWidth, displayHeight);
+ mStableBottom = mTmpNavigationFrame.top;
if (navVisible) {
mDockBottom = mTmpNavigationFrame.top;
mRestrictedScreenHeight = mDockBottom - mDockTop;
@@ -2094,6 +2126,7 @@
// Landscape screen; nav bar goes to the right.
mTmpNavigationFrame.set(displayWidth-mNavigationBarWidth, 0,
displayWidth, displayHeight);
+ mStableRight = mTmpNavigationFrame.left;
if (navVisible) {
mDockRight = mTmpNavigationFrame.left;
mRestrictedScreenWidth = mDockRight - mDockLeft;
@@ -2125,11 +2158,23 @@
pf.bottom = df.bottom = vf.bottom = mDockBottom;
mStatusBar.computeFrameLw(pf, df, vf, vf);
+ final Rect r = mStatusBar.getFrameLw();
+
+ // Compute the stable dimensions whether or not the status bar is hidden.
+ if (mStatusBarCanHide) {
+ if (mDockTop == r.top) mStableTop = r.bottom;
+ else if (mDockBottom == r.bottom) mStableBottom = r.top;
+ } else {
+ if (mStableTop == r.top) {
+ mStableTop = r.bottom;
+ } else if (mStableBottom == r.bottom) {
+ mStableBottom = r.top;
+ }
+ }
if (mStatusBar.isVisibleLw()) {
// If the status bar is hidden, we don't want to cause
// windows behind it to scroll.
- final Rect r = mStatusBar.getFrameLw();
if (mStatusBarCanHide) {
// Status bar may go away, so the screen area it occupies
// is available to apps but just covering them when the
@@ -2229,7 +2274,8 @@
final int fl = attrs.flags;
final int sim = attrs.softInputMode;
-
+ final int sysUiFl = win.getSystemUiVisibility();
+
final Rect pf = mTmpParentFrame;
final Rect df = mTmpDisplayFrame;
final Rect cf = mTmpContentFrame;
@@ -2250,7 +2296,8 @@
final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
- == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
+ == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)
+ && (sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
if (DEBUG_LAYOUT)
Log.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ "): IN_SCREEN, INSET_DECOR, !FULLSCREEN");
@@ -2286,6 +2333,17 @@
"Laying out status bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
}
+ } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
+ && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
+ && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+ // Asking for layout as if the nav bar is hidden, lets the
+ // application extend into the unrestricted screen area. We
+ // only do this for application windows to ensure no window that
+ // can be above the nav bar can do this.
+ pf.left = df.left = mUnrestrictedScreenLeft;
+ pf.top = df.top = mUnrestrictedScreenTop;
+ pf.right = df.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
+ pf.bottom = df.bottom = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
} else {
pf.left = df.left = mRestrictedScreenLeft;
pf.top = df.top = mRestrictedScreenTop;
@@ -2303,6 +2361,14 @@
cf.right = mContentRight;
cf.bottom = mContentBottom;
}
+ if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
+ // If app is requesting a stable layout, don't let the
+ // content insets go below the stable values.
+ if (cf.left < mStableLeft) cf.left = mStableLeft;
+ if (cf.top < mStableTop) cf.top = mStableTop;
+ if (cf.right > mStableRight) cf.right = mStableRight;
+ if (cf.bottom > mStableBottom) cf.bottom = mStableBottom;
+ }
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.left = mCurLeft;
vf.top = mCurTop;
@@ -2312,7 +2378,9 @@
vf.set(cf);
}
}
- } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0) {
+ } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0 || (sysUiFl
+ & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
if (DEBUG_LAYOUT)
Log.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): IN_SCREEN");
// A window that has requested to fill the entire screen just
@@ -2327,7 +2395,6 @@
pf.bottom = df.bottom = cf.bottom = hasNavBar
? mRestrictedScreenTop+mRestrictedScreenHeight
: mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
-
if (DEBUG_LAYOUT) {
Log.v(TAG, String.format(
"Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
@@ -2359,6 +2426,21 @@
pf.right = df.right = cf.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
pf.bottom = df.bottom = cf.bottom
= mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+ } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
+ && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
+ && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+ // Asking for layout as if the nav bar is hidden, lets the
+ // application extend into the unrestricted screen area. We
+ // only do this for application windows to ensure no window that
+ // can be above the nav bar can do this.
+ // XXX This assumes that an app asking for this will also
+ // ask for layout in only content. We can't currently figure out
+ // what the screen would be if only laying out to hide the nav bar.
+ pf.left = df.left = cf.left = mUnrestrictedScreenLeft;
+ pf.top = df.top = cf.top = mUnrestrictedScreenTop;
+ pf.right = df.right = cf.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
+ pf.bottom = df.bottom = cf.bottom
+ = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
} else {
pf.left = df.left = cf.left = mRestrictedScreenLeft;
pf.top = df.top = cf.top = mRestrictedScreenTop;
@@ -2366,6 +2448,14 @@
pf.bottom = df.bottom = cf.bottom
= mRestrictedScreenTop+mRestrictedScreenHeight;
}
+ if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
+ // If app is requesting a stable layout, don't let the
+ // content insets go below the stable values.
+ if (cf.left < mStableLeft) cf.left = mStableLeft;
+ if (cf.top < mStableTop) cf.top = mStableTop;
+ if (cf.right > mStableRight) cf.right = mStableRight;
+ if (cf.bottom > mStableBottom) cf.bottom = mStableBottom;
+ }
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.left = mCurLeft;
vf.top = mCurTop;
@@ -2526,7 +2616,8 @@
Log.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+ " lp.flags=0x" + Integer.toHexString(lp.flags));
}
- topIsFullscreen = (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
+ topIsFullscreen = (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0
+ || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
// The subtle difference between the window for mTopFullscreenOpaqueWindowState
// and mTopIsFullscreen is that that mTopIsFullscreen is set only if the window
// has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the
@@ -2591,7 +2682,7 @@
}
}
- if ((updateSystemUiVisibilityLw()&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) {
+ if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
changes |= FINISH_LAYOUT_REDO_LAYOUT;
@@ -2638,7 +2729,7 @@
public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
mFocusedWindow = newFocus;
- if ((updateSystemUiVisibilityLw()&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) {
+ if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
return FINISH_LAYOUT_REDO_LAYOUT;
@@ -3998,7 +4089,7 @@
}
if (mStatusBarService != null) {
try {
- mStatusBarService.setSystemUiVisibility(visibility);
+ mStatusBarService.setSystemUiVisibility(visibility, 0xffffffff);
mStatusBarService.topAppWindowChanged(needsMenu);
} catch (RemoteException e) {
// not much to be done
@@ -4065,6 +4156,10 @@
pw.print(","); pw.print(mRestrictedScreenTop);
pw.print(") "); pw.print(mRestrictedScreenWidth);
pw.print("x"); pw.println(mRestrictedScreenHeight);
+ pw.print(prefix); pw.print("mStable=("); pw.print(mStableLeft);
+ pw.print(","); pw.print(mStableTop);
+ pw.print(")-("); pw.print(mStableRight);
+ pw.print(","); pw.print(mStableBottom); pw.println(")");
pw.print(prefix); pw.print("mCur=("); pw.print(mCurLeft);
pw.print(","); pw.print(mCurTop);
pw.print(")-("); pw.print(mCurRight);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index c705646..96ee2f0 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1132,6 +1132,7 @@
private boolean needsToShowImeSwitchOngoingNotification() {
if (!mShowOngoingImeSwitcherForPhones) return false;
+ if (isScreenLocked()) return false;
synchronized (mMethodMap) {
List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
final int N = imis.size();
@@ -2148,15 +2149,18 @@
mContext.startActivity(intent);
}
+ private boolean isScreenLocked() {
+ return mKeyguardManager != null
+ && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
+ }
private void showInputMethodMenuInternal(boolean showSubtypes) {
if (DEBUG) Slog.v(TAG, "Show switching menu");
final Context context = mContext;
final PackageManager pm = context.getPackageManager();
- final boolean isScreenLocked = mKeyguardManager != null
- && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
+ final boolean isScreenLocked = isScreenLocked();
- String lastInputMethodId = Settings.Secure.getString(context
+ final String lastInputMethodId = Settings.Secure.getString(context
.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
int lastInputMethodSubtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 6833a57..3db4601 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -172,6 +172,7 @@
pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
pw.println(prefix + " tickerText=" + notification.tickerText);
pw.println(prefix + " contentView=" + notification.contentView);
+ pw.println(prefix + " uid=" + uid);
pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults));
pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags));
pw.println(prefix + " sound=" + notification.sound);
diff --git a/services/java/com/android/server/NsdService.java b/services/java/com/android/server/NsdService.java
new file mode 100644
index 0000000..768be7d
--- /dev/null
+++ b/services/java/com/android/server/NsdService.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2010 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.server;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.nsd.DnsSdServiceInfo;
+import android.net.nsd.DnsSdTxtRecord;
+import android.net.nsd.INsdManager;
+import android.net.nsd.NsdManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.IBinder;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.List;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.AsyncChannel;
+import com.android.server.am.BatteryStatsService;
+import com.android.server.NativeDaemonConnector.Command;
+import com.android.internal.R;
+
+/**
+ * Network Service Discovery Service handles remote service discovery operation requests by
+ * implementing the INsdManager interface.
+ *
+ * @hide
+ */
+public class NsdService extends INsdManager.Stub {
+ private static final String TAG = "NsdService";
+ private static final String MDNS_TAG = "mDnsConnector";
+
+ private static final boolean DBG = true;
+
+ private Context mContext;
+
+ /**
+ * Clients receiving asynchronous messages
+ */
+ private List<AsyncChannel> mClients = new ArrayList<AsyncChannel>();
+
+ private AsyncChannel mReplyChannel = new AsyncChannel();
+
+ /**
+ * Handles client(app) connections
+ */
+ private class AsyncServiceHandler extends Handler {
+
+ AsyncServiceHandler(android.os.Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ AsyncChannel c = (AsyncChannel) msg.obj;
+ if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
+ c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
+ mClients.add(c);
+ } else {
+ Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
+ }
+ break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
+ Slog.e(TAG, "Send failed, client connection lost");
+ } else {
+ if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
+ }
+ mClients.remove((AsyncChannel) msg.obj);
+ break;
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
+ AsyncChannel ac = new AsyncChannel();
+ ac.connect(mContext, this, msg.replyTo);
+ break;
+ case NsdManager.DISCOVER_SERVICES:
+ if (DBG) Slog.d(TAG, "Discover services");
+ DnsSdServiceInfo s = (DnsSdServiceInfo) msg.obj;
+ discoverServices(1, s.getServiceType());
+ mReplyChannel.replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED);
+ break;
+ case NsdManager.STOP_DISCOVERY:
+ if (DBG) Slog.d(TAG, "Stop service discovery");
+ mReplyChannel.replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED);
+ break;
+ case NsdManager.REGISTER_SERVICE:
+ if (DBG) Slog.d(TAG, "Register service");
+ mReplyChannel.replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED);
+ break;
+ case NsdManager.UPDATE_SERVICE:
+ if (DBG) Slog.d(TAG, "Update service");
+ mReplyChannel.replyToMessage(msg, NsdManager.UPDATE_SERVICE_FAILED);
+ break;
+ default:
+ Slog.d(TAG, "NsdServicehandler.handleMessage ignoring msg=" + msg);
+ break;
+ }
+ }
+ }
+ private AsyncServiceHandler mAsyncServiceHandler;
+
+ private NativeDaemonConnector mNativeConnector;
+ private final CountDownLatch mNativeDaemonConnected = new CountDownLatch(1);
+
+ private NsdService(Context context) {
+ mContext = context;
+
+ HandlerThread nsdThread = new HandlerThread("NsdService");
+ nsdThread.start();
+ mAsyncServiceHandler = new AsyncServiceHandler(nsdThread.getLooper());
+
+ /*
+ mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), "mdns", 10,
+ MDNS_TAG, 25);
+ Thread th = new Thread(mNativeConnector, MDNS_TAG);
+ th.start();
+ */
+ }
+
+ public static NsdService create(Context context) throws InterruptedException {
+ NsdService service = new NsdService(context);
+ /* service.mNativeDaemonConnected.await(); */
+ return service;
+ }
+
+ public Messenger getMessenger() {
+ return new Messenger(mAsyncServiceHandler);
+ }
+
+ /* These should be in sync with system/netd/mDnsResponseCode.h */
+ class NativeResponseCode {
+ public static final int SERVICE_FOUND = 101;
+ public static final int SERVICE_LOST = 102;
+ public static final int SERVICE_DISCOVERY_FAILED = 103;
+
+ public static final int SERVICE_REGISTERED = 104;
+ public static final int SERVICE_REGISTRATION_FAILED = 105;
+
+ public static final int SERVICE_UPDATED = 106;
+ public static final int SERVICE_UPDATE_FAILED = 107;
+
+ public static final int SERVICE_RESOLVED = 108;
+ public static final int SERVICE_RESOLUTION_FAILED = 109;
+ }
+
+
+ class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
+ public void onDaemonConnected() {
+ mNativeDaemonConnected.countDown();
+ }
+
+ public boolean onEvent(int code, String raw, String[] cooked) {
+ switch (code) {
+ case NativeResponseCode.SERVICE_FOUND:
+ /* NNN uniqueId serviceName regType */
+ break;
+ case NativeResponseCode.SERVICE_LOST:
+ /* NNN uniqueId serviceName regType */
+ break;
+ case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
+ /* NNN uniqueId errorCode */
+ break;
+ case NativeResponseCode.SERVICE_REGISTERED:
+ /* NNN regId serviceName regType */
+ break;
+ case NativeResponseCode.SERVICE_REGISTRATION_FAILED:
+ /* NNN regId errorCode */
+ break;
+ case NativeResponseCode.SERVICE_UPDATED:
+ /* NNN regId */
+ break;
+ case NativeResponseCode.SERVICE_UPDATE_FAILED:
+ /* NNN regId errorCode */
+ break;
+ case NativeResponseCode.SERVICE_RESOLVED:
+ /* NNN resolveId fullName hostName port txtlen txtdata */
+ break;
+ case NativeResponseCode.SERVICE_RESOLUTION_FAILED:
+ /* NNN resovleId errorCode */
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+ }
+
+ private void registerService(int regId, DnsSdServiceInfo service) {
+ try {
+ //Add txtlen and txtdata
+ mNativeConnector.execute("mdnssd", "register", regId, service.getServiceName(),
+ service.getServiceType(), service.getPort());
+ } catch(NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Failed to execute registerService");
+ }
+ }
+
+ private void updateService(int regId, DnsSdTxtRecord t) {
+ try {
+ if (t == null) return;
+ mNativeConnector.execute("mdnssd", "update", regId, t.size(), t.getRawData());
+ } catch(NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Failed to updateServices");
+ }
+ }
+
+ private void discoverServices(int discoveryId, String serviceType) {
+ try {
+ mNativeConnector.execute("mdnssd", "discover", discoveryId, serviceType);
+ } catch(NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Failed to discoverServices");
+ }
+ }
+
+ private void stopServiceDiscovery(int discoveryId) {
+ try {
+ mNativeConnector.execute("mdnssd", "stopdiscover", discoveryId);
+ } catch(NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Failed to stopServiceDiscovery");
+ }
+ }
+
+ private void resolveService(DnsSdServiceInfo service) {
+ try {
+ mNativeConnector.execute("mdnssd", "resolve", service.getServiceName(),
+ service.getServiceType());
+ } catch(NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Failed to resolveService");
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump ServiceDiscoverService from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ pw.println("Internal state:");
+ }
+}
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 6452be7..8429086 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -292,27 +292,27 @@
}
}
- public void setSystemUiVisibility(int vis) {
+ public void setSystemUiVisibility(int vis, int mask) {
// also allows calls from window manager which is in this process.
enforceStatusBarService();
if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
synchronized (mLock) {
- updateUiVisibilityLocked(vis);
+ updateUiVisibilityLocked(vis, mask);
disableLocked(vis & StatusBarManager.DISABLE_MASK, mSysUiVisToken,
"WindowManager.LayoutParams");
}
}
- private void updateUiVisibilityLocked(final int vis) {
+ private void updateUiVisibilityLocked(final int vis, final int mask) {
if (mSystemUiVisibility != vis) {
mSystemUiVisibility = vis;
mHandler.post(new Runnable() {
public void run() {
if (mBar != null) {
try {
- mBar.setSystemUiVisibility(vis);
+ mBar.setSystemUiVisibility(vis, mask);
} catch (RemoteException ex) {
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 423dad6..e091edf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -120,6 +120,7 @@
ConnectivityService connectivity = null;
WifiP2pService wifiP2p = null;
WifiService wifi = null;
+ NsdService serviceDiscovery= null;
IPackageManager pm = null;
Context context = null;
WindowManagerService wm = null;
@@ -394,6 +395,15 @@
}
try {
+ Slog.i(TAG, "Network Service Discovery Service");
+ serviceDiscovery = NsdService.create(context);
+ ServiceManager.addService(
+ Context.NSD_SERVICE, serviceDiscovery);
+ } catch (Throwable e) {
+ reportWtf("starting Service Discovery Service", e);
+ }
+
+ try {
Slog.i(TAG, "Throttle Service");
throttle = new ThrottleService(context);
ServiceManager.addService(
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index bbaebd9..a6fbdd7 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -904,10 +904,15 @@
null, null, 0, null, null, null,
false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
- Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
- mContext, proc, (ActivityRecord)data.get("activity"));
- d.show();
- proc.anrDialog = d;
+ if (mShowDialogs) {
+ Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
+ mContext, proc, (ActivityRecord)data.get("activity"));
+ d.show();
+ proc.anrDialog = d;
+ } else {
+ // Just kill the app if there is no dialog to be shown.
+ killAppAtUsersRequest(proc, null);
+ }
}
ensureBootCompleted();
@@ -3587,11 +3592,9 @@
// If uid is specified and the uid and process name match
// Or, the uid is not specified and the process name matches
} else if (((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid)
- && (app.processName.equals(packageName)
- || app.processName.startsWith(procNamePrefix)))
- || (uid < 0
- && (app.processName.equals(packageName)
- || app.processName.startsWith(procNamePrefix)))) {
+ || ((app.processName.equals(packageName)
+ || app.processName.startsWith(procNamePrefix))
+ && uid < 0))) {
if (app.setAdj >= minOomAdj) {
if (!doit) {
return true;
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
new file mode 100644
index 0000000..6ba4c35
--- /dev/null
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -0,0 +1,288 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.android.server.wm;
+
+import android.graphics.Matrix;
+import android.util.Slog;
+import android.view.Surface;
+import android.view.WindowManagerPolicy;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import java.io.PrintWriter;
+
+/**
+ *
+ */
+public class AppWindowAnimator {
+
+ final AppWindowToken mAppToken;
+ final WindowManagerService mService;
+ final WindowAnimator mAnimator;
+
+ boolean animating;
+ Animation animation;
+ boolean animInitialized;
+ boolean hasTransformation;
+ final Transformation transformation = new Transformation();
+
+ // Have we been asked to have this token keep the screen frozen?
+ // Protect with mAnimator.
+ boolean freezingScreen;
+
+ // Offset to the window of all layers in the token, for use by
+ // AppWindowToken animations.
+ int animLayerAdjustment;
+
+ // Special surface for thumbnail animation.
+ Surface thumbnail;
+ int thumbnailTransactionSeq;
+ int thumbnailX;
+ int thumbnailY;
+ int thumbnailLayer;
+ Animation thumbnailAnimation;
+ final Transformation thumbnailTransformation = new Transformation();
+
+ public AppWindowAnimator(final WindowManagerService service, final AppWindowToken atoken) {
+ mService = service;
+ mAppToken = atoken;
+ mAnimator = service.mAnimator;
+ }
+
+ public void setAnimation(Animation anim, boolean initialized) {
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
+ animation = anim;
+ animating = false;
+ animInitialized = initialized;
+ anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
+ anim.scaleCurrentDuration(mService.mTransitionAnimationScale);
+ int zorder = anim.getZAdjustment();
+ int adj = 0;
+ if (zorder == Animation.ZORDER_TOP) {
+ adj = WindowManagerService.TYPE_LAYER_OFFSET;
+ } else if (zorder == Animation.ZORDER_BOTTOM) {
+ adj = -WindowManagerService.TYPE_LAYER_OFFSET;
+ }
+
+ if (animLayerAdjustment != adj) {
+ animLayerAdjustment = adj;
+ updateLayers();
+ }
+ // Start out animation gone if window is gone, or visible if window is visible.
+ transformation.clear();
+ transformation.setAlpha(mAppToken.reportedVisible ? 1 : 0);
+ hasTransformation = true;
+ }
+
+ public void setDummyAnimation() {
+ if (animation == null) {
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Setting dummy animation in " + this);
+ animation = WindowManagerService.sDummyAnimation;
+ animInitialized = false;
+ }
+ }
+
+ public void clearAnimation() {
+ if (animation != null) {
+ animation = null;
+ animating = true;
+ animInitialized = false;
+ }
+ clearThumbnail();
+ }
+
+ public void clearThumbnail() {
+ if (thumbnail != null) {
+ thumbnail.destroy();
+ thumbnail = null;
+ }
+ }
+
+ void updateLayers() {
+ final int N = mAppToken.allAppWindows.size();
+ final int adj = animLayerAdjustment;
+ thumbnailLayer = -1;
+ for (int i=0; i<N; i++) {
+ final WindowState w = mAppToken.allAppWindows.get(i);
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+ winAnimator.mAnimLayer = w.mLayer + adj;
+ if (winAnimator.mAnimLayer > thumbnailLayer) {
+ thumbnailLayer = winAnimator.mAnimLayer;
+ }
+ if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": "
+ + winAnimator.mAnimLayer);
+ if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
+ mService.setInputMethodAnimLayerAdjustment(adj);
+ }
+ if (w == mService.mWallpaperTarget && mService.mLowerWallpaperTarget == null) {
+ mService.setWallpaperAnimLayerAdjustmentLocked(adj);
+ }
+ }
+ }
+
+ private void stepThumbnailAnimation(long currentTime) {
+ thumbnailTransformation.clear();
+ thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
+ thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
+ final boolean screenAnimation = mAnimator.mScreenRotationAnimation != null
+ && mAnimator.mScreenRotationAnimation.isAnimating();
+ if (screenAnimation) {
+ thumbnailTransformation.postCompose(
+ mAnimator.mScreenRotationAnimation.getEnterTransformation());
+ }
+ // cache often used attributes locally
+ final float tmpFloats[] = mService.mTmpFloats;
+ thumbnailTransformation.getMatrix().getValues(tmpFloats);
+ if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
+ "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
+ + ", " + tmpFloats[Matrix.MTRANS_Y], null);
+ thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
+ if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
+ "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
+ + " layer=" + thumbnailLayer
+ + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
+ + "," + tmpFloats[Matrix.MSKEW_Y]
+ + "][" + tmpFloats[Matrix.MSKEW_X]
+ + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null);
+ thumbnail.setAlpha(thumbnailTransformation.getAlpha());
+ // The thumbnail is layered below the window immediately above this
+ // token's anim layer.
+ thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
+ - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
+ thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
+ tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
+ }
+
+ private boolean stepAnimation(long currentTime) {
+ if (animation == null) {
+ return false;
+ }
+ transformation.clear();
+ final boolean more = animation.getTransformation(currentTime, transformation);
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Stepped animation in " + this +
+ ": more=" + more + ", xform=" + transformation);
+ if (!more) {
+ animation = null;
+ clearThumbnail();
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Finished animation in " + this +
+ " @ " + currentTime);
+ }
+ hasTransformation = more;
+ return more;
+ }
+
+ // This must be called while inside a transaction.
+ boolean stepAnimationLocked(long currentTime, int dw, int dh) {
+ if (mService.okToDisplay()) {
+ // We will run animations as long as the display isn't frozen.
+
+ if (animation == WindowManagerService.sDummyAnimation) {
+ // This guy is going to animate, but not yet. For now count
+ // it as not animating for purposes of scheduling transactions;
+ // when it is really time to animate, this will be set to
+ // a real animation and the next call will execute normally.
+ return false;
+ }
+
+ if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
+ && animation != null) {
+ if (!animating) {
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Starting animation in " + this +
+ " @ " + currentTime + ": dw=" + dw + " dh=" + dh
+ + " scale=" + mService.mTransitionAnimationScale
+ + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
+ if (!animInitialized) {
+ animation.initialize(dw, dh, dw, dh);
+ }
+ animation.setStartTime(currentTime);
+ animating = true;
+ if (thumbnail != null) {
+ thumbnail.show();
+ thumbnailAnimation.setStartTime(currentTime);
+ }
+ }
+ if (stepAnimation(currentTime)) {
+ // animation isn't over, step any thumbnail and that's
+ // it for now.
+ if (thumbnail != null) {
+ stepThumbnailAnimation(currentTime);
+ }
+ return true;
+ }
+ }
+ } else if (animation != null) {
+ // If the display is frozen, and there is a pending animation,
+ // clear it and make sure we run the cleanup code.
+ animating = true;
+ animation = null;
+ }
+
+ hasTransformation = false;
+
+ if (!animating) {
+ return false;
+ }
+
+ mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ mService.debugLayoutRepeats("AppWindowToken", mAnimator.mPendingLayoutChanges);
+ }
+
+ clearAnimation();
+ animating = false;
+ if (animLayerAdjustment != 0) {
+ animLayerAdjustment = 0;
+ updateLayers();
+ }
+ if (mService.mInputMethodTarget != null
+ && mService.mInputMethodTarget.mAppToken == mAppToken) {
+ mService.moveInputMethodWindowsIfNeededLocked(true);
+ }
+
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Animation done in " + this
+ + ": reportedVisible=" + mAppToken.reportedVisible);
+
+ transformation.clear();
+
+ final int N = mAppToken.windows.size();
+ for (int i=0; i<N; i++) {
+ mAppToken.windows.get(i).mWinAnimator.finishExit();
+ }
+ mAppToken.updateReportedVisibilityLocked();
+
+ return false;
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ if (freezingScreen) {
+ pw.print(prefix); pw.print(" freezingScreen="); pw.println(freezingScreen);
+ }
+ if (animating || animation != null) {
+ pw.print(prefix); pw.print("animating="); pw.print(animating);
+ pw.print(" animation="); pw.println(animation);
+ }
+ if (hasTransformation) {
+ pw.print(prefix); pw.print("XForm: ");
+ transformation.printShortString(pw);
+ pw.println();
+ }
+ if (animLayerAdjustment != 0) {
+ pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment);
+ }
+ if (thumbnail != null) {
+ pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
+ pw.print(" x="); pw.print(thumbnailX);
+ pw.print(" y="); pw.print(thumbnailY);
+ pw.print(" layer="); pw.println(thumbnailLayer);
+ pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
+ pw.print(prefix); pw.print("thumbnailTransformation=");
+ pw.println(thumbnailTransformation.toShortString());
+ }
+ }
+}
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index d1f1b44..e3d46d8 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -21,17 +21,12 @@
import com.android.server.wm.WindowManagerService.H;
import android.content.pm.ActivityInfo;
-import android.graphics.Matrix;
import android.os.Message;
import android.os.RemoteException;
import android.util.Slog;
import android.view.IApplicationToken;
-import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-import android.view.animation.Animation;
-import android.view.animation.Transformation;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -47,6 +42,9 @@
// All of the windows and child windows that are included in this
// application token. Note this list is NOT sorted!
final ArrayList<WindowState> allAppWindows = new ArrayList<WindowState>();
+ final AppWindowAnimator mAppAnimator;
+
+ final WindowAnimator mAnimator;
int groupId = -1;
boolean appFullscreen;
@@ -87,19 +85,6 @@
// Set to true when the token has been removed from the window mgr.
boolean removed;
- // Have we been asked to have this token keep the screen frozen?
- boolean freezingScreen;
-
- boolean animating;
- Animation animation;
- boolean animInitialized;
- boolean hasTransformation;
- final Transformation transformation = new Transformation();
-
- // Offset to the window of all layers in the token, for use by
- // AppWindowToken animations.
- int animLayerAdjustment;
-
// Information about an application starting window if displayed.
StartingData startingData;
WindowState startingWindow;
@@ -108,15 +93,6 @@
boolean startingMoved;
boolean firstWindowDrawn;
- // Special surface for thumbnail animation.
- Surface thumbnail;
- int thumbnailTransactionSeq;
- int thumbnailX;
- int thumbnailY;
- int thumbnailLayer;
- Animation thumbnailAnimation;
- final Transformation thumbnailTransformation = new Transformation();
-
// Input application handle used by the input dispatcher.
final InputApplicationHandle mInputApplicationHandle;
@@ -126,79 +102,8 @@
appWindowToken = this;
appToken = _token;
mInputApplicationHandle = new InputApplicationHandle(this);
- }
-
- public void setAnimation(Animation anim, boolean initialized) {
- if (WindowManagerService.localLOGV) Slog.v(
- WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
- animation = anim;
- animating = false;
- animInitialized = initialized;
- anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(service.mTransitionAnimationScale);
- int zorder = anim.getZAdjustment();
- int adj = 0;
- if (zorder == Animation.ZORDER_TOP) {
- adj = WindowManagerService.TYPE_LAYER_OFFSET;
- } else if (zorder == Animation.ZORDER_BOTTOM) {
- adj = -WindowManagerService.TYPE_LAYER_OFFSET;
- }
-
- if (animLayerAdjustment != adj) {
- animLayerAdjustment = adj;
- updateLayers();
- }
- // Start out animation gone if window is gone, or visible if window is visible.
- transformation.clear();
- transformation.setAlpha(reportedVisible ? 1 : 0);
- hasTransformation = true;
- }
-
- public void setDummyAnimation() {
- if (animation == null) {
- if (WindowManagerService.localLOGV) Slog.v(
- WindowManagerService.TAG, "Setting dummy animation in " + this);
- animation = WindowManagerService.sDummyAnimation;
- animInitialized = false;
- }
- }
-
- public void clearAnimation() {
- if (animation != null) {
- animation = null;
- animating = true;
- animInitialized = false;
- }
- clearThumbnail();
- }
-
- public void clearThumbnail() {
- if (thumbnail != null) {
- thumbnail.destroy();
- thumbnail = null;
- }
- }
-
- void updateLayers() {
- final int N = allAppWindows.size();
- final int adj = animLayerAdjustment;
- thumbnailLayer = -1;
- for (int i=0; i<N; i++) {
- final WindowState w = allAppWindows.get(i);
- final WindowStateAnimator winAnimator = w.mWinAnimator;
- winAnimator.mAnimLayer = w.mLayer + adj;
- if (winAnimator.mAnimLayer > thumbnailLayer) {
- thumbnailLayer = winAnimator.mAnimLayer;
- }
- if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": "
- + winAnimator.mAnimLayer);
- if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) {
- service.setInputMethodAnimLayerAdjustment(adj);
- }
- if (w == service.mWallpaperTarget && service.mLowerWallpaperTarget == null) {
- service.setWallpaperAnimLayerAdjustmentLocked(adj);
- }
- }
+ mAnimator = service.mAnimator;
+ mAppAnimator = new AppWindowAnimator(_service, this);
}
void sendAppVisibilityToClients() {
@@ -231,141 +136,6 @@
return isAnimating;
}
- private void stepThumbnailAnimation(long currentTime) {
- thumbnailTransformation.clear();
- thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
- thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
- final boolean screenAnimation = service.mAnimator.mScreenRotationAnimation != null
- && service.mAnimator.mScreenRotationAnimation.isAnimating();
- if (screenAnimation) {
- thumbnailTransformation.postCompose(
- service.mAnimator.mScreenRotationAnimation.getEnterTransformation());
- }
- // cache often used attributes locally
- final float tmpFloats[] = service.mTmpFloats;
- thumbnailTransformation.getMatrix().getValues(tmpFloats);
- if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
- "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
- + ", " + tmpFloats[Matrix.MTRANS_Y], null);
- thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
- if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
- "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
- + " layer=" + thumbnailLayer
- + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
- + "," + tmpFloats[Matrix.MSKEW_Y]
- + "][" + tmpFloats[Matrix.MSKEW_X]
- + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null);
- thumbnail.setAlpha(thumbnailTransformation.getAlpha());
- // The thumbnail is layered below the window immediately above this
- // token's anim layer.
- thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
- - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
- thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
- tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
- }
-
- private boolean stepAnimation(long currentTime) {
- if (animation == null) {
- return false;
- }
- transformation.clear();
- final boolean more = animation.getTransformation(currentTime, transformation);
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Stepped animation in " + this +
- ": more=" + more + ", xform=" + transformation);
- if (!more) {
- animation = null;
- clearThumbnail();
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Finished animation in " + this +
- " @ " + currentTime);
- }
- hasTransformation = more;
- return more;
- }
-
- // This must be called while inside a transaction.
- boolean stepAnimationLocked(long currentTime, int dw, int dh) {
- if (service.okToDisplay()) {
- // We will run animations as long as the display isn't frozen.
-
- if (animation == WindowManagerService.sDummyAnimation) {
- // This guy is going to animate, but not yet. For now count
- // it as not animating for purposes of scheduling transactions;
- // when it is really time to animate, this will be set to
- // a real animation and the next call will execute normally.
- return false;
- }
-
- if ((allDrawn || animating || startingDisplayed) && animation != null) {
- if (!animating) {
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Starting animation in " + this +
- " @ " + currentTime + ": dw=" + dw + " dh=" + dh
- + " scale=" + service.mTransitionAnimationScale
- + " allDrawn=" + allDrawn + " animating=" + animating);
- if (!animInitialized) {
- animation.initialize(dw, dh, dw, dh);
- }
- animation.setStartTime(currentTime);
- animating = true;
- if (thumbnail != null) {
- thumbnail.show();
- thumbnailAnimation.setStartTime(currentTime);
- }
- }
- if (stepAnimation(currentTime)) {
- // animation isn't over, step any thumbnail and that's
- // it for now.
- if (thumbnail != null) {
- stepThumbnailAnimation(currentTime);
- }
- return true;
- }
- }
- } else if (animation != null) {
- // If the display is frozen, and there is a pending animation,
- // clear it and make sure we run the cleanup code.
- animating = true;
- animation = null;
- }
-
- hasTransformation = false;
-
- if (!animating) {
- return false;
- }
-
- service.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- service.debugLayoutRepeats("AppWindowToken");
- }
-
- clearAnimation();
- animating = false;
- if (animLayerAdjustment != 0) {
- animLayerAdjustment = 0;
- updateLayers();
- }
- if (service.mInputMethodTarget != null && service.mInputMethodTarget.mAppToken == this) {
- service.moveInputMethodWindowsIfNeededLocked(true);
- }
-
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Animation done in " + this
- + ": reportedVisible=" + reportedVisible);
-
- transformation.clear();
-
- final int N = windows.size();
- for (int i=0; i<N; i++) {
- windows.get(i).mWinAnimator.finishExit();
- }
- updateReportedVisibilityLocked();
-
- return false;
- }
-
void updateReportedVisibilityLocked() {
if (appToken == null) {
return;
@@ -376,7 +146,8 @@
int numDrawn = 0;
boolean nowGone = true;
- if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Update reported visibility: " + this);
+ if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG,
+ "Update reported visibility: " + this);
final int N = allAppWindows.size();
for (int i=0; i<N; i++) {
WindowState win = allAppWindows.get(i);
@@ -393,8 +164,7 @@
if (!win.isDrawnLw()) {
Slog.v(WindowManagerService.TAG, "Not displayed: s=" + win.mWinAnimator.mSurface
+ " pv=" + win.mPolicyVisibility
- + " dp=" + win.mWinAnimator.mDrawPending
- + " cdp=" + win.mWinAnimator.mCommitDrawPending
+ + " mDrawState=" + win.mWinAnimator.mDrawState
+ " ah=" + win.mAttachedHidden
+ " th="
+ (win.mAppToken != null
@@ -479,9 +249,8 @@
pw.print(" willBeHidden="); pw.print(willBeHidden);
pw.print(" reportedDrawn="); pw.print(reportedDrawn);
pw.print(" reportedVisible="); pw.println(reportedVisible);
- if (paused || freezingScreen) {
- pw.print(prefix); pw.print("paused="); pw.print(paused);
- pw.print(" freezingScreen="); pw.println(freezingScreen);
+ if (paused) {
+ pw.print(prefix); pw.print("paused="); pw.println(paused);
}
if (numInterestingWindows != 0 || numDrawnWindows != 0
|| inPendingTransaction || allDrawn) {
@@ -491,18 +260,6 @@
pw.print(" inPendingTransaction="); pw.print(inPendingTransaction);
pw.print(" allDrawn="); pw.println(allDrawn);
}
- if (animating || animation != null) {
- pw.print(prefix); pw.print("animating="); pw.print(animating);
- pw.print(" animation="); pw.println(animation);
- }
- if (hasTransformation) {
- pw.print(prefix); pw.print("XForm: ");
- transformation.printShortString(pw);
- pw.println();
- }
- if (animLayerAdjustment != 0) {
- pw.print(prefix); pw.print("animLayerAdjustment="); pw.println(animLayerAdjustment);
- }
if (startingData != null || removed || firstWindowDrawn) {
pw.print(prefix); pw.print("startingData="); pw.print(startingData);
pw.print(" removed="); pw.print(removed);
@@ -515,15 +272,6 @@
pw.print(" startingDisplayed="); pw.print(startingDisplayed);
pw.print(" startingMoved"); pw.println(startingMoved);
}
- if (thumbnail != null) {
- pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
- pw.print(" x="); pw.print(thumbnailX);
- pw.print(" y="); pw.print(thumbnailY);
- pw.print(" layer="); pw.println(thumbnailLayer);
- pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
- pw.print(prefix); pw.print("thumbnailTransformation=");
- pw.println(thumbnailTransformation.toShortString());
- }
}
@Override
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index 0051d98..405dd04 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -57,12 +57,17 @@
}
/**
- * Show the dim surface.
+ * Set's the dim surface's layer and update dim parameters that will be used in
+ * {@link #updateSurface} after all windows are examined.
*/
- void show(int dw, int dh) {
+ void updateParameters(final Resources res, final Parameters params, final long currentTime) {
+ final int dw = params.mDimWidth;
+ final int dh = params.mDimHeight;
+ final WindowStateAnimator winAnimator = params.mDimWinAnimator;
+ final float target = params.mDimTarget;
if (!mDimShown) {
- if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" +
- dw + "x" + dh + ")");
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
+ " DIM " + mDimSurface + ": SHOW pos=(0,0) (" + dw + "x" + dh + ")");
mDimShown = true;
try {
mLastDimWidth = dw;
@@ -78,17 +83,9 @@
mLastDimHeight = dh;
mDimSurface.setSize(dw, dh);
}
- }
- /**
- * Set's the dim surface's layer and update dim parameters that will be used in
- * {@link #updateSurface} after all windows are examined.
- */
- void updateParameters(Resources res, WindowState w, long currentTime) {
- final WindowStateAnimator winAnimator = w.mWinAnimator;
mDimSurface.setLayer(winAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM);
- final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, " DIM "
+ mDimSurface + ": layer=" + (winAnimator.mAnimLayer-1) + " target=" + target);
if (mDimTargetAlpha != target) {
@@ -189,4 +186,18 @@
pw.print(" delta="); pw.print(mDimDeltaPerMs);
pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime);
}
+
+ static class Parameters {
+ final WindowStateAnimator mDimWinAnimator;
+ final int mDimWidth;
+ final int mDimHeight;
+ final float mDimTarget;
+ Parameters(final WindowStateAnimator dimWinAnimator, final int dimWidth,
+ final int dimHeight, final float dimTarget) {
+ mDimWinAnimator = dimWinAnimator;
+ mDimWidth = dimWidth;
+ mDimHeight = dimHeight;
+ mDimTarget = dimTarget;
+ }
+ }
}
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 26f4d0d..77f94d9 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -6,6 +6,9 @@
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_MAY_CHANGE;
+import static com.android.server.wm.WindowManagerService.LayoutFields.SET_FORCE_HIDING_CHANGED;
+
+import static com.android.server.wm.WindowManagerService.H.SET_DIM_PARAMETERS;
import android.content.Context;
import android.os.SystemClock;
@@ -21,7 +24,6 @@
import java.io.PrintWriter;
/**
- * @author cmautner@google.com (Craig Mautner)
* Singleton class that carries out the animations and Surface operations in a separate task
* on behalf of WindowManagerService.
*/
@@ -67,6 +69,9 @@
int mBulkUpdateParams = 0;
+ DimAnimator mDimAnimator = null;
+ DimAnimator.Parameters mDimParams = null;
+
WindowAnimator(final WindowManagerService service, final Context context,
final WindowManagerPolicy policy) {
mService = service;
@@ -91,7 +96,8 @@
if (mService.mWallpaperTarget == target
|| mService.mLowerWallpaperTarget == target
|| mService.mUpperWallpaperTarget == target) {
- for (int i=0; i<mService.mWindows.size(); i++) {
+ final int N = mService.mWindows.size();
+ for (int i = 0; i < N; i++) {
WindowState w = mService.mWindows.get(i);
if (w.mIsWallpaper) {
target = w;
@@ -116,32 +122,34 @@
int i;
final int NAT = mService.mAppTokens.size();
for (i=0; i<NAT; i++) {
- final AppWindowToken appToken = mService.mAppTokens.get(i);
- final boolean wasAnimating = appToken.animation != null
- && appToken.animation != WindowManagerService.sDummyAnimation;
- if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
+ final AppWindowAnimator appAnimator = mService.mAppTokens.get(i).mAppAnimator;
+ final boolean wasAnimating = appAnimator.animation != null
+ && appAnimator.animation != WindowManagerService.sDummyAnimation;
+ if (appAnimator.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("appToken " + appToken + " done");
+ mService.debugLayoutRepeats("appToken " + appAnimator.mAppToken + " done",
+ mPendingLayoutChanges);
}
}
}
final int NEAT = mService.mExitingAppTokens.size();
for (i=0; i<NEAT; i++) {
- final AppWindowToken appToken = mService.mExitingAppTokens.get(i);
- final boolean wasAnimating = appToken.animation != null
- && appToken.animation != WindowManagerService.sDummyAnimation;
- if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
+ final AppWindowAnimator appAnimator = mService.mAppTokens.get(i).mAppAnimator;
+ final boolean wasAnimating = appAnimator.animation != null
+ && appAnimator.animation != WindowManagerService.sDummyAnimation;
+ if (appAnimator.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) {
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("exiting appToken " + appToken + " done");
+ mService.debugLayoutRepeats("exiting appToken " + appAnimator.mAppToken
+ + " done", mPendingLayoutChanges);
}
}
}
@@ -185,13 +193,13 @@
&& winAnimator.mAnimation.getDetachWallpaper()) {
mDetachedWallpaper = w;
}
- if (winAnimator.mAnimation.getBackgroundColor() != 0) {
+ final int backgroundColor = winAnimator.mAnimation.getBackgroundColor();
+ if (backgroundColor != 0) {
if (mWindowAnimationBackground == null
|| (winAnimator.mAnimLayer <
mWindowAnimationBackground.mWinAnimator.mAnimLayer)) {
mWindowAnimationBackground = w;
- mWindowAnimationBackgroundColor =
- winAnimator.mAnimation.getBackgroundColor();
+ mWindowAnimationBackgroundColor = backgroundColor;
}
}
}
@@ -201,19 +209,21 @@
// If this window's app token is running a detached wallpaper
// animation, make a note so we can ensure the wallpaper is
// displayed behind it.
- if (w.mAppToken != null && w.mAppToken.animation != null
- && w.mAppToken.animating) {
+ final AppWindowAnimator appAnimator =
+ w.mAppToken == null ? null : w.mAppToken.mAppAnimator;
+ if (appAnimator != null && appAnimator.animation != null
+ && appAnimator.animating) {
if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0
- && w.mAppToken.animation.getDetachWallpaper()) {
+ && appAnimator.animation.getDetachWallpaper()) {
mDetachedWallpaper = w;
}
- if (w.mAppToken.animation.getBackgroundColor() != 0) {
+ final int backgroundColor = appAnimator.animation.getBackgroundColor();
+ if (backgroundColor != 0) {
if (mWindowAnimationBackground == null
|| (winAnimator.mAnimLayer <
mWindowAnimationBackground.mWinAnimator.mAnimLayer)) {
mWindowAnimationBackground = w;
- mWindowAnimationBackgroundColor =
- w.mAppToken.animation.getBackgroundColor();
+ mWindowAnimationBackgroundColor = backgroundColor;
}
}
}
@@ -222,7 +232,8 @@
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2");
+ mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2",
+ mPendingLayoutChanges);
}
}
@@ -231,17 +242,18 @@
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
"Animation started that could impact force hide: "
+ w);
- mService.mInnerFields.mWallpaperForceHidingChanged = true;
+ mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3");
+ mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3",
+ mPendingLayoutChanges);
}
mService.mFocusMayChange = true;
} else if (w.isReadyForDisplay() && winAnimator.mAnimation == null) {
mForceHiding = true;
}
} else if (mPolicy.canBeForceHidden(w, attrs)) {
- boolean changed;
+ final boolean changed;
if (mForceHiding) {
changed = w.hideLw(false, false);
if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
@@ -251,7 +263,7 @@
if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG,
"Now policy shown: " + w);
if (changed) {
- if (mService.mInnerFields.mWallpaperForceHidingChanged
+ if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0
&& w.isVisibleNow() /*w.isReadyForDisplay()*/) {
// Assume we will need to animate. If
// we don't (because the wallpaper will
@@ -275,14 +287,15 @@
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4");
+ mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4",
+ mPendingLayoutChanges);
}
}
}
}
final AppWindowToken atoken = w.mAppToken;
- if (atoken != null && (!atoken.allDrawn || atoken.freezingScreen)) {
+ if (atoken != null && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
if (atoken.lastTransactionSequence != mTransactionSequence) {
atoken.lastTransactionSequence = mTransactionSequence;
atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
@@ -299,22 +312,21 @@
if (!w.isDrawnLw()) {
Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurface
+ " pv=" + w.mPolicyVisibility
- + " dp=" + winAnimator.mDrawPending
- + " cdp=" + winAnimator.mCommitDrawPending
+ + " mDrawState=" + winAnimator.mDrawState
+ " ah=" + w.mAttachedHidden
+ " th=" + atoken.hiddenRequested
+ " a=" + winAnimator.mAnimating);
}
}
if (w != atoken.startingWindow) {
- if (!atoken.freezingScreen || !w.mAppFreezing) {
+ if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
atoken.numInterestingWindows++;
if (w.isDrawnLw()) {
atoken.numDrawnWindows++;
if (WindowManagerService.DEBUG_VISIBILITY ||
WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
"tokenMayBeDrawn: " + atoken
- + " freezingScreen=" + atoken.freezingScreen
+ + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
+ " mAppFreezing=" + w.mAppFreezing);
mTokenMayBeDrawn = true;
}
@@ -323,21 +335,24 @@
atoken.startingDisplayed = true;
}
}
- } else if (w.mReadyToShow) {
+ } else if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) {
if (winAnimator.performShowLocked()) {
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5");
+ mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5",
+ mPendingLayoutChanges);
}
}
}
- if (atoken != null && atoken.thumbnail != null) {
- if (atoken.thumbnailTransactionSeq != mTransactionSequence) {
- atoken.thumbnailTransactionSeq = mTransactionSequence;
- atoken.thumbnailLayer = 0;
+ final AppWindowAnimator appAnimator =
+ atoken == null ? null : atoken.mAppAnimator;
+ if (appAnimator != null && appAnimator.thumbnail != null) {
+ if (appAnimator.thumbnailTransactionSeq != mTransactionSequence) {
+ appAnimator.thumbnailTransactionSeq = mTransactionSequence;
+ appAnimator.thumbnailLayer = 0;
}
- if (atoken.thumbnailLayer < winAnimator.mAnimLayer) {
- atoken.thumbnailLayer = winAnimator.mAnimLayer;
+ if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {
+ appAnimator.thumbnailLayer = winAnimator.mAnimLayer;
}
}
} // end forall windows
@@ -349,7 +364,7 @@
final int NT = mService.mAppTokens.size();
for (int i=0; i<NT; i++) {
AppWindowToken wtoken = mService.mAppTokens.get(i);
- if (wtoken.freezingScreen) {
+ if (wtoken.mAppAnimator.freezingScreen) {
int numInteresting = wtoken.numInterestingWindows;
if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
@@ -362,7 +377,6 @@
"Setting mOrientationChangeComplete=true because wtoken "
+ wtoken + " numInteresting=" + numInteresting
+ " numDrawn=" + wtoken.numDrawnWindows);
- mService.mInnerFields.mOrientationChangeComplete = true;
}
} else if (!wtoken.allDrawn) {
int numInteresting = wtoken.numInterestingWindows;
@@ -374,7 +388,8 @@
wtoken.allDrawn = true;
mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("testTokenMayBeDrawnLocked");
+ mService.debugLayoutRepeats("testTokenMayBeDrawnLocked",
+ mPendingLayoutChanges);
}
// We can now show all of the drawn windows!
@@ -420,14 +435,13 @@
mScreenRotationAnimation.updateSurfaces();
}
- final int N = mService.mWindows.size();
- for (int i=N-1; i>=0; i--) {
+ for (int i = mService.mWindows.size() - 1; i >= 0; i--) {
WindowState w = mService.mWindows.get(i);
w.mWinAnimator.prepareSurfaceLocked(true);
}
- if (mService.mDimAnimator != null && mService.mDimAnimator.mDimShown) {
- mAnimating |= mService.mDimAnimator.updateSurface(mService.mInnerFields.mDimming,
+ if (mDimAnimator != null && mDimAnimator.mDimShown) {
+ mAnimating |= mDimAnimator.updateSurface(mService.mInnerFields.mDimming,
mCurrentTime, !mService.okToDisplay());
}
@@ -439,6 +453,10 @@
mService.mBlackFrame.clearMatrix();
}
}
+
+ if (mDimParams != null) {
+ mDimAnimator.updateParameters(mContext.getResources(), mDimParams, mCurrentTime);
+ }
} catch (RuntimeException e) {
Log.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
@@ -451,7 +469,7 @@
}
WindowState mCurrentFocus;
- void setCurrentFocus(WindowState currentFocus) {
+ void setCurrentFocus(final WindowState currentFocus) {
mCurrentFocus = currentFocus;
}
@@ -463,6 +481,20 @@
mInnerDh = appHeight;
}
+ void startDimming(final WindowStateAnimator winAnimator, final float target,
+ final int width, final int height) {
+ if (mDimAnimator == null) {
+ mDimAnimator = new DimAnimator(mService.mFxSession);
+ }
+ mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS,
+ new DimAnimator.Parameters(winAnimator, width, height, target)));
+ }
+
+ // TODO(cmautner): Move into Handler
+ void stopDimming() {
+ mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS, null));
+ }
+
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
if (mWindowDetachedWallpaper != null) {
pw.print(" mWindowDetachedWallpaper="); pw.println(mWindowDetachedWallpaper);
@@ -471,5 +503,11 @@
pw.println(" mWindowAnimationBackgroundSurface:");
mWindowAnimationBackgroundSurface.printTo(" ", pw);
}
+ if (mDimAnimator != null) {
+ pw.println(" mDimAnimator:");
+ mDimAnimator.printTo(" ", pw);
+ } else {
+ pw.println( " no DimAnimator ");
+ }
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index a91e716..7eca401 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -171,7 +171,7 @@
static final boolean DEBUG_SCREEN_ON = false;
static final boolean DEBUG_SCREENSHOT = false;
static final boolean DEBUG_BOOT = false;
- static final boolean DEBUG_LAYOUT_REPEATS = false;
+ static final boolean DEBUG_LAYOUT_REPEATS = true;
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
@@ -353,7 +353,32 @@
* controlling the ordering of windows in different applications. This
* contains AppWindowToken objects.
*/
- final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>();
+ final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>() {
+ @Override
+ public void add(int index, AppWindowToken object) {
+ synchronized (mAnimator) {
+ super.add(index, object);
+ }
+ };
+ @Override
+ public boolean add(AppWindowToken object) {
+ synchronized (mAnimator) {
+ return super.add(object);
+ }
+ };
+ @Override
+ public AppWindowToken remove(int index) {
+ synchronized (mAnimator) {
+ return super.remove(index);
+ }
+ };
+ @Override
+ public boolean remove(Object object) {
+ synchronized (mAnimator) {
+ return super.remove(object);
+ }
+ };
+ };
/**
* Application tokens that are in the process of exiting, but still
@@ -370,7 +395,32 @@
/**
* Z-ordered (bottom-most first) list of all Window objects.
*/
- final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
+ final ArrayList<WindowState> mWindows = new ArrayList<WindowState>() {
+ @Override
+ public void add(int index, WindowState object) {
+ synchronized (mAnimator) {
+ super.add(index, object);
+ }
+ };
+ @Override
+ public boolean add(WindowState object) {
+ synchronized (mAnimator) {
+ return super.add(object);
+ }
+ };
+ @Override
+ public WindowState remove(int index) {
+ synchronized (mAnimator) {
+ return super.remove(index);
+ }
+ };
+ @Override
+ public boolean remove(Object object) {
+ synchronized (mAnimator) {
+ return super.remove(object);
+ }
+ };
+ };
/**
* Fake windows added to the window manager. Note: ordered from top to
@@ -427,7 +477,6 @@
IInputMethodManager mInputMethodManager;
SurfaceSession mFxSession;
- DimAnimator mDimAnimator = null;
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
@@ -590,8 +639,10 @@
/** Pulled out of performLayoutAndPlaceSurfacesLockedInner in order to refactor into multiple
* methods. */
class LayoutFields {
- static final int SET_UPDATE_ROTATION = 1 << 0;
- static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
+ static final int SET_UPDATE_ROTATION = 1 << 0;
+ static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
+ static final int SET_FORCE_HIDING_CHANGED = 1 << 2;
+ static final int CLEAR_ORIENTATION_CHANGE_COMPLETE = 1 << 3;
boolean mWallpaperForceHidingChanged = false;
boolean mWallpaperMayChange = false;
@@ -1214,7 +1265,7 @@
AppWindowToken token = curTarget.mAppToken;
WindowState highestTarget = null;
int highestPos = 0;
- if (token.animating || token.animation != null) {
+ if (token.mAppAnimator.animating || token.mAppAnimator.animation != null) {
int pos = localmWindows.indexOf(curTarget);
while (pos >= 0) {
WindowState win = localmWindows.get(pos);
@@ -1274,7 +1325,7 @@
mInputMethodTarget = w;
mInputMethodTargetWaitingAnim = false;
if (w.mAppToken != null) {
- setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment);
+ setInputMethodAnimLayerAdjustment(w.mAppToken.mAppAnimator.animLayerAdjustment);
} else {
setInputMethodAnimLayerAdjustment(0);
}
@@ -1537,12 +1588,12 @@
if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
+ (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
+ " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
- ? wallpaperTarget.mAppToken.animation : null)
+ ? wallpaperTarget.mAppToken.mAppAnimator.animation : null)
+ " upper=" + mUpperWallpaperTarget
+ " lower=" + mLowerWallpaperTarget);
return (wallpaperTarget != null
&& (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
- && wallpaperTarget.mAppToken.animation != null)))
+ && wallpaperTarget.mAppToken.mAppAnimator.animation != null)))
|| mUpperWallpaperTarget != null
|| mLowerWallpaperTarget != null;
}
@@ -1582,15 +1633,14 @@
if (w != mAnimator.mWindowDetachedWallpaper && w.mAppToken != null) {
// If this window's app token is hidden and not animating,
// it is of no interest to us.
- if (w.mAppToken.hidden && w.mAppToken.animation == null) {
+ if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Skipping not hidden or animating token: " + w);
continue;
}
}
if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": readyfordisplay="
- + w.isReadyForDisplay() + " drawpending=" + w.mWinAnimator.mDrawPending
- + " commitdrawpending=" + w.mWinAnimator.mCommitDrawPending);
+ + w.isReadyForDisplay() + " mDrawState=" + w.mWinAnimator.mDrawState);
if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isReadyForDisplay()
&& (mWallpaperTarget == w || w.isDrawnLw())) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
@@ -1598,7 +1648,7 @@
foundW = w;
foundI = i;
if (w == mWallpaperTarget && ((w.mAppToken != null
- && w.mAppToken.animation != null)
+ && w.mAppToken.mAppAnimator.animation != null)
|| w.mWinAnimator.mAnimation != null)) {
// The current wallpaper target is animating, so we'll
// look behind it for another possible target and figure
@@ -1657,9 +1707,11 @@
// animating, then we are in our super special mode!
if (foundW != null && oldW != null) {
boolean oldAnim = oldW.mWinAnimator.mAnimation != null
- || (oldW.mAppToken != null && oldW.mAppToken.animation != null);
+ || (oldW.mAppToken != null
+ && oldW.mAppToken.mAppAnimator.animation != null);
boolean foundAnim = foundW.mWinAnimator.mAnimation != null
- || (foundW.mAppToken != null && foundW.mAppToken.animation != null);
+ || (foundW.mAppToken != null &&
+ foundW.mAppToken.mAppAnimator.animation != null);
if (DEBUG_WALLPAPER) {
Slog.v(TAG, "New animation: " + foundAnim
+ " old animation: " + oldAnim);
@@ -1712,10 +1764,10 @@
// Is it time to stop animating?
boolean lowerAnimating = mLowerWallpaperTarget.mWinAnimator.mAnimation != null
|| (mLowerWallpaperTarget.mAppToken != null
- && mLowerWallpaperTarget.mAppToken.animation != null);
+ && mLowerWallpaperTarget.mAppToken.mAppAnimator.animation != null);
boolean upperAnimating = mUpperWallpaperTarget.mWinAnimator.mAnimation != null
|| (mUpperWallpaperTarget.mAppToken != null
- && mUpperWallpaperTarget.mAppToken.animation != null);
+ && mUpperWallpaperTarget.mAppToken.mAppAnimator.animation != null);
if (!lowerAnimating || !upperAnimating) {
if (DEBUG_WALLPAPER) {
Slog.v(TAG, "No longer animating wallpaper targets!");
@@ -1737,7 +1789,7 @@
// between two wallpaper targets.
mWallpaperAnimLayerAdjustment =
(mLowerWallpaperTarget == null && foundW.mAppToken != null)
- ? foundW.mAppToken.animLayerAdjustment : 0;
+ ? foundW.mAppToken.mAppAnimator.animLayerAdjustment : 0;
final int maxLayer = mPolicy.getMaxWallpaperLayer()
* TYPE_LAYER_MULTIPLIER
@@ -1972,6 +2024,11 @@
}
}
+ // TODO(cmautner): Move to WindowAnimator.
+ void setWallpaperOffset(final WindowStateAnimator winAnimator, final int left, final int top) {
+ mH.sendMessage(mH.obtainMessage(H.SET_WALLPAPER_OFFSET, left, top, winAnimator));
+ }
+
void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
final int dw = mAppDisplayWidth;
final int dh = mAppDisplayHeight;
@@ -2003,6 +2060,7 @@
winAnimator.computeShownFrameLocked();
// No need to lay out the windows - we can just set the wallpaper position
// directly.
+ // TODO(cmautner): Don't move this from here, just lock the WindowAnimator.
if (winAnimator.mSurfaceX != wallpaper.mShownFrame.left
|| winAnimator.mSurfaceY != wallpaper.mShownFrame.top) {
Surface.openTransaction();
@@ -2010,10 +2068,8 @@
if (SHOW_TRANSACTIONS) logSurface(wallpaper,
"POS " + wallpaper.mShownFrame.left
+ ", " + wallpaper.mShownFrame.top, null);
- winAnimator.mSurfaceX = wallpaper.mShownFrame.left;
- winAnimator.mSurfaceY = wallpaper.mShownFrame.top;
- winAnimator.mSurface.setPosition(wallpaper.mShownFrame.left,
- wallpaper.mShownFrame.top);
+ setWallpaperOffset(winAnimator, (int) wallpaper.mShownFrame.left,
+ (int) wallpaper.mShownFrame.top);
} catch (RuntimeException e) {
Slog.w(TAG, "Error positioning surface of " + wallpaper
+ " pos=(" + wallpaper.mShownFrame.left
@@ -2304,7 +2360,7 @@
+ " mExiting=" + win.mExiting
+ " isAnimating=" + win.mWinAnimator.isAnimating()
+ " app-animation="
- + (win.mAppToken != null ? win.mAppToken.animation : null)
+ + (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
+ " inPendingTransaction="
+ (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
+ " mDisplayFrozen=" + mDisplayFrozen);
@@ -2314,7 +2370,7 @@
// to hold off on removing the window until the animation is done.
// If the display is frozen, just remove immediately, since the
// animation wouldn't be seen.
- if (win.mWinAnimator.mSurface != null && okToDisplay()) {
+ if (win.mHasSurface && okToDisplay()) {
// If we are not currently running the exit animation, we
// need to see about starting one.
wasVisible = win.isWinVisibleLw();
@@ -2458,8 +2514,7 @@
}
static void logSurface(WindowState w, String msg, RuntimeException where) {
- String str = " SURFACE " + Integer.toHexString(w.hashCode())
- + ": " + msg + " / " + w.mAttrs.getTitle();
+ String str = " SURFACE " + msg + ": " + w;
if (where != null) {
Slog.i(TAG, str, where);
} else {
@@ -2475,26 +2530,20 @@
Slog.i(TAG, str);
}
}
-
+
+ // TODO(cmautner): Move to WindowStateAnimator.
+ void setTransparentRegionHint(final WindowStateAnimator winAnimator, final Region region) {
+ mH.sendMessage(mH.obtainMessage(H.SET_TRANSPARENT_REGION,
+ new Pair<WindowStateAnimator, Region>(winAnimator, region)));
+ }
+
void setTransparentRegionWindow(Session session, IWindow client, Region region) {
long origId = Binder.clearCallingIdentity();
try {
synchronized (mWindowMap) {
WindowState w = windowForClientLocked(session, client, false);
- WindowStateAnimator winAnimator = w.mWinAnimator;
- if ((w != null) && (winAnimator.mSurface != null)) {
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- ">>> OPEN TRANSACTION setTransparentRegion");
- Surface.openTransaction();
- try {
- if (SHOW_TRANSACTIONS) logSurface(w,
- "transparentRegionHint=" + region, null);
- winAnimator.mSurface.setTransparentRegionHint(region);
- } finally {
- Surface.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION setTransparentRegion");
- }
+ if ((w != null) && w.mHasSurface) {
+ setTransparentRegionHint(w.mWinAnimator, region);
}
}
} finally {
@@ -2617,11 +2666,12 @@
long origId = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
+ // TODO(cmautner): synchronize on mAnimator or win.mWinAnimator.
WindowState win = windowForClientLocked(session, client, false);
- WindowStateAnimator winAnimator = win.mWinAnimator;
if (win == null) {
return 0;
}
+ WindowStateAnimator winAnimator = win.mWinAnimator;
if (win.mRequestedWidth != requestedWidth
|| win.mRequestedHeight != requestedHeight) {
win.mLayoutNeeded = true;
@@ -2648,7 +2698,8 @@
}
flagChanges = win.mAttrs.flags ^= attrs.flags;
attrChanges = win.mAttrs.copyFrom(attrs);
- if ((attrChanges&WindowManager.LayoutParams.LAYOUT_CHANGED) != 0) {
+ if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
+ | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
win.mLayoutNeeded = true;
}
}
@@ -2701,6 +2752,7 @@
displayed = !win.isVisibleLw();
if (win.mExiting) {
winAnimator.cancelExitAnimationForNextAnimationLocked();
+ win.mExiting = false;
}
if (win.mDestroying) {
win.mDestroying = false;
@@ -2739,7 +2791,7 @@
surfaceChanged = true;
}
try {
- if (winAnimator.mSurface == null) {
+ if (!win.mHasSurface) {
surfaceChanged = true;
}
Surface surface = winAnimator.createSurfaceLocked();
@@ -3176,13 +3228,13 @@
}
Slog.v(TAG, "Loaded animation " + a + " for " + wtoken, e);
}
- wtoken.setAnimation(a, initialized);
+ wtoken.mAppAnimator.setAnimation(a, initialized);
}
} else {
- wtoken.clearAnimation();
+ wtoken.mAppAnimator.clearAnimation();
}
- return wtoken.animation != null;
+ return wtoken.mAppAnimator.animation != null;
}
// -------------------------------------------------------------
@@ -3327,7 +3379,7 @@
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
-
+
// Get the dispatching timeout here while we are not holding any locks so that it
// can be cached by the AppWindowToken. The timeout value is used later by the
// input dispatcher in code that does hold locks. If we did not cache the value
@@ -3415,15 +3467,13 @@
}
public int getOrientationFromAppTokensLocked() {
- int pos = mAppTokens.size() - 1;
int curGroup = 0;
int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean findingBehind = false;
boolean haveGroup = false;
boolean lastFullscreen = false;
- while (pos >= 0) {
+ for (int pos = mAppTokens.size() - 1; pos >= 0; pos--) {
AppWindowToken wtoken = mAppTokens.get(pos);
- pos--;
if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + wtoken);
@@ -3832,14 +3882,16 @@
wtoken.clientHidden = ttoken.clientHidden;
wtoken.sendAppVisibilityToClients();
}
- if (ttoken.animation != null) {
- wtoken.animation = ttoken.animation;
- wtoken.animating = ttoken.animating;
- wtoken.animLayerAdjustment = ttoken.animLayerAdjustment;
- ttoken.animation = null;
- ttoken.animLayerAdjustment = 0;
- wtoken.updateLayers();
- ttoken.updateLayers();
+ final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator;
+ final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator;
+ if (tAppAnimator.animation != null) {
+ wAppAnimator.animation = tAppAnimator.animation;
+ wAppAnimator.animating = tAppAnimator.animating;
+ wAppAnimator.animLayerAdjustment = tAppAnimator.animLayerAdjustment;
+ tAppAnimator.animation = null;
+ tAppAnimator.animLayerAdjustment = 0;
+ wAppAnimator.updateLayers();
+ tAppAnimator.updateLayers();
}
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -3864,18 +3916,20 @@
mH.sendMessageAtFrontOfQueue(m);
return;
}
- if (ttoken.thumbnail != null) {
+ final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator;
+ final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator;
+ if (tAppAnimator.thumbnail != null) {
// The old token is animating with a thumbnail, transfer
// that to the new token.
- if (wtoken.thumbnail != null) {
- wtoken.thumbnail.destroy();
+ if (wAppAnimator.thumbnail != null) {
+ wAppAnimator.thumbnail.destroy();
}
- wtoken.thumbnail = ttoken.thumbnail;
- wtoken.thumbnailX = ttoken.thumbnailX;
- wtoken.thumbnailY = ttoken.thumbnailY;
- wtoken.thumbnailLayer = ttoken.thumbnailLayer;
- wtoken.thumbnailAnimation = ttoken.thumbnailAnimation;
- ttoken.thumbnail = null;
+ wAppAnimator.thumbnail = tAppAnimator.thumbnail;
+ wAppAnimator.thumbnailX = tAppAnimator.thumbnailX;
+ wAppAnimator.thumbnailY = tAppAnimator.thumbnailY;
+ wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
+ wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
+ tAppAnimator.thumbnail = null;
}
}
}
@@ -3965,12 +4019,12 @@
boolean runningAppAnimation = false;
if (transit != WindowManagerPolicy.TRANSIT_UNSET) {
- if (wtoken.animation == sDummyAnimation) {
- wtoken.animation = null;
+ if (wtoken.mAppAnimator.animation == sDummyAnimation) {
+ wtoken.mAppAnimator.animation = null;
}
applyAnimationLocked(wtoken, lp, transit, visible);
changed = true;
- if (wtoken.animation != null) {
+ if (wtoken.mAppAnimator.animation != null) {
delayed = runningAppAnimation = true;
}
}
@@ -4033,7 +4087,7 @@
}
}
- if (wtoken.animation != null) {
+ if (wtoken.mAppAnimator.animation != null) {
delayed = true;
}
@@ -4078,7 +4132,7 @@
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Setting dummy animation on: " + wtoken);
- wtoken.setDummyAnimation();
+ wtoken.mAppAnimator.setDummyAnimation();
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
wtoken.waitingToShow = wtoken.waitingToHide = false;
@@ -4128,7 +4182,7 @@
void unsetAppFreezingScreenLocked(AppWindowToken wtoken,
boolean unfreezeSurfaceNow, boolean force) {
- if (wtoken.freezingScreen) {
+ if (wtoken.mAppAnimator.freezingScreen) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Clear freezing of " + wtoken
+ " force=" + force);
final int N = wtoken.allAppWindows.size();
@@ -4137,7 +4191,7 @@
WindowState w = wtoken.allAppWindows.get(i);
if (w.mAppFreezing) {
w.mAppFreezing = false;
- if (w.mWinAnimator.mSurface != null && !w.mOrientationChanging) {
+ if (w.mHasSurface && !w.mOrientationChanging) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "set mOrientationChanging of " + w);
w.mOrientationChanging = true;
}
@@ -4146,7 +4200,7 @@
}
if (force || unfrozeWindows) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "No longer freezing: " + wtoken);
- wtoken.freezingScreen = false;
+ wtoken.mAppAnimator.freezingScreen = false;
mAppsFreezingScreen--;
}
if (unfreezeSurfaceNow) {
@@ -4169,11 +4223,11 @@
}
Slog.i(TAG, "Set freezing of " + wtoken.appToken
+ ": hidden=" + wtoken.hidden + " freezing="
- + wtoken.freezingScreen, e);
+ + wtoken.mAppAnimator.freezingScreen, e);
}
if (!wtoken.hiddenRequested) {
- if (!wtoken.freezingScreen) {
- wtoken.freezingScreen = true;
+ if (!wtoken.mAppAnimator.freezingScreen) {
+ wtoken.mAppAnimator.freezingScreen = true;
mAppsFreezingScreen++;
if (mAppsFreezingScreen == 1) {
startFreezingDisplayLocked(false);
@@ -4226,7 +4280,7 @@
}
final long origId = Binder.clearCallingIdentity();
if (DEBUG_ORIENTATION) Slog.v(TAG, "Clear freezing of " + token
- + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.freezingScreen);
+ + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.mAppAnimator.freezingScreen);
unsetAppFreezingScreenLocked(wtoken, true, force);
Binder.restoreCallingIdentity(origId);
}
@@ -4261,8 +4315,8 @@
}
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Removing app " + wtoken + " delayed=" + delayed
- + " animation=" + wtoken.animation
- + " animating=" + wtoken.animating);
+ + " animation=" + wtoken.mAppAnimator.animation
+ + " animating=" + wtoken.mAppAnimator.animating);
if (delayed) {
// set the token aside because it has an active animation to be finished
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
@@ -4272,9 +4326,8 @@
// Make sure there is no animation running on this token,
// so any windows associated with it will be removed as
// soon as their animations are complete
- wtoken.clearAnimation();
- wtoken.animation = null;
- wtoken.animating = false;
+ wtoken.mAppAnimator.clearAnimation();
+ wtoken.mAppAnimator.animating = false;
}
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
"removeAppToken: " + wtoken);
@@ -4725,7 +4778,7 @@
synchronized(mWindowMap) {
for (int i=mWindows.size()-1; i>=0; i--) {
WindowState w = mWindows.get(i);
- if (w.mWinAnimator.mSurface != null) {
+ if (w.mHasSurface) {
try {
w.mClient.closeSystemDialogs(reason);
} catch (RemoteException e) {
@@ -5197,7 +5250,7 @@
boolean including = false;
for (int i=mWindows.size()-1; i>=0; i--) {
WindowState ws = mWindows.get(i);
- if (ws.mWinAnimator.mSurface == null) {
+ if (!ws.mHasSurface) {
continue;
}
if (ws.mLayer >= aboveAppLayer) {
@@ -5511,7 +5564,7 @@
for (int i=mWindows.size()-1; i>=0; i--) {
WindowState w = mWindows.get(i);
- if (w.mWinAnimator.mSurface != null) {
+ if (w.mHasSurface) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w);
w.mOrientationChanging = true;
}
@@ -6658,6 +6711,11 @@
public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
public static final int BULK_UPDATE_PARAMETERS = 25;
+ public static final int ANIMATOR_WHAT_OFFSET = 100000;
+ public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1;
+ public static final int SET_WALLPAPER_OFFSET = ANIMATOR_WHAT_OFFSET + 2;
+ public static final int SET_DIM_PARAMETERS = ANIMATOR_WHAT_OFFSET + 3;
+
private Session mLastReportedHold;
public H() {
@@ -6980,14 +7038,16 @@
case APP_FREEZE_TIMEOUT: {
synchronized (mWindowMap) {
- Slog.w(TAG, "App freeze timeout expired.");
- int i = mAppTokens.size();
- while (i > 0) {
- i--;
- AppWindowToken tok = mAppTokens.get(i);
- if (tok.freezingScreen) {
- Slog.w(TAG, "Force clearing freeze: " + tok);
- unsetAppFreezingScreenLocked(tok, true, true);
+ synchronized (mAnimator) {
+ Slog.w(TAG, "App freeze timeout expired.");
+ int i = mAppTokens.size();
+ while (i > 0) {
+ i--;
+ AppWindowToken tok = mAppTokens.get(i);
+ if (tok.mAppAnimator.freezingScreen) {
+ Slog.w(TAG, "Force clearing freeze: " + tok);
+ unsetAppFreezingScreenLocked(tok, true, true);
+ }
}
}
}
@@ -7069,6 +7129,7 @@
}
case BULK_UPDATE_PARAMETERS: {
+ // Used to send multiple changes from the animation side to the layout side.
synchronized (mWindowMap) {
// TODO(cmautner): As the number of bits grows, use masks of bit groups to
// eliminate unnecessary tests.
@@ -7078,9 +7139,50 @@
if ((msg.arg1 & LayoutFields.SET_WALLPAPER_MAY_CHANGE) != 0) {
mInnerFields.mWallpaperMayChange = true;
}
+ if ((msg.arg1 & LayoutFields.SET_FORCE_HIDING_CHANGED) != 0) {
+ mInnerFields.mWallpaperForceHidingChanged = true;
+ }
+ if ((msg.arg1 & LayoutFields.CLEAR_ORIENTATION_CHANGE_COMPLETE) != 0) {
+ mInnerFields.mOrientationChangeComplete = false;
+ }
requestTraversalLocked();
}
+ break;
+ }
+
+ // Animation messages. Move to Window{State}Animator
+ case SET_TRANSPARENT_REGION: {
+ // TODO(cmautner): Remove sync.
+ synchronized (mAnimator) {
+ Pair<WindowStateAnimator, Region> pair =
+ (Pair<WindowStateAnimator, Region>) msg.obj;
+ final WindowStateAnimator winAnimator = pair.first;
+ winAnimator.setTransparentRegionHint(pair.second);
+ }
+
+ scheduleAnimationLocked();
+ break;
+ }
+
+ case SET_WALLPAPER_OFFSET: {
+ // TODO(cmautner): Remove sync.
+ synchronized (mAnimator) {
+ final WindowStateAnimator winAnimator = (WindowStateAnimator) msg.obj;
+ winAnimator.setWallpaperOffset(msg.arg1, msg.arg2);
+ }
+
+ scheduleAnimationLocked();
+ break;
+ }
+
+ case SET_DIM_PARAMETERS: {
+ synchronized (mAnimator) {
+ mAnimator.mDimParams = (DimAnimator.Parameters) msg.obj;
+ }
+
+ scheduleAnimationLocked();
+ break;
}
}
}
@@ -7428,9 +7530,11 @@
w.mLayer = curLayer;
}
if (w.mTargetAppToken != null) {
- w.mWinAnimator.mAnimLayer = w.mLayer + w.mTargetAppToken.animLayerAdjustment;
+ w.mWinAnimator.mAnimLayer =
+ w.mLayer + w.mTargetAppToken.mAppAnimator.animLayerAdjustment;
} else if (w.mAppToken != null) {
- w.mWinAnimator.mAnimLayer = w.mLayer + w.mAppToken.animLayerAdjustment;
+ w.mWinAnimator.mAnimLayer =
+ w.mLayer + w.mAppToken.mAppAnimator.animLayerAdjustment;
} else {
w.mWinAnimator.mAnimLayer = w.mLayer;
}
@@ -7864,10 +7968,10 @@
AppWindowToken wtoken = mOpeningApps.get(i);
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"Now opening app" + wtoken);
- wtoken.clearThumbnail();
+ wtoken.mAppAnimator.clearThumbnail();
wtoken.reportedVisible = false;
wtoken.inPendingTransaction = false;
- wtoken.animation = null;
+ wtoken.mAppAnimator.animation = null;
setTokenVisibilityLocked(wtoken, animLp, true,
transit, false);
wtoken.updateReportedVisibilityLocked();
@@ -7892,9 +7996,9 @@
AppWindowToken wtoken = mClosingApps.get(i);
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"Now closing app" + wtoken);
- wtoken.clearThumbnail();
+ wtoken.mAppAnimator.clearThumbnail();
wtoken.inPendingTransaction = false;
- wtoken.animation = null;
+ wtoken.mAppAnimator.animation = null;
setTokenVisibilityLocked(wtoken, animLp, false,
transit, false);
wtoken.updateReportedVisibilityLocked();
@@ -7906,7 +8010,7 @@
}
if (mNextAppTransitionThumbnail != null && topOpeningApp != null
- && topOpeningApp.animation != null) {
+ && topOpeningApp.mAppAnimator.animation != null) {
// This thumbnail animation is very special, we need to have
// an extra surface with the thumbnail included with the animation.
Rect dirty = new Rect(0, 0, mNextAppTransitionThumbnail.getWidth(),
@@ -7915,7 +8019,7 @@
Surface surface = new Surface(mFxSession, Process.myPid(),
"thumbnail anim", 0, dirty.width(), dirty.height(),
PixelFormat.TRANSLUCENT, Surface.HIDDEN);
- topOpeningApp.thumbnail = surface;
+ topOpeningApp.mAppAnimator.thumbnail = surface;
if (SHOW_TRANSACTIONS) Slog.i(TAG, " THUMBNAIL "
+ surface + ": CREATE");
Surface drawSurface = new Surface();
@@ -7924,17 +8028,17 @@
c.drawBitmap(mNextAppTransitionThumbnail, 0, 0, null);
drawSurface.unlockCanvasAndPost(c);
drawSurface.release();
- topOpeningApp.thumbnailLayer = topOpeningLayer;
+ topOpeningApp.mAppAnimator.thumbnailLayer = topOpeningLayer;
Animation anim = createThumbnailAnimationLocked(transit, true, true);
- topOpeningApp.thumbnailAnimation = anim;
+ topOpeningApp.mAppAnimator.thumbnailAnimation = anim;
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mTransitionAnimationScale);
- topOpeningApp.thumbnailX = mNextAppTransitionStartX;
- topOpeningApp.thumbnailY = mNextAppTransitionStartY;
+ topOpeningApp.mAppAnimator.thumbnailX = mNextAppTransitionStartX;
+ topOpeningApp.mAppAnimator.thumbnailY = mNextAppTransitionStartY;
} catch (Surface.OutOfResourcesException e) {
Slog.e(TAG, "Can't allocate thumbnail surface w=" + dirty.width()
+ " h=" + dirty.height(), e);
- topOpeningApp.clearThumbnail();
+ topOpeningApp.mAppAnimator.clearThumbnail();
}
}
@@ -8016,7 +8120,6 @@
}
}
mInnerFields.mAdjResult |= adjustWallpaperWindowsLocked();
- mInnerFields.mWallpaperForceHidingChanged = false;
if (DEBUG_WALLPAPER) Slog.v(TAG, "****** OLD: " + oldWallpaper
+ " NEW: " + mWallpaperTarget
+ " LOWER: " + mLowerWallpaperTarget);
@@ -8026,7 +8129,7 @@
mAnimator.mForceHiding = false;
for (int i=mWindows.size()-1; i>=0; i--) {
WindowState w = mWindows.get(i);
- if (w.mWinAnimator.mSurface != null) {
+ if (w.mHasSurface) {
final WindowManager.LayoutParams attrs = w.mAttrs;
if (mPolicy.doesForceHide(w, attrs) && w.isVisibleLw()) {
if (DEBUG_FOCUS) Slog.i(TAG, "win=" + w + " force hides other windows");
@@ -8088,9 +8191,7 @@
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation start waiting for draw in "
+ w + ", surface " + w.mWinAnimator.mSurface);
- winAnimator.mDrawPending = true;
- winAnimator.mCommitDrawPending = false;
- w.mReadyToShow = false;
+ winAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
if (w.mAppToken != null) {
w.mAppToken.allDrawn = false;
}
@@ -8126,7 +8227,7 @@
final int attrFlags = attrs.flags;
final boolean canBeSeen = w.isDisplayedLw();
- if (w.mWinAnimator.mSurface != null) {
+ if (w.mHasSurface) {
if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
mInnerFields.mHoldScreen = w.mSession;
}
@@ -8157,16 +8258,16 @@
if (!mInnerFields.mDimming) {
//Slog.i(TAG, "DIM BEHIND: " + w);
mInnerFields.mDimming = true;
- if (mDimAnimator == null) {
- mDimAnimator = new DimAnimator(mFxSession);
- }
+ final int width, height;
if (attrs.type == WindowManager.LayoutParams.TYPE_BOOT_PROGRESS) {
- mDimAnimator.show(mCurDisplayWidth, mCurDisplayHeight);
+ width = mCurDisplayWidth;
+ height = mCurDisplayHeight;
} else {
- mDimAnimator.show(innerDw, innerDh);
+ width = innerDw;
+ height = innerDh;
}
- mDimAnimator.updateParameters(mContext.getResources(),
- w, currentTime);
+ mAnimator.startDimming(w.mWinAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount,
+ width, height);
}
}
}
@@ -8203,7 +8304,6 @@
mExitingAppTokens.get(i).hasVisible = false;
}
- mInnerFields.mOrientationChangeComplete = true;
mInnerFields.mHoldScreen = null;
mInnerFields.mScreenBrightness = -1;
mInnerFields.mButtonBrightness = -1;
@@ -8232,7 +8332,6 @@
}
try {
- mInnerFields.mWallpaperForceHidingChanged = false;
int repeats = 0;
do {
@@ -8243,7 +8342,8 @@
break;
}
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
+ mPendingLayoutChanges);
if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
@@ -8274,16 +8374,18 @@
// FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
// it is animating.
mPendingLayoutChanges = 0;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number " + mLayoutRepeatCount);
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number " + mLayoutRepeatCount,
+ mPendingLayoutChanges);
mPolicy.beginAnimationLw(dw, dh);
for (i = mWindows.size() - 1; i >= 0; i--) {
WindowState w = mWindows.get(i);
- if (w.mWinAnimator.mSurface != null) {
+ if (w.mHasSurface) {
mPolicy.animatingWindowLw(w, w.mAttrs);
}
}
mPendingLayoutChanges |= mPolicy.finishAnimationLw();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishAnimationLw");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishAnimationLw",
+ mPendingLayoutChanges);
} while (mPendingLayoutChanges != 0);
final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
@@ -8317,6 +8419,9 @@
updateWallpaperVisibilityLocked();
}
}
+ if (!mInnerFields.mDimming) {
+ mAnimator.stopDimming();
+ }
} catch (RuntimeException e) {
Log.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
@@ -8328,7 +8433,8 @@
// to go.
if (mAppTransitionReady) {
mPendingLayoutChanges |= handleAppTransitionReadyLocked();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked",
+ mPendingLayoutChanges);
}
mInnerFields.mAdjResult = 0;
@@ -8341,7 +8447,8 @@
// be out of sync with it. So here we will just rebuild the
// entire app window list. Fun!
mPendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock",
+ mPendingLayoutChanges);
}
if (mInnerFields.mWallpaperForceHidingChanged && mPendingLayoutChanges == 0 &&
@@ -8353,9 +8460,10 @@
// hard -- the wallpaper now needs to be shown behind
// something that was hidden.
mPendingLayoutChanges |= animateAwayWallpaperLocked();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked",
+ mPendingLayoutChanges);
}
-
+ mInnerFields.mWallpaperForceHidingChanged = false;
if (mInnerFields.mWallpaperMayChange) {
if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
@@ -8385,7 +8493,7 @@
if (mLayoutNeeded) {
mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded", mPendingLayoutChanges);
}
final int N = mWindows.size();
@@ -8396,7 +8504,7 @@
updateResizingWindows(w);
// Moved from updateWindowsAndWallpaperLocked().
- if (winAnimator.mSurface != null) {
+ if (w.mHasSurface) {
// Take care of the window being ready to display.
if (winAnimator.commitFinishDrawingLocked(currentTime)) {
if ((w.mAttrs.flags
@@ -8406,7 +8514,8 @@
mInnerFields.mWallpaperMayChange = true;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- debugLayoutRepeats("updateWindowsAndWallpaperLocked 1");
+ debugLayoutRepeats("updateWindowsAndWallpaperLocked 1",
+ mPendingLayoutChanges);
}
}
}
@@ -8435,7 +8544,7 @@
// associated with exiting/removed apps
mAnimator.animate();
mPendingLayoutChanges |= mAnimator.mPendingLayoutChanges;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animate()");
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animate()", mPendingLayoutChanges);
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
@@ -8455,10 +8564,8 @@
stopFreezingDisplayLocked();
}
- i = mResizingWindows.size();
- if (i > 0) {
- do {
- i--;
+ if (!mResizingWindows.isEmpty()) {
+ for (i = mResizingWindows.size() - 1; i >= 0; i--) {
WindowState win = mResizingWindows.get(i);
final WindowStateAnimator winAnimator = win.mWinAnimator;
try {
@@ -8477,19 +8584,21 @@
+ Integer.toHexString(diff));
}
win.mConfiguration = mCurConfiguration;
- if (DEBUG_ORIENTATION && winAnimator.mDrawPending) Slog.i(
+ if (DEBUG_ORIENTATION &&
+ winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING) Slog.i(
TAG, "Resizing " + win + " WITH DRAW PENDING");
win.mClient.resized((int)winAnimator.mSurfaceW,
(int)winAnimator.mSurfaceH,
win.mLastContentInsets, win.mLastVisibleInsets,
- winAnimator.mDrawPending, configChanged ? win.mConfiguration : null);
+ winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING,
+ configChanged ? win.mConfiguration : null);
win.mContentInsetsChanged = false;
win.mVisibleInsetsChanged = false;
winAnimator.mSurfaceResized = false;
} catch (RemoteException e) {
win.mOrientationChanging = false;
}
- } while (i > 0);
+ }
mResizingWindows.clear();
}
@@ -8530,9 +8639,8 @@
// Make sure there is no animation running on this token,
// so any windows associated with it will be removed as
// soon as their animations are complete
- token.clearAnimation();
- token.animation = null;
- token.animating = false;
+ token.mAppAnimator.clearAnimation();
+ token.mAppAnimator.animating = false;
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
"performLayout: App token exiting now removed" + token);
mAppTokens.remove(token);
@@ -8609,6 +8717,7 @@
!mInnerFields.mUpdateRotation) {
checkDrawnWindowsLocked();
}
+ mInnerFields.mOrientationChangeComplete = true;
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
@@ -8724,6 +8833,7 @@
wsa.mSurface.destroy();
wsa.mSurfaceShown = false;
wsa.mSurface = null;
+ ws.mHasSurface = false;
mForceRemoves.add(ws);
i--;
N--;
@@ -8736,6 +8846,7 @@
wsa.mSurface.destroy();
wsa.mSurfaceShown = false;
wsa.mSurface = null;
+ ws.mHasSurface = false;
leakedSurface = true;
}
}
@@ -8774,6 +8885,7 @@
surface.destroy();
winAnimator.mSurfaceShown = false;
winAnimator.mSurface = null;
+ winAnimator.mWin.mHasSurface = false;
}
try {
@@ -8799,7 +8911,7 @@
TAG, "Changing focus from " + mCurrentFocus + " to " + newFocus);
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
- mAnimator.setCurrentFocus(mCurrentFocus);
+ mAnimator.setCurrentFocus(newFocus);
mLosingFocus.remove(newFocus);
int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
@@ -8846,12 +8958,11 @@
WindowState result = null;
WindowState win;
- int i = mWindows.size() - 1;
int nextAppIndex = mAppTokens.size()-1;
WindowToken nextApp = nextAppIndex >= 0
? mAppTokens.get(nextAppIndex) : null;
- while (i >= 0) {
+ for (int i = mWindows.size() - 1; i >= 0; i--) {
win = mWindows.get(i);
if (localLOGV || DEBUG_FOCUS) Slog.v(
@@ -8864,7 +8975,6 @@
// If this window's application has been removed, just skip it.
if (thisApp != null && thisApp.removed) {
- i--;
continue;
}
@@ -8904,8 +9014,6 @@
result = win;
break;
}
-
- i--;
}
return result;
@@ -9453,12 +9561,6 @@
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
pw.print(" mLayoutNeeded="); pw.println(mLayoutNeeded);
- if (mDimAnimator != null) {
- pw.println(" mDimAnimator:");
- mDimAnimator.printTo(" ", pw);
- } else {
- pw.println( " no DimAnimator ");
- }
pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen);
pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
@@ -9668,10 +9770,10 @@
requestTraversalLocked();
}
- void debugLayoutRepeats(final String msg) {
+ void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) {
- Slog.v(TAG, "Layouts looping: " + msg);
- Slog.v(TAG, "mPendingLayoutChanges = 0x" + Integer.toHexString(mPendingLayoutChanges));
+ Slog.v(TAG, "Layouts looping: " + msg + ", mPendingLayoutChanges = 0x" +
+ Integer.toHexString(pendingLayoutChanges));
}
}
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 7f63429..05797a4 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -65,6 +65,9 @@
WindowToken mRootToken;
AppWindowToken mAppToken;
AppWindowToken mTargetAppToken;
+
+ // mAttrs.flags is tested in animation without being locked. If the bits tested are ever
+ // modified they will need to be locked.
final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
final DeathRecipient mDeathRecipient;
final WindowState mAttachedWindow;
@@ -83,7 +86,6 @@
boolean mPolicyVisibilityAfterAnim = true;
boolean mAppFreezing;
boolean mAttachedHidden; // is our parent window hidden?
- boolean mLastHidden; // was this window last hidden?
boolean mWallpaperVisible; // for wallpaper, what was last vis report?
/**
@@ -206,15 +208,6 @@
// when in that case until the layout is done.
boolean mLayoutNeeded;
- // This is set during the time after the window's drawing has been
- // committed, and before its surface is actually shown. It is used
- // to delay showing the surface until all windows in a token are ready
- // to be shown.
- boolean mReadyToShow;
-
- // Set when the window has been shown in the screen the first time.
- boolean mHasDrawn;
-
// Currently running an exit animation?
boolean mExiting;
@@ -246,6 +239,8 @@
final WindowStateAnimator mWinAnimator;
+ boolean mHasSurface = false;
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int seq, WindowManager.LayoutParams a,
int viewVisibility) {
@@ -609,7 +604,7 @@
*/
public boolean isVisibleLw() {
final AppWindowToken atoken = mAppToken;
- return mWinAnimator.mSurface != null && mPolicyVisibility && !mAttachedHidden
+ return mHasSurface && mPolicyVisibility && !mAttachedHidden
&& (atoken == null || !atoken.hiddenRequested)
&& !mExiting && !mDestroying;
}
@@ -629,8 +624,8 @@
}
final AppWindowToken atoken = mAppToken;
final boolean animating = atoken != null
- ? (atoken.animation != null) : false;
- return mWinAnimator.mSurface != null && !mDestroying && !mExiting
+ ? (atoken.mAppAnimator.animation != null) : false;
+ return mHasSurface && !mDestroying && !mExiting
&& (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
&& ((!mAttachedHidden && mViewVisibility == View.VISIBLE
&& !mRootToken.hidden)
@@ -644,8 +639,8 @@
*/
public boolean isWinVisibleLw() {
final AppWindowToken atoken = mAppToken;
- return mWinAnimator.mSurface != null && mPolicyVisibility && !mAttachedHidden
- && (atoken == null || !atoken.hiddenRequested || atoken.animating)
+ return mHasSurface && mPolicyVisibility && !mAttachedHidden
+ && (atoken == null || !atoken.hiddenRequested || atoken.mAppAnimator.animating)
&& !mExiting && !mDestroying;
}
@@ -654,7 +649,7 @@
* the associated app token, not the pending requested hidden state.
*/
boolean isVisibleNow() {
- return mWinAnimator.mSurface != null && mPolicyVisibility && !mAttachedHidden
+ return mHasSurface && mPolicyVisibility && !mAttachedHidden
&& !mRootToken.hidden && !mExiting && !mDestroying;
}
@@ -674,7 +669,7 @@
*/
boolean isVisibleOrAdding() {
final AppWindowToken atoken = mAppToken;
- return ((mWinAnimator.mSurface != null && !mWinAnimator.mReportDestroySurface)
+ return ((mHasSurface && !mWinAnimator.mReportDestroySurface)
|| (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
&& mPolicyVisibility && !mAttachedHidden
&& (atoken == null || !atoken.hiddenRequested)
@@ -687,15 +682,15 @@
* being visible.
*/
boolean isOnScreen() {
+ if (!mHasSurface || !mPolicyVisibility || mDestroying) {
+ return false;
+ }
final AppWindowToken atoken = mAppToken;
if (atoken != null) {
- return mWinAnimator.mSurface != null && mPolicyVisibility && !mDestroying
- && ((!mAttachedHidden && !atoken.hiddenRequested)
- || mWinAnimator.mAnimation != null || atoken.animation != null);
- } else {
- return mWinAnimator.mSurface != null && mPolicyVisibility && !mDestroying
- && (!mAttachedHidden || mWinAnimator.mAnimation != null);
+ return ((!mAttachedHidden && !atoken.hiddenRequested)
+ || mWinAnimator.mAnimation != null || atoken.mAppAnimator.animation != null);
}
+ return !mAttachedHidden || mWinAnimator.mAnimation != null;
}
/**
@@ -707,11 +702,11 @@
mService.mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
return false;
}
- return mWinAnimator.mSurface != null && mPolicyVisibility && !mDestroying
+ return mHasSurface && mPolicyVisibility && !mDestroying
&& ((!mAttachedHidden && mViewVisibility == View.VISIBLE
&& !mRootToken.hidden)
|| mWinAnimator.mAnimation != null
- || ((mAppToken != null) && (mAppToken.animation != null)));
+ || ((mAppToken != null) && (mAppToken.mAppAnimator.animation != null)));
}
/**
@@ -741,8 +736,9 @@
* complete UI in to.
*/
public boolean isDrawnLw() {
- return mWinAnimator.mSurface != null && !mDestroying
- && !mWinAnimator.mDrawPending && !mWinAnimator.mCommitDrawPending;
+ return mHasSurface && !mDestroying &&
+ (mWinAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW
+ || mWinAnimator.mDrawState == WindowStateAnimator.HAS_DRAWN);
}
/**
@@ -753,7 +749,7 @@
return (mAttrs.format == PixelFormat.OPAQUE
|| mAttrs.type == TYPE_WALLPAPER)
&& isDrawnLw() && mWinAnimator.mAnimation == null
- && (mAppToken == null || mAppToken.animation == null);
+ && (mAppToken == null || mAppToken.mAppAnimator.animation == null);
}
/**
@@ -762,7 +758,7 @@
* sense to call from performLayoutAndPlaceSurfacesLockedInner().)
*/
boolean shouldAnimateMove() {
- return mContentChanged && !mExiting && !mLastHidden && mService.okToDisplay()
+ return mContentChanged && !mExiting && !mWinAnimator.mLastHidden && mService.okToDisplay()
&& (mFrame.top != mLastFrame.top
|| mFrame.left != mLastFrame.left)
&& (mAttachedWindow == null || !mAttachedWindow.shouldAnimateMove());
@@ -835,10 +831,12 @@
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0);
}
+ @Override
public boolean hasDrawnLw() {
- return mHasDrawn;
+ return mWinAnimator.mDrawState == WindowStateAnimator.HAS_DRAWN;
}
+ @Override
public boolean showLw(boolean doAnimation) {
return showLw(doAnimation, true);
}
@@ -967,8 +965,9 @@
pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
pw.print(" mSubLayer="); pw.print(mSubLayer);
pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+");
- pw.print((mTargetAppToken != null ? mTargetAppToken.animLayerAdjustment
- : (mAppToken != null ? mAppToken.animLayerAdjustment : 0)));
+ pw.print((mTargetAppToken != null ?
+ mTargetAppToken.mAppAnimator.animLayerAdjustment
+ : (mAppToken != null ? mAppToken.mAppAnimator.animLayerAdjustment : 0)));
pw.print("="); pw.print(mWinAnimator.mAnimLayer);
pw.print(" mLastLayer="); pw.println(mWinAnimator.mLastLayer);
}
@@ -983,7 +982,6 @@
}
pw.print(prefix); pw.print("mViewVisibility=0x");
pw.print(Integer.toHexString(mViewVisibility));
- pw.print(" mLastHidden="); pw.print(mLastHidden);
pw.print(" mHaveFrame="); pw.print(mHaveFrame);
pw.print(" mObscured="); pw.println(mObscured);
pw.print(prefix); pw.print("mSeq="); pw.print(mSeq);
@@ -1017,8 +1015,8 @@
}
pw.print(prefix); pw.print("mConfiguration="); pw.println(mConfiguration);
}
- pw.print(prefix); pw.print("mShownFrame=");
- mShownFrame.printShortString(pw); pw.println();
+ pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
+ pw.print(" mShownFrame="); mShownFrame.printShortString(pw); pw.println();
if (dumpAll) {
pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
pw.print(" last="); mLastFrame.printShortString(pw);
@@ -1046,12 +1044,6 @@
pw.println();
}
mWinAnimator.dump(pw, prefix, dumpAll);
- if (dumpAll) {
- pw.print(prefix); pw.print("mDrawPending="); pw.print(mWinAnimator.mDrawPending);
- pw.print(" mCommitDrawPending="); pw.print(mWinAnimator.mCommitDrawPending);
- pw.print(" mReadyToShow="); pw.print(mReadyToShow);
- pw.print(" mHasDrawn="); pw.println(mHasDrawn);
- }
if (mExiting || mRemoveOnExit || mDestroying || mRemoved) {
pw.print(prefix); pw.print("mExiting="); pw.print(mExiting);
pw.print(" mRemoveOnExit="); pw.print(mRemoveOnExit);
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index e99340c..6d0921e 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -5,10 +5,13 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.wm.WindowManagerService.LayoutFields.CLEAR_ORIENTATION_CHANGE_COMPLETE;
+
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.RemoteException;
import android.util.Slog;
import android.view.Surface;
@@ -35,6 +38,7 @@
static final boolean SHOW_LIGHT_TRANSACTIONS = WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
static final boolean SHOW_SURFACE_ALLOC = WindowManagerService.SHOW_SURFACE_ALLOC;
static final boolean localLOGV = WindowManagerService.localLOGV;
+ static final boolean DEBUG_ORIENTATION = WindowManagerService.DEBUG_ORIENTATION;
static final String TAG = "WindowStateAnimator";
@@ -98,14 +102,24 @@
// an enter animation.
boolean mEnterAnimationPending;
- // This is set after the Surface has been created but before the
- // window has been drawn. During this time the surface is hidden.
- boolean mDrawPending;
+ /** This is set when there is no Surface */
+ static final int NO_SURFACE = 0;
+ /** This is set after the Surface has been created but before the window has been drawn. During
+ * this time the surface is hidden. */
+ static final int DRAW_PENDING = 1;
+ /** This is set after the window has finished drawing for the first time but before its surface
+ * is shown. The surface will be displayed when the next layout is run. */
+ static final int COMMIT_DRAW_PENDING = 2;
+ /** This is set during the time after the window's drawing has been committed, and before its
+ * surface is actually shown. It is used to delay showing the surface until all windows in a
+ * token are ready to be shown. */
+ static final int READY_TO_SHOW = 3;
+ /** Set when the window has been shown in the screen the first time. */
+ static final int HAS_DRAWN = 4;
+ int mDrawState;
- // This is set after the window has finished drawing for the first
- // time but before its surface is shown. The surface will be
- // displayed when the next layout is run.
- boolean mCommitDrawPending;
+ /** Was this window last hidden? */
+ boolean mLastHidden;
public WindowStateAnimator(final WindowManagerService service, final WindowState win,
final WindowState attachedWindow) {
@@ -128,7 +142,7 @@
mAnimation.scaleCurrentDuration(mService.mWindowAnimationScale);
// Start out animation gone if window is gone, or visible if window is visible.
mTransformation.clear();
- mTransformation.setAlpha(mWin.mLastHidden ? 0 : 1);
+ mTransformation.setAlpha(mLastHidden ? 0 : 1);
mHasLocalTransformation = true;
}
@@ -148,7 +162,7 @@
return mAnimation != null
|| (attached != null && attached.mWinAnimator.mAnimation != null)
|| (atoken != null &&
- (atoken.animation != null
+ (atoken.mAppAnimator.animation != null
|| atoken.inPendingTransaction));
}
@@ -158,13 +172,11 @@
}
void cancelExitAnimationForNextAnimationLocked() {
- if (!mWin.mExiting) return;
if (mAnimation != null) {
mAnimation.cancel();
mAnimation = null;
destroySurfaceLocked();
}
- mWin.mExiting = false;
}
private boolean stepAnimation(long currentTime) {
@@ -216,7 +228,7 @@
}
mHasLocalTransformation = false;
if ((!mLocalAnimating || mAnimationIsEntrance) && mWin.mAppToken != null
- && mWin.mAppToken.animation != null) {
+ && mWin.mAppToken.mAppAnimator.animation != null) {
// When our app token is animating, we kind-of pretend like
// we are as well. Note the mLocalAnimating mAnimationIsEntrance
// part of this check means that we will only do this if
@@ -291,7 +303,7 @@
}
}
mTransformation.clear();
- if (mWin.mHasDrawn
+ if (mDrawState == HAS_DRAWN
&& mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
&& mWin.mAppToken != null
&& mWin.mAppToken.firstWindowDrawn
@@ -303,8 +315,9 @@
}
finishExit();
- mService.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats("WindowState");
+ mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats(
+ "WindowStateAnimator", mAnimator.mPendingLayoutChanges);
if (mWin.mAppToken != null) {
mWin.mAppToken.updateReportedVisibilityLocked();
@@ -347,7 +360,7 @@
} catch (RuntimeException e) {
Slog.w(TAG, "Error hiding surface in " + this, e);
}
- mWin.mLastHidden = true;
+ mLastHidden = true;
}
mWin.mExiting = false;
if (mWin.mRemoveOnExit) {
@@ -357,11 +370,10 @@
}
boolean finishDrawingLocked() {
- if (mDrawPending) {
- if (SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION) Slog.v(
+ if (mDrawState == DRAW_PENDING) {
+ if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Slog.v(
TAG, "finishDrawingLocked: " + this + " in " + mSurface);
- mCommitDrawPending = true;
- mDrawPending = false;
+ mDrawState = COMMIT_DRAW_PENDING;
return true;
}
return false;
@@ -370,11 +382,10 @@
// This must be called while inside a transaction.
boolean commitFinishDrawingLocked(long currentTime) {
//Slog.i(TAG, "commitFinishDrawingLocked: " + mSurface);
- if (!mCommitDrawPending) {
+ if (mDrawState != COMMIT_DRAW_PENDING) {
return false;
}
- mCommitDrawPending = false;
- mWin.mReadyToShow = true;
+ mDrawState = READY_TO_SHOW;
final boolean starting = mWin.mAttrs.type == TYPE_APPLICATION_STARTING;
final AppWindowToken atoken = mWin.mAppToken;
if (atoken == null || atoken.allDrawn || starting) {
@@ -387,11 +398,9 @@
if (mSurface == null) {
mReportDestroySurface = false;
mSurfacePendingDestroy = false;
- if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG,
+ if (DEBUG_ORIENTATION) Slog.i(TAG,
"createSurface " + this + ": DRAW NOW PENDING");
- mDrawPending = true;
- mCommitDrawPending = false;
- mWin.mReadyToShow = false;
+ mDrawState = DRAW_PENDING;
if (mWin.mAppToken != null) {
mWin.mAppToken.allDrawn = false;
}
@@ -443,6 +452,7 @@
mSession.mSurfaceSession, mSession.mPid,
attrs.getTitle().toString(),
0, w, h, format, flags);
+ mWin.mHasSurface = true;
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
" CREATE SURFACE "
+ mSurface + " IN SESSION "
@@ -452,11 +462,15 @@
+ Integer.toHexString(flags)
+ " / " + this);
} catch (Surface.OutOfResourcesException e) {
+ mWin.mHasSurface = false;
Slog.w(TAG, "OutOfResourcesException creating surface");
mService.reclaimSomeSurfaceMemoryLocked(this, "create", true);
+ mDrawState = NO_SURFACE;
return null;
} catch (Exception e) {
+ mWin.mHasSurface = false;
Slog.e(TAG, "Exception creating surface", e);
+ mDrawState = NO_SURFACE;
return null;
}
@@ -489,7 +503,7 @@
Slog.w(TAG, "Error creating surface in " + w, e);
mService.reclaimSomeSurfaceMemoryLocked(this, "create-init", true);
}
- mWin.mLastHidden = true;
+ mLastHidden = true;
} finally {
Surface.closeTransaction();
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
@@ -506,10 +520,8 @@
mWin.mAppToken.startingDisplayed = false;
}
+ mDrawState = NO_SURFACE;
if (mSurface != null) {
- mDrawPending = false;
- mCommitDrawPending = false;
- mWin.mReadyToShow = false;
int i = mWin.mChildWindows.size();
while (i > 0) {
@@ -573,6 +585,7 @@
mSurfaceShown = false;
mSurface = null;
+ mWin.mHasSurface =false;
}
}
@@ -603,9 +616,10 @@
Transformation attachedTransformation =
(mAttachedWindow != null && mAttachedWindow.mWinAnimator.mHasLocalTransformation)
? mAttachedWindow.mWinAnimator.mTransformation : null;
- Transformation appTransformation =
- (mWin.mAppToken != null && mWin.mAppToken.hasTransformation)
- ? mWin.mAppToken.transformation : null;
+ final AppWindowAnimator appAnimator =
+ mWin.mAppToken == null ? null : mWin.mAppToken.mAppAnimator;
+ Transformation appTransformation = (appAnimator != null && appAnimator.hasTransformation)
+ ? appAnimator.transformation : null;
// Wallpapers are animated based on the "real" window they
// are currently targeting.
@@ -619,11 +633,13 @@
Slog.v(TAG, "WP target attached xform: " + attachedTransformation);
}
}
- if (mService.mWallpaperTarget.mAppToken != null &&
- mService.mWallpaperTarget.mAppToken.hasTransformation &&
- mService.mWallpaperTarget.mAppToken.animation != null &&
- !mService.mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) {
- appTransformation = mService.mWallpaperTarget.mAppToken.transformation;
+ final AppWindowAnimator wpAppAnimator = mService.mWallpaperTarget.mAppToken == null
+ ? null : mService.mWallpaperTarget.mAppToken.mAppAnimator;
+ if (wpAppAnimator != null &&
+ wpAppAnimator.hasTransformation &&
+ wpAppAnimator.animation != null &&
+ !wpAppAnimator.animation.getDetachWallpaper()) {
+ appTransformation = wpAppAnimator.transformation;
if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) {
Slog.v(TAG, "WP target app xform: " + appTransformation);
}
@@ -746,7 +762,7 @@
final WindowState w = mWin;
if (mSurface == null) {
if (w.mOrientationChanging) {
- if (WindowManagerService.DEBUG_ORIENTATION) {
+ if (DEBUG_ORIENTATION) {
Slog.v(TAG, "Orientation change skips hidden " + w);
}
w.mOrientationChanging = false;
@@ -819,9 +835,9 @@
}
if (w.mAttachedHidden || !w.isReadyForDisplay()) {
- if (!w.mLastHidden) {
+ if (!mLastHidden) {
//dump();
- w.mLastHidden = true;
+ mLastHidden = true;
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"HIDE (performLayout)", null);
if (mSurface != null) {
@@ -841,7 +857,7 @@
// new orientation.
if (w.mOrientationChanging) {
w.mOrientationChanging = false;
- if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation change skips hidden " + w);
}
} else if (mLastLayer != mAnimLayer
@@ -852,7 +868,7 @@
|| mLastDtDy != mDtDy
|| w.mLastHScale != w.mHScale
|| w.mLastVScale != w.mVScale
- || w.mLastHidden) {
+ || mLastHidden) {
displayed = true;
mLastAlpha = mShownAlpha;
mLastLayer = mAnimLayer;
@@ -877,6 +893,21 @@
mSurface.setMatrix(
mDsDx*w.mHScale, mDtDx*w.mVScale,
mDsDy*w.mHScale, mDtDy*w.mVScale);
+
+ if (mLastHidden && mDrawState == HAS_DRAWN) {
+ if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
+ "SHOW (performLayout)", null);
+ if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
+ + " during relayout");
+ if (showSurfaceRobustlyLocked()) {
+ mLastHidden = false;
+ } else {
+ w.mOrientationChanging = false;
+ }
+ }
+ if (mSurface != null) {
+ w.mToken.hasVisible = true;
+ }
} catch (RuntimeException e) {
Slog.w(TAG, "Error updating surface in " + w, e);
if (!recoveringMemory) {
@@ -884,23 +915,6 @@
}
}
}
-
- if (w.mLastHidden && w.isDrawnLw()
- && !w.mReadyToShow) {
- if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
- "SHOW (performLayout)", null);
- if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
- + " during relayout");
- if (showSurfaceRobustlyLocked()) {
- w.mHasDrawn = true;
- w.mLastHidden = false;
- } else {
- w.mOrientationChanging = false;
- }
- }
- if (mSurface != null) {
- w.mToken.hasVisible = true;
- }
} else {
displayed = true;
}
@@ -908,19 +922,46 @@
if (displayed) {
if (w.mOrientationChanging) {
if (!w.isDrawnLw()) {
- mService.mInnerFields.mOrientationChangeComplete = false;
- if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+ mAnimator.mBulkUpdateParams |= CLEAR_ORIENTATION_CHANGE_COMPLETE;
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation continue waiting for draw in " + w);
} else {
w.mOrientationChanging = false;
- if (WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
- "Orientation change complete in " + w);
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change complete in " + w);
}
}
w.mToken.hasVisible = true;
}
}
+ void setTransparentRegionHint(final Region region) {
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ ">>> OPEN TRANSACTION setTransparentRegion");
+ Surface.openTransaction();
+ try {
+ if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
+ "transparentRegionHint=" + region, null);
+ mSurface.setTransparentRegionHint(region);
+ } finally {
+ Surface.closeTransaction();
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+ "<<< CLOSE TRANSACTION setTransparentRegion");
+ }
+ }
+
+ void setWallpaperOffset(int left, int top) {
+ Surface.openTransaction();
+ try {
+ mSurfaceX = left;
+ mSurfaceY = top;
+ mSurface.setPosition(left, top);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error positioning surface of " + mWin
+ + " pos=(" + left + "," + top + ")", e);
+ }
+ Surface.closeTransaction();
+ }
+
// This must be called while inside a transaction.
boolean performShowLocked() {
if (DEBUG_VISIBILITY) {
@@ -930,12 +971,12 @@
e.fillInStackTrace();
}
Slog.v(TAG, "performShow on " + this
- + ": readyToShow=" + mWin.mReadyToShow + " readyForDisplay="
+ + ": mDrawState=" + mDrawState + " readyForDisplay="
+ mWin.isReadyForDisplay()
+ " starting=" + (mWin.mAttrs.type == TYPE_APPLICATION_STARTING), e);
}
- if (mWin.mReadyToShow && mWin.isReadyForDisplay()) {
- if (SHOW_TRANSACTIONS || WindowManagerService.DEBUG_ORIENTATION)
+ if (mDrawState == READY_TO_SHOW && mWin.isReadyForDisplay()) {
+ if (SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
WindowManagerService.logSurface(mWin, "SHOW (performShowLocked)", null);
if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
+ " during animation: policyVis=" + mWin.mPolicyVisibility
@@ -946,7 +987,7 @@
+ (mWin.mAppToken != null ? mWin.mAppToken.hidden : false)
+ " animating=" + mAnimating
+ " tok animating="
- + (mWin.mAppToken != null ? mWin.mAppToken.animating : false));
+ + (mWin.mAppToken != null ? mWin.mAppToken.mAppAnimator.animating : false));
if (!showSurfaceRobustlyLocked()) {
return false;
}
@@ -956,9 +997,8 @@
applyEnterAnimationLocked();
mLastAlpha = -1;
- mWin.mHasDrawn = true;
- mWin.mLastHidden = false;
- mWin.mReadyToShow = false;
+ mLastHidden = false;
+ mDrawState = HAS_DRAWN;
int i = mWin.mChildWindows.size();
while (i > 0) {
@@ -1051,6 +1091,7 @@
applyAnimationLocked(transit, true);
}
+ // TODO(cmautner): Move back to WindowState?
/**
* Choose the correct animation and set it to the passed WindowState.
* @param transit If WindowManagerPolicy.TRANSIT_PREVIEW_DONE and the app window has been drawn
@@ -1138,6 +1179,8 @@
if (mSurface != null) {
if (dumpAll) {
pw.print(prefix); pw.print("mSurface="); pw.println(mSurface);
+ pw.print(prefix); pw.print("mDrawState="); pw.print(mDrawState);
+ pw.print(" mLastHidden="); pw.println(mLastHidden);
}
pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
pw.print(" layer="); pw.print(mSurfaceLayer);
@@ -1169,4 +1212,12 @@
}
}
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer("WindowStateAnimator (");
+ sb.append(mWin.mLastTitle + "): ");
+ sb.append("mSurface " + mSurface);
+ sb.append(", mAnimation " + mAnimation);
+ return sb.toString();
+ }
}
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index dd6c426..16ddd91 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -35,6 +35,7 @@
#include <gui/ISensorServer.h>
#include <gui/ISensorEventConnection.h>
+#include <gui/SensorEventQueue.h>
#include <hardware/sensors.h>
@@ -587,10 +588,9 @@
count = numEvents;
}
- if (count == 0)
- return 0;
-
- ssize_t size = mChannel->write(scratch, count*sizeof(sensors_event_t));
+ // NOTE: ASensorEvent and sensors_event_t are the same type
+ ssize_t size = SensorEventQueue::write(mChannel,
+ reinterpret_cast<ASensorEvent const*>(scratch), count);
if (size == -EAGAIN) {
// the destination doesn't accept events anymore, it's probably
// full. For now, we just drop the events on the floor.
@@ -598,9 +598,6 @@
return size;
}
- //ALOGE_IF(size<0, "dropping %d events on the floor (%s)",
- // count, strerror(-size));
-
return size < 0 ? status_t(size) : status_t(NO_ERROR);
}
diff --git a/telephony/java/com/android/internal/telephony/ApnContext.java b/telephony/java/com/android/internal/telephony/ApnContext.java
index 8aeee87..80d5044 100644
--- a/telephony/java/com/android/internal/telephony/ApnContext.java
+++ b/telephony/java/com/android/internal/telephony/ApnContext.java
@@ -17,6 +17,9 @@
package com.android.internal.telephony;
import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -207,4 +210,19 @@
protected void log(String s) {
Log.d(LOG_TAG, "[ApnContext] " + s);
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("ApnContext:");
+ pw.println(" mApnType=" + mApnType);
+ pw.println(" mState=" + mState);
+ pw.println(" mWaitingApns=" + mWaitingApns);
+ pw.println(" mWaitingApnsPermanentFailureCountDown=" +
+ mWaitingApnsPermanentFailureCountDown);
+ pw.println(" mApnSetting=" + mApnSetting);
+ pw.println(" mDataConnection=" + mDataConnection);
+ pw.println(" mDataConnectionAc=" + mDataConnectionAc);
+ pw.println(" mReason=" + mReason);
+ pw.println(" mDataEnabled=" + mDataEnabled);
+ pw.println(" mDependencyMet=" + mDependencyMet);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/CallTracker.java b/telephony/java/com/android/internal/telephony/CallTracker.java
index 958481c..62caf01 100644
--- a/telephony/java/com/android/internal/telephony/CallTracker.java
+++ b/telephony/java/com/android/internal/telephony/CallTracker.java
@@ -25,6 +25,9 @@
import com.android.internal.telephony.CommandException;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* {@hide}
@@ -170,4 +173,10 @@
protected abstract void log(String msg);
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CallTracker:");
+ pw.println(" pendingOperations=" + pendingOperations);
+ pw.println(" needsPoll=" + needsPoll);
+ pw.println(" lastRelevantPoll=" + lastRelevantPoll);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 238afbe..486a924 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -32,6 +32,8 @@
import android.os.SystemProperties;
import android.text.TextUtils;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -1185,4 +1187,27 @@
sendMessage(obtainMessage(EVENT_DISCONNECT_ALL,
new DisconnectParams(reason, onCompletedMsg)));
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("DataConnection name=" + getName() + ":");
+ pw.println(" mApnList=" + mApnList);
+ pw.println(" mDataConnectionTracker=" + mDataConnectionTracker);
+ pw.println(" mApn=" + mApn);
+ pw.println(" mTag=" + mTag);
+ pw.println(" phone=" + phone);
+ pw.println(" mRilVersion=" + mRilVersion);
+ pw.println(" cid=" + cid);
+ pw.println(" mLinkProperties=" + mLinkProperties);
+ pw.println(" mCapabilities=" + mCapabilities);
+ pw.println(" createTime=" + createTime);
+ pw.println(" lastFailTime=" + lastFailTime);
+ pw.println(" lastFailCause=" + lastFailCause);
+ pw.println(" mRetryOverride=" + mRetryOverride);
+ pw.println(" mRefCount=" + mRefCount);
+ pw.println(" userData=" + userData);
+ pw.println(" total messages=" + getProcessedMessagesCount());
+ for (int i=0; i < getProcessedMessagesSize(); i++) {
+ pw.printf(" msg[%d]=%s\n", i, getProcessedMessageInfo(i));
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index fa90f1c..55f2ca3 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -49,8 +49,12 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Map.Entry;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@@ -1193,4 +1197,80 @@
dc.resetRetryCount();
}
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("DataConnectionTracker:");
+ pw.println(" mInternalDataEnabled=" + mInternalDataEnabled);
+ pw.println(" mUserDataEnabled=" + mUserDataEnabled);
+ pw.println(" sPolicyDataEnabed=" + sPolicyDataEnabled);
+ pw.println(" dataEnabled:");
+ for(int i=0; i < dataEnabled.length; i++) {
+ pw.printf(" dataEnabled[%d]=%b\n", i, dataEnabled[i]);
+ }
+ pw.flush();
+ pw.println(" enabledCount=" + enabledCount);
+ pw.println(" mRequestedApnType=" + mRequestedApnType);
+ pw.println(" mPhone=" + mPhone.getPhoneName());
+ pw.println(" mActivity=" + mActivity);
+ pw.println(" mState=" + mState);
+ pw.println(" mTxPkts=" + mTxPkts);
+ pw.println(" mRxPkts=" + mRxPkts);
+ pw.println(" mNetStatPollPeriod=" + mNetStatPollPeriod);
+ pw.println(" mNetStatPollEnabled=" + mNetStatPollEnabled);
+ pw.println(" mDataStallTxRxSum=" + mDataStallTxRxSum);
+ pw.println(" mDataStallAlarmTag=" + mDataStallAlarmTag);
+ pw.println(" mSentSinceLastRecv=" + mSentSinceLastRecv);
+ pw.println(" mNoRecvPollCount=" + mNoRecvPollCount);
+ pw.println(" mIsWifiConnected=" + mIsWifiConnected);
+ pw.println(" mReconnectIntent=" + mReconnectIntent);
+ pw.println(" mCidActive=" + mCidActive);
+ pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation);
+ pw.println(" mIsScreenOn=" + mIsScreenOn);
+ pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
+ pw.flush();
+ pw.println(" ***************************************");
+ Set<Entry<Integer, DataConnection> > mDcSet = mDataConnections.entrySet();
+ pw.println(" mDataConnections: count=" + mDcSet.size());
+ for (Entry<Integer, DataConnection> entry : mDcSet) {
+ pw.printf(" *** mDataConnection[%d] \n", entry.getKey());
+ entry.getValue().dump(fd, pw, args);
+ }
+ pw.println(" ***************************************");
+ pw.flush();
+ Set<Entry<String, Integer>> mApnToDcIdSet = mApnToDataConnectionId.entrySet();
+ pw.println(" mApnToDataConnectonId size=" + mApnToDcIdSet.size());
+ for (Entry<String, Integer> entry : mApnToDcIdSet) {
+ pw.printf(" mApnToDataConnectonId[%s]=%d\n", entry.getKey(), entry.getValue());
+ }
+ pw.println(" ***************************************");
+ pw.flush();
+ if (mApnContexts != null) {
+ Set<Entry<String, ApnContext>> mApnContextsSet = mApnContexts.entrySet();
+ pw.println(" mApnContexts size=" + mApnContextsSet.size());
+ for (Entry<String, ApnContext> entry : mApnContextsSet) {
+ pw.printf(" *** mApnContexts[%s]:\n", entry.getKey());
+ entry.getValue().dump(fd, pw, args);
+ }
+ pw.println(" ***************************************");
+ } else {
+ pw.println(" mApnContexts=null");
+ }
+ pw.flush();
+ pw.println(" mActiveApn=" + mActiveApn);
+ if (mAllApns != null) {
+ pw.println(" mAllApns size=" + mAllApns.size());
+ for (int i=0; i < mAllApns.size(); i++) {
+ pw.printf(" mAllApns[%d]: %s\n", i, mAllApns.get(i));
+ }
+ pw.flush();
+ } else {
+ pw.println(" mAllApns=null");
+ }
+ pw.println(" mPreferredApn=" + mPreferredApn);
+ pw.println(" mIsPsRestricted=" + mIsPsRestricted);
+ pw.println(" mIsDisposed=" + mIsDisposed);
+ pw.println(" mIntentReceiver=" + mIntentReceiver);
+ pw.println(" mDataRoamingSettingObserver=" + mDataRoamingSettingObserver);
+ pw.flush();
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/DebugService.java b/telephony/java/com/android/internal/telephony/DebugService.java
new file mode 100644
index 0000000..29fea6e
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/DebugService.java
@@ -0,0 +1,108 @@
+/*
+ * 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.internal.telephony;
+
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A debug service that will dump telephony's state
+ *
+ * Currently this "Service" has a proxy in the phone app
+ * com.android.phone.TelephonyDebugService which actually
+ * invokes the dump method.
+ */
+public class DebugService {
+ private static String TAG = "DebugService";
+
+ /** Constructor */
+ public DebugService() {
+ log("DebugService:");
+ }
+
+ /**
+ * Dump the state of various objects, add calls to other objects as desired.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ log("dump: +");
+ PhoneProxy phoneProxy = null;
+ PhoneBase phoneBase = null;
+
+ try {
+ phoneProxy = (PhoneProxy) PhoneFactory.getDefaultPhone();
+ } catch (Exception e) {
+ pw.println("Telephony DebugService: Could not getDefaultPhone e=" + e);
+ return;
+ }
+ try {
+ phoneBase = (PhoneBase)phoneProxy.getActivePhone();
+ } catch (Exception e) {
+ pw.println("Telephony DebugService: Could not PhoneBase e=" + e);
+ return;
+ }
+
+ /**
+ * Surround each of the sub dump's with try/catch so even
+ * if one fails we'll be able to dump the next ones.
+ */
+ pw.println();
+ pw.println("++++++++++++++++++++++++++++++++");
+ pw.flush();
+ try {
+ phoneBase.dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
+ pw.println("++++++++++++++++++++++++++++++++");
+ try {
+ phoneBase.mDataConnectionTracker.dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
+ pw.println("++++++++++++++++++++++++++++++++");
+ try {
+ phoneBase.getServiceStateTracker().dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
+ pw.println("++++++++++++++++++++++++++++++++");
+ try {
+ phoneBase.getCallTracker().dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
+ pw.println("++++++++++++++++++++++++++++++++");
+ try {
+ ((RIL)phoneBase.mCM).dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
+ pw.println("++++++++++++++++++++++++++++++++");
+ log("dump: -");
+ }
+
+ private static void log(String s) {
+ Log.d(TAG, "DebugService " + s);
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 3aa53eef..1b4cb15 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -42,6 +42,8 @@
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.gsm.SIMRecords;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
@@ -1144,4 +1146,43 @@
public UsimServiceTable getUsimServiceTable() {
return mIccRecords.getUsimServiceTable();
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("PhoneBase:");
+ pw.println(" mCM=" + mCM);
+ pw.println(" mDnsCheckDisabled=" + mDnsCheckDisabled);
+ pw.println(" mDataConnectionTracker=" + mDataConnectionTracker);
+ pw.println(" mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing);
+ pw.println(" mCallRingContinueToken=" + mCallRingContinueToken);
+ pw.println(" mCallRingDelay=" + mCallRingDelay);
+ pw.println(" mIsTheCurrentActivePhone=" + mIsTheCurrentActivePhone);
+ pw.println(" mIsVoiceCapable=" + mIsVoiceCapable);
+ pw.println(" mIccRecords=" + mIccRecords);
+ pw.println(" mIccCard=" + mIccCard.get());
+ pw.println(" mSmsStorageMonitor=" + mSmsStorageMonitor);
+ pw.println(" mSmsUsageMonitor=" + mSmsUsageMonitor);
+ pw.println(" mSMS=" + mSMS);
+ pw.flush();
+ pw.println(" mLooper=" + mLooper);
+ pw.println(" mContext=" + mContext);
+ pw.println(" mNotifier=" + mNotifier);
+ pw.println(" mSimulatedRadioControl=" + mSimulatedRadioControl);
+ pw.println(" mUnitTestMode=" + mUnitTestMode);
+ pw.println(" isDnsCheckDisabled()=" + isDnsCheckDisabled());
+ pw.println(" getUnitTestMode()=" + getUnitTestMode());
+ pw.println(" getState()=" + getState());
+ pw.println(" getIccSerialNumber()=" + getIccSerialNumber());
+ pw.println(" getIccRecordsLoaded()=" + getIccRecordsLoaded());
+ pw.println(" getMessageWaitingIndicator()=" + getMessageWaitingIndicator());
+ pw.println(" getCallForwardingIndicator()=" + getCallForwardingIndicator());
+ pw.println(" isInEmergencyCall()=" + isInEmergencyCall());
+ pw.flush();
+ pw.println(" isInEcm()=" + isInEcm());
+ pw.println(" getPhoneName()=" + getPhoneName());
+ pw.println(" getPhoneType()=" + getPhoneType());
+ pw.println(" getVoiceMessageCount()=" + getVoiceMessageCount());
+ pw.println(" getActiveApnTypes()=" + getActiveApnTypes());
+ pw.println(" isDataConnectivityPossible()=" + isDataConnectivityPossible());
+ pw.println(" needsOtaServiceProvisioning=" + needsOtaServiceProvisioning());
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index cf96ab2..b14f6c8 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -56,8 +56,10 @@
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -3833,4 +3835,27 @@
if (RILJ_LOGD) riljLog("testingEmergencyCall");
mTestingEmergencyCall.set(true);
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("RIL:");
+ pw.println(" mSocket=" + mSocket);
+ pw.println(" mSenderThread=" + mSenderThread);
+ pw.println(" mSender=" + mSender);
+ pw.println(" mReceiverThread=" + mReceiverThread);
+ pw.println(" mReceiver=" + mReceiver);
+ pw.println(" mWakeLock=" + mWakeLock);
+ pw.println(" mWakeLockTimeout=" + mWakeLockTimeout);
+ synchronized (mRequestsList) {
+ pw.println(" mRequestMessagesPending=" + mRequestMessagesPending);
+ pw.println(" mRequestMessagesWaiting=" + mRequestMessagesWaiting);
+ int count = mRequestsList.size();
+ pw.println(" mRequestList count=" + count);
+ for (int i = 0; i < count; i++) {
+ RILRequest rr = mRequestsList.get(i);
+ pw.println(" [" + rr.mSerial + "] " + requestToString(rr.mRequest));
+ }
+ }
+ pw.println(" mLastNITZTimeInfo=" + mLastNITZTimeInfo);
+ pw.println(" mTestingEmergencyCall=" + mTestingEmergencyCall.get());
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index fd39e04..75eb226 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -24,6 +24,9 @@
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* {@hide}
*/
@@ -459,4 +462,19 @@
// This will effectively cancel the rest of the poll requests.
pollingContext = new int[1];
}
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("ServiceStateTracker:");
+ pw.println(" ss=" + ss);
+ pw.println(" newSS=" + newSS);
+ pw.println(" mSignalStrength=" + mSignalStrength);
+ pw.println(" mRestrictedState=" + mRestrictedState);
+ pw.println(" pollingContext=" + pollingContext);
+ pw.println(" mDesiredPowerState=" + mDesiredPowerState);
+ pw.println(" mRilRadioTechnology=" + mRilRadioTechnology);
+ pw.println(" mNewRilRadioTechnology=" + mNewRilRadioTechnology);
+ pw.println(" dontPollSignalStrength=" + dontPollSignalStrength);
+ pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
+ pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index 110d8bf..d99a625 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -39,6 +39,9 @@
import com.android.internal.telephony.ims.IsimRecords;
import com.android.internal.telephony.uicc.UiccController;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
public class CDMALTEPhone extends CDMAPhone {
static final String LOG_TAG = "CDMA";
@@ -259,4 +262,11 @@
protected void log(String s) {
Log.d(LOG_TAG, "[CDMALTEPhone] " + s);
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CDMALTEPhone extends:");
+ super.dump(fd, pw, args);
+ pw.println(" m3gppSMS=" + m3gppSMS);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index bb00d4b..ed0081b 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -66,6 +66,8 @@
import com.android.internal.telephony.cat.CatService;
import com.android.internal.telephony.uicc.UiccController;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
@@ -81,6 +83,7 @@
public class CDMAPhone extends PhoneBase {
static final String LOG_TAG = "CDMA";
private static final boolean DBG = true;
+ private static final boolean VDBG = false; /* STOP SHIP if true */
// Default Emergency Callback Mode exit timer
private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
@@ -1471,4 +1474,32 @@
if (DBG)
Log.d(LOG_TAG, "[CDMAPhone] " + s);
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CDMAPhone extends:");
+ super.dump(fd, pw, args);
+ pw.println(" mVmNumber=" + mVmNumber);
+ pw.println(" mCT=" + mCT);
+ pw.println(" mSST=" + mSST);
+ pw.println(" mCdmaSSM=" + mCdmaSSM);
+ pw.println(" mPendingMmis=" + mPendingMmis);
+ pw.println(" mRuimPhoneBookInterfaceManager=" + mRuimPhoneBookInterfaceManager);
+ pw.println(" mRuimSmsInterfaceManager=" + mRuimSmsInterfaceManager);
+ pw.println(" mCdmaSubscriptionSource=" + mCdmaSubscriptionSource);
+ pw.println(" mSubInfo=" + mSubInfo);
+ pw.println(" mEriManager=" + mEriManager);
+ pw.println(" mWakeLock=" + mWakeLock);
+ pw.println(" mIsPhoneInEcmState=" + mIsPhoneInEcmState);
+ if (VDBG) pw.println(" mImei=" + mImei);
+ if (VDBG) pw.println(" mImeiSv=" + mImeiSv);
+ if (VDBG) pw.println(" mEsn=" + mEsn);
+ if (VDBG) pw.println(" mMeid=" + mMeid);
+ pw.println(" mCarrierOtaSpNumSchema=" + mCarrierOtaSpNumSchema);
+ pw.println(" getCdmaEriIconIndex()=" + getCdmaEriIconIndex());
+ pw.println(" getCdmaEriIconMode()=" + getCdmaEriIconMode());
+ pw.println(" getCdmaEriText()=" + getCdmaEriText());
+ pw.println(" isMinInfoReady()=" + isMinInfoReady());
+ pw.println(" isCspPlmnEnabled()=" + isCspPlmnEnabled());
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index f918dce..af92b08 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -34,6 +34,8 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TelephonyProperties;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -1129,4 +1131,32 @@
Log.d(LOG_TAG, "[CdmaCallTracker] " + msg);
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("GsmCallTracker extends:");
+ super.dump(fd, pw, args);
+ pw.println("droppedDuringPoll: length=" + connections.length);
+ for(int i=0; i < connections.length; i++) {
+ pw.printf(" connections[%d]=%s\n", i, connections[i]);
+ }
+ pw.println(" voiceCallEndedRegistrants=" + voiceCallEndedRegistrants);
+ pw.println(" voiceCallStartedRegistrants=" + voiceCallStartedRegistrants);
+ pw.println(" callWaitingRegistrants=" + callWaitingRegistrants);
+ pw.println("droppedDuringPoll: size=" + droppedDuringPoll.size());
+ for(int i = 0; i < droppedDuringPoll.size(); i++) {
+ pw.printf( " droppedDuringPoll[%d]=%s\n", i, droppedDuringPoll.get(i));
+ }
+ pw.println(" ringingCall=" + ringingCall);
+ pw.println(" foregroundCall=" + foregroundCall);
+ pw.println(" backgroundCall=" + backgroundCall);
+ pw.println(" pendingMO=" + pendingMO);
+ pw.println(" hangupPendingMO=" + hangupPendingMO);
+ pw.println(" pendingCallInEcm=" + pendingCallInEcm);
+ pw.println(" mIsInEmergencyCall=" + mIsInEmergencyCall);
+ pw.println(" phone=" + phone);
+ pw.println(" desiredMute=" + desiredMute);
+ pw.println(" pendingCallClirMode=" + pendingCallClirMode);
+ pw.println(" state=" + state);
+ pw.println(" mIsEcmTimerCanceled=" + mIsEcmTimerCanceled);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
index 64d018e..4ef05ea 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
@@ -25,6 +25,9 @@
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.RetryManager;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* {@hide}
*/
@@ -114,4 +117,10 @@
protected void log(String s) {
Log.d(LOG_TAG, "[" + getName() + "] " + s);
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CdmaDataConnection extends:");
+ super.dump(fd, pw, args);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 5f1a014..7e5e707 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -46,6 +46,8 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.telephony.RILConstants;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
/**
@@ -1017,4 +1019,18 @@
protected void loge(String s) {
Log.e(LOG_TAG, "[CdmaDCT] " + s);
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CdmaDataConnectionTracker extends:");
+ super.dump(fd, pw, args);
+ pw.println(" mCdmaPhone=" + mCdmaPhone);
+ pw.println(" mCdmaSSM=" + mCdmaSSM);
+ pw.println(" mPendingDataConnection=" + mPendingDataConnection);
+ pw.println(" mPendingRestartRadio=" + mPendingRestartRadio);
+ pw.println(" mSupportedApnTypes=" + mSupportedApnTypes);
+ pw.println(" mDefaultApnTypes=" + mDefaultApnTypes);
+ pw.println(" mDunApnTypes=" + mDunApnTypes);
+ pw.println(" mDefaultApnId=" + mDefaultApnId);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index 09008cd..98a106a0 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -36,6 +36,9 @@
import com.android.internal.telephony.gsm.GsmDataConnectionTracker;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker {
CDMALTEPhone mCdmaLtePhone;
@@ -519,4 +522,12 @@
protected void loge(String s) {
Log.e(LOG_TAG, "[CdmaLteSST] " + s);
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CdmaLteServiceStateTracker extends:");
+ super.dump(fd, pw, args);
+ pw.println(" mCdmaLtePhone=" + mCdmaLtePhone);
+ pw.println(" mLteSS=" + mLteSS);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 9ec56fc..9f27696 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -54,6 +54,8 @@
import android.util.Log;
import android.util.TimeUtils;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
@@ -1665,4 +1667,43 @@
protected void loge(String s) {
Log.e(LOG_TAG, "[CdmaSST] " + s);
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("CdmaServiceStateTracker extends:");
+ super.dump(fd, pw, args);
+ pw.println(" phone=" + phone);
+ pw.println(" cellLoc=" + cellLoc);
+ pw.println(" newCellLoc=" + newCellLoc);
+ pw.println(" mCurrentOtaspMode=" + mCurrentOtaspMode);
+ pw.println(" mCdmaRoaming=" + mCdmaRoaming);
+ pw.println(" mRoamingIndicator=" + mRoamingIndicator);
+ pw.println(" mIsInPrl=" + mIsInPrl);
+ pw.println(" mDefaultRoamingIndicator=" + mDefaultRoamingIndicator);
+ pw.println(" mDataConnectionState=" + mDataConnectionState);
+ pw.println(" mNewDataConnectionState=" + mNewDataConnectionState);
+ pw.println(" mRegistrationState=" + mRegistrationState);
+ pw.println(" mNeedFixZone=" + mNeedFixZone);
+ pw.println(" mZoneOffset=" + mZoneOffset);
+ pw.println(" mZoneDst=" + mZoneDst);
+ pw.println(" mZoneTime=" + mZoneTime);
+ pw.println(" mGotCountryCode=" + mGotCountryCode);
+ pw.println(" mSavedTimeZone=" + mSavedTimeZone);
+ pw.println(" mSavedTime=" + mSavedTime);
+ pw.println(" mSavedAtTime=" + mSavedAtTime);
+ pw.println(" mNeedToRegForRuimLoaded=" + mNeedToRegForRuimLoaded);
+ pw.println(" mWakeLock=" + mWakeLock);
+ pw.println(" mCurPlmn=" + mCurPlmn);
+ pw.println(" mMdn=" + mMdn);
+ pw.println(" mHomeSystemId=" + mHomeSystemId);
+ pw.println(" mHomeNetworkId=" + mHomeNetworkId);
+ pw.println(" mMin=" + mMin);
+ pw.println(" mPrlVersion=" + mPrlVersion);
+ pw.println(" mIsMinInfoReady=" + mIsMinInfoReady);
+ pw.println(" isEriTextLoaded=" + isEriTextLoaded);
+ pw.println(" isSubscriptionFromRuim=" + isSubscriptionFromRuim);
+ pw.println(" mCdmaSSM=" + mCdmaSSM);
+ pw.println(" mRegistrationDeniedReason=" + mRegistrationDeniedReason);
+ pw.println(" currentCarrier=" + currentCarrier);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index be13c35..6e9cd51 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -74,7 +74,9 @@
import com.android.internal.telephony.IccVmNotSupportedException;
import com.android.internal.telephony.ServiceStateTracker;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
@@ -90,6 +92,7 @@
// log. (Use "adb logcat -b radio" to see them.)
static final String LOG_TAG = "GSM";
private static final boolean LOCAL_DEBUG = true;
+ private static final boolean VDBG = false; /* STOP SHIP if true */
// Key used to read/write current ciphering state
public static final String CIPHERING_KEY = "ciphering_key";
@@ -1487,4 +1490,18 @@
mIccRecords.unregisterForRecordsLoaded(this);
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("GSMPhone extends:");
+ super.dump(fd, pw, args);
+ pw.println(" mCT=" + mCT);
+ pw.println(" mSST=" + mSST);
+ pw.println(" mPendingMMIs=" + mPendingMMIs);
+ pw.println(" mSimPhoneBookIntManager=" + mSimPhoneBookIntManager);
+ pw.println(" mSimSmsIntManager=" + mSimSmsIntManager);
+ pw.println(" mSubInfo=" + mSubInfo);
+ if (VDBG) pw.println(" mImei=" + mImei);
+ if (VDBG) pw.println(" mImeiSv=" + mImeiSv);
+ pw.println(" mVmNumber=" + mVmNumber);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
index b4e0775..e86d853 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
@@ -43,6 +43,8 @@
import com.android.internal.telephony.gsm.GsmCall;
import com.android.internal.telephony.gsm.GsmConnection;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
import java.util.ArrayList;
@@ -922,4 +924,28 @@
protected void log(String msg) {
Log.d(LOG_TAG, "[GsmCallTracker] " + msg);
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("GsmCallTracker extends:");
+ super.dump(fd, pw, args);
+ pw.println("connections: length=" + connections.length);
+ for(int i=0; i < connections.length; i++) {
+ pw.printf(" connections[%d]=%s\n", i, connections[i]);
+ }
+ pw.println(" voiceCallEndedRegistrants=" + voiceCallEndedRegistrants);
+ pw.println(" voiceCallStartedRegistrants=" + voiceCallStartedRegistrants);
+ pw.println(" droppedDuringPoll: size=" + droppedDuringPoll.size());
+ for(int i = 0; i < droppedDuringPoll.size(); i++) {
+ pw.printf( " droppedDuringPoll[%d]=%s\n", i, droppedDuringPoll.get(i));
+ }
+ pw.println(" ringingCall=" + ringingCall);
+ pw.println(" foregroundCall=" + foregroundCall);
+ pw.println(" backgroundCall=" + backgroundCall);
+ pw.println(" pendingMO=" + pendingMO);
+ pw.println(" hangupPendingMO=" + hangupPendingMO);
+ pw.println(" phone=" + phone);
+ pw.println(" desiredMute=" + desiredMute);
+ pw.println(" state=" + state);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
index 4956ef4..fec0158 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
@@ -28,6 +28,9 @@
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.RetryManager;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* {@hide}
*/
@@ -153,4 +156,11 @@
return Patterns.IP_ADDRESS.matcher(address).matches();
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("GsmDataConnection extends:");
+ super.dump(fd, pw, args);
+ pw.println(" mProfileId=" + mProfileId);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index d1873eb..40ee58c 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -65,6 +65,8 @@
import com.android.internal.telephony.RetryManager;
import com.android.internal.util.AsyncChannel;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -2562,4 +2564,16 @@
protected void loge(String s) {
Log.e(LOG_TAG, "[GsmDCT] " + s);
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("GsmDataConnectionTracker extends:");
+ super.dump(fd, pw, args);
+ pw.println(" RADIO_TESTS=" + RADIO_TESTS);
+ pw.println(" mReregisterOnReconnectFailure=" + mReregisterOnReconnectFailure);
+ pw.println(" mResolver=" + mResolver);
+ pw.println(" canSetPreferApn=" + canSetPreferApn);
+ pw.println(" mApnObserver=" + mApnObserver);
+ pw.println(" getOverallState=" + getOverallState());
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 080d90cd..662f1f6 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -59,6 +59,8 @@
import android.util.Log;
import android.util.TimeUtils;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -1681,4 +1683,40 @@
private static void sloge(String s) {
Log.e(LOG_TAG, "[GsmSST] " + s);
}
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("GsmServiceStateTracker extends:");
+ super.dump(fd, pw, args);
+ pw.println(" phone=" + phone);
+ pw.println(" cellLoc=" + cellLoc);
+ pw.println(" newCellLoc=" + newCellLoc);
+ pw.println(" mPreferredNetworkType=" + mPreferredNetworkType);
+ pw.println(" gprsState=" + gprsState);
+ pw.println(" newGPRSState=" + newGPRSState);
+ pw.println(" mMaxDataCalls=" + mMaxDataCalls);
+ pw.println(" mNewMaxDataCalls=" + mNewMaxDataCalls);
+ pw.println(" mReasonDataDenied=" + mReasonDataDenied);
+ pw.println(" mNewReasonDataDenied=" + mNewReasonDataDenied);
+ pw.println(" mGsmRoaming=" + mGsmRoaming);
+ pw.println(" mDataRoaming=" + mDataRoaming);
+ pw.println(" mEmergencyOnly=" + mEmergencyOnly);
+ pw.println(" mNeedFixZone=" + mNeedFixZone);
+ pw.println(" mZoneOffset=" + mZoneOffset);
+ pw.println(" mZoneDst=" + mZoneDst);
+ pw.println(" mZoneTime=" + mZoneTime);
+ pw.println(" mGotCountryCode=" + mGotCountryCode);
+ pw.println(" mNitzUpdatedTime=" + mNitzUpdatedTime);
+ pw.println(" mSavedTimeZone=" + mSavedTimeZone);
+ pw.println(" mSavedTime=" + mSavedTime);
+ pw.println(" mSavedAtTime=" + mSavedAtTime);
+ pw.println(" mNeedToRegForSimLoaded=" + mNeedToRegForSimLoaded);
+ pw.println(" mStartedGprsRegCheck=" + mStartedGprsRegCheck);
+ pw.println(" mReportedGprsNoReg=" + mReportedGprsNoReg);
+ pw.println(" mNotification=" + mNotification);
+ pw.println(" mWakeLock=" + mWakeLock);
+ pw.println(" curSpn=" + curSpn);
+ pw.println(" curPlmn=" + curPlmn);
+ pw.println(" curSpnRule=" + curSpnRule);
+ }
}
diff --git a/test-runner/src/android/test/AndroidTestRunner.java b/test-runner/src/android/test/AndroidTestRunner.java
index fc9832c..30876d0 100644
--- a/test-runner/src/android/test/AndroidTestRunner.java
+++ b/test-runner/src/android/test/AndroidTestRunner.java
@@ -28,6 +28,7 @@
import junit.framework.TestSuite;
import junit.runner.BaseTestRunner;
+import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
@@ -91,15 +92,35 @@
private TestCase buildSingleTestMethod(Class testClass, String testMethodName) {
try {
- TestCase testCase = (TestCase) testClass.newInstance();
+ Constructor c = testClass.getConstructor();
+ return newSingleTestMethod(testClass, testMethodName, c);
+ } catch (NoSuchMethodException e) {
+ }
+
+ try {
+ Constructor c = testClass.getConstructor(String.class);
+ return newSingleTestMethod(testClass, testMethodName, c, testMethodName);
+ } catch (NoSuchMethodException e) {
+ }
+
+ return null;
+ }
+
+ private TestCase newSingleTestMethod(Class testClass, String testMethodName,
+ Constructor constructor, Object... args) {
+ try {
+ TestCase testCase = (TestCase) constructor.newInstance(args);
testCase.setName(testMethodName);
return testCase;
} catch (IllegalAccessException e) {
runFailed("Could not access test class. Class: " + testClass.getName());
} catch (InstantiationException e) {
runFailed("Could not instantiate test class. Class: " + testClass.getName());
+ } catch (IllegalArgumentException e) {
+ runFailed("Illegal argument passed to constructor. Class: " + testClass.getName());
+ } catch (InvocationTargetException e) {
+ runFailed("Constructor thew an exception. Class: " + testClass.getName());
}
-
return null;
}
diff --git a/tests/BiDiTests/AndroidManifest.xml b/tests/BiDiTests/AndroidManifest.xml
index c60edd8..4aead814 100644
--- a/tests/BiDiTests/AndroidManifest.xml
+++ b/tests/BiDiTests/AndroidManifest.xml
@@ -19,7 +19,9 @@
android:versionCode="1"
android:versionName="1.0">
- <application android:label="BiDiTests" android:hardwareAccelerated="true">
+ <application android:label="BiDiTests"
+ android:hardwareAccelerated="true"
+ android:supportsRtl="true" >
<activity android:name=".BiDiTestActivity"
android:windowSoftInputMode="stateAlwaysHidden">
diff --git a/tests/BiDiTests/res/layout/textview_alignment_ltr.xml b/tests/BiDiTests/res/layout/textview_alignment_ltr.xml
new file mode 100644
index 0000000..0e1adba
--- /dev/null
+++ b/tests/BiDiTests/res/layout/textview_alignment_ltr.xml
@@ -0,0 +1,578 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/textview_alignment_ltr"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layoutDirection="ltr">
+
+ <TableLayout android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TableRow>
+ <TextView android:text="(unspecified)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity (default)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="left"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="left"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="right"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="right"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="start"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="start"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="end"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="end"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="center_horizontal"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="center_horizontal"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="textStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="textStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="textStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="textEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="textEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="textEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="viewStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="viewStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="viewStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="viewEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="viewEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="viewEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="gravity"
+ android:gravity="center_horizontal">
+
+ <TextView android:text="inherit gravity (default)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="center">
+
+ <TextView android:text="inherit gravity center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="textStart">
+
+ <TextView android:text="inherit textStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="textEnd">
+
+ <TextView android:text="inherit textEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="viewStart">
+
+ <TextView android:text="inherit viewStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="viewEnd">
+
+ <TextView android:text="inherit viewEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ </TableLayout>
+
+</FrameLayout>
diff --git a/tests/BiDiTests/res/layout/textview_alignment_rtl.xml b/tests/BiDiTests/res/layout/textview_alignment_rtl.xml
new file mode 100644
index 0000000..12a90d5
--- /dev/null
+++ b/tests/BiDiTests/res/layout/textview_alignment_rtl.xml
@@ -0,0 +1,578 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/textview_alignment_rtl"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layoutDirection="rtl">
+
+ <TableLayout android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TableRow>
+ <TextView android:text="(unspecified)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity (default)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="left"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="left"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="right"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="right"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="start"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="start"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="end"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="end"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="gravity center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="gravity"
+ android:gravity="center_horizontal"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="gravity"
+ android:gravity="center_horizontal"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="center"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="textStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="textStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="textStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="textEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="textEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="textEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="viewStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="viewStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="viewStart"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:text="viewEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="viewEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="viewEnd"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="gravity"
+ android:gravity="center_horizontal">
+
+ <TextView android:text="inherit gravity (default)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="center">
+
+ <TextView android:text="inherit gravity center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="textStart">
+
+ <TextView android:text="inherit textStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="textEnd">
+
+ <TextView android:text="inherit textEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="viewStart">
+
+ <TextView android:text="inherit viewStart"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ <TableRow android:textAlignment="viewEnd">
+
+ <TextView android:text="inherit viewEnd"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:typeface="serif"
+ android:layout_marginRight="7dip"
+ android:layout_marginLeft="7dip"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/textview_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ <TextView android:layout_height="wrap_content"
+ android:layout_width="200dip"
+ android:textSize="24dip"
+ android:text="@string/hebrew_text"
+ android:textAlignment="inherit"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:background="#444444"
+ />
+ </TableRow>
+
+ </TableLayout>
+
+</FrameLayout>
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index c5a1235..209597e 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -104,6 +104,16 @@
addItem(result, "Canvas", BiDiTestCanvas.class, R.id.canvas);
addItem(result, "Canvas2", BiDiTestCanvas2.class, R.id.canvas2);
+ addItem(result, "TextView LTR", BiDiTestTextViewLtr.class, R.id.textview_ltr);
+ addItem(result, "TextView RTL", BiDiTestTextViewRtl.class, R.id.textview_rtl);
+ addItem(result, "TextView LOC", BiDiTestTextViewLocale.class, R.id.textview_locale);
+
+ addItem(result, "TextDirection LTR", BiDiTestTextViewDirectionLtr.class, R.id.textview_direction_ltr);
+ addItem(result, "TextDirection RTL", BiDiTestTextViewDirectionRtl.class, R.id.textview_direction_rtl);
+
+ addItem(result, "TextAlignment LTR", BiDiTestTextViewAlignmentLtr.class, R.id.textview_alignment_ltr);
+ addItem(result, "TextAlignment RTL", BiDiTestTextViewAlignmentRtl.class, R.id.textview_alignment_rtl);
+
addItem(result, "Linear LTR", BiDiTestLinearLayoutLtr.class, R.id.linear_layout_ltr);
addItem(result, "Linear RTL", BiDiTestLinearLayoutRtl.class, R.id.linear_layout_rtl);
addItem(result, "Linear LOC", BiDiTestLinearLayoutLocale.class, R.id.linear_layout_locale);
@@ -134,15 +144,9 @@
addItem(result, "Margin MIXED", BiDiTestViewGroupMarginMixed.class, R.id.view_group_margin_mixed);
- addItem(result, "TextView LTR", BiDiTestTextViewLtr.class, R.id.textview_ltr);
- addItem(result, "TextView RTL", BiDiTestTextViewRtl.class, R.id.textview_rtl);
- addItem(result, "TextView LOC", BiDiTestTextViewLocale.class, R.id.textview_locale);
-
- addItem(result, "TextDirection LTR", BiDiTestTextViewDirectionLtr.class, R.id.textview_direction_ltr);
- addItem(result, "TextDirection RTL", BiDiTestTextViewDirectionRtl.class, R.id.textview_direction_rtl);
-
addItem(result, "TextView Drawables LTR", BiDiTestTextViewDrawablesLtr.class, R.id.textview_drawables_ltr);
addItem(result, "TextView Drawables RTL", BiDiTestTextViewDrawablesRtl.class, R.id.textview_drawables_rtl);
+
addItem(result, "Gallery LTR", BiDiTestGalleryLtr.class, R.id.gallery_ltr);
addItem(result, "Gallery RTL", BiDiTestGalleryRtl.class, R.id.gallery_rtl);
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentLtr.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentLtr.java
new file mode 100644
index 0000000..5ea5d81
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentLtr.java
@@ -0,0 +1,31 @@
+/*
+ * 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.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestTextViewAlignmentLtr extends Fragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.textview_alignment_ltr, container, false);
+ }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentRtl.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentRtl.java
new file mode 100644
index 0000000..fcc7a5d
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewAlignmentRtl.java
@@ -0,0 +1,31 @@
+/*
+ * 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.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestTextViewAlignmentRtl extends Fragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.textview_alignment_rtl, container, false);
+ }
+}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index b310d93..3775f9f 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -42,6 +42,15 @@
</activity>
<activity
+ android:name="DatePickerActivity"
+ android:label="_DatePicker">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="ClipRegionActivity"
android:label="_ClipRegion">
<intent-filter>
@@ -638,7 +647,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
-
<activity
android:name="StackActivity"
@@ -649,5 +657,23 @@
</intent-filter>
</activity>
+ <activity
+ android:name="TransformsAndAnimationsActivity"
+ android:label="_TransformAnim">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="ViewPropertyAlphaActivity"
+ android:label="_ViewPropAlpha">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/tests/HwAccelerationTest/res/layout/date_picker.xml b/tests/HwAccelerationTest/res/layout/date_picker.xml
new file mode 100644
index 0000000..742a03b
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/date_picker.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- Layout of date picker-->
+
+<!-- The width of this container is manually set a little bigger than the one of the children
+ contained in it. This helps to prevent rounding errors when toggling the "Show year" option -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="270dip"
+ android:layout_height="wrap_content">
+
+ <CheckBox
+ android:id="@+id/yearToggle"
+ android:text="Provide a year"
+ android:paddingTop="5dip"
+ android:paddingBottom="5dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <!-- Warning: everything within the parent is removed and re-ordered depending
+ on the date format selected by the user. -->
+ <LinearLayout
+ android:id="@+id/parent"
+ android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:animateLayoutChanges="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <!-- Month -->
+ <NumberPicker
+ android:id="@+id/month"
+ android:layout_width="80dip"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="1dip"
+ android:layout_marginRight="1dip"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ />
+
+ <!-- Day -->
+ <NumberPicker
+ android:id="@+id/day"
+ android:layout_width="80dip"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="1dip"
+ android:layout_marginRight="1dip"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ />
+
+ <!-- Year -->
+ <NumberPicker
+ android:id="@+id/year"
+ android:layout_width="95dip"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="1dip"
+ android:layout_marginRight="1dip"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ />
+ </LinearLayout>
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/res/layout/transforms_and_animations.xml b/tests/HwAccelerationTest/res/layout/transforms_and_animations.xml
new file mode 100644
index 0000000..1595502
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/transforms_and_animations.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <CheckBox android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="None"
+ android:checked="true"
+ android:id="@+id/layersNoneCB"/>
+
+ <CheckBox android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Hardware"
+ android:id="@+id/layersHwCB"/>
+
+ <CheckBox android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Software"
+ android:id="@+id/layersSwCB"/>
+
+ </LinearLayout>
+
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="1"
+ android:id="@+id/button1"/>
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="2"
+ android:id="@+id/button2"/>
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="3"
+ android:id="@+id/button3"/>
+
+ </LinearLayout>
+
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="1a"
+ android:id="@+id/button1a"/>
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="2a"
+ android:id="@+id/button2a"/>
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="3a"
+ android:id="@+id/button3a"/>
+
+ </LinearLayout>
+
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="1b"
+ android:id="@+id/button1b"/>
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="2b"
+ android:id="@+id/button2b"/>
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="3b"
+ android:id="@+id/button3b"/>
+
+ </LinearLayout>
+
+ <view class="com.android.test.hwui.TransformsAndAnimationsActivity$MyLayout"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="4"
+ android:id="@+id/button4"/>
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="5"
+ android:id="@+id/button5"/>
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="6"
+ android:id="@+id/button6"/>
+
+ </view>
+
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="7"
+ android:id="@+id/button7"/>
+
+ <Button android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="8"
+ android:id="@+id/button8"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/res/layout/view_properties.xml b/tests/HwAccelerationTest/res/layout/view_properties.xml
new file mode 100644
index 0000000..d7ed819
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/view_properties.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Invalidate"
+ android:id="@+id/invalidateButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button"
+ android:id="@+id/button"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Some text"
+ android:id="@+id/textview"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/spantext"/>
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Edit text"
+ android:id="@+id/edittext"/>
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Selected text"
+ android:id="@+id/selectedtext"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Some text"
+ android:background="#00ff00"
+ android:id="@+id/textviewbackground"/>
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/icon"
+ android:id="@+id/imageview"/>
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:id="@+id/layout">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Some text"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Some text"
+ android:background="#00ff00"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/DatePicker.java b/tests/HwAccelerationTest/src/com/android/test/hwui/DatePicker.java
new file mode 100644
index 0000000..db247e3
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/DatePicker.java
@@ -0,0 +1,474 @@
+/*
+ * 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.test.hwui;
+
+import android.annotation.Widget;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.NumberPicker;
+
+import java.text.DateFormatSymbols;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+/**
+ * A view for selecting a month / year / day based on a calendar like layout.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
+ *
+ * For a dialog using this view, see {@link android.app.DatePickerDialog}.
+ */
+@Widget
+public class DatePicker extends FrameLayout {
+
+ private static final int DEFAULT_START_YEAR = 1900;
+ private static final int DEFAULT_END_YEAR = 2100;
+
+ /* UI Components */
+ private final CheckBox mYearToggle;
+ private final NumberPicker mDayPicker;
+ private final NumberPicker mMonthPicker;
+ private final NumberPicker mYearPicker;
+
+ /**
+ * How we notify users the date has changed.
+ */
+ private OnDateChangedListener mOnDateChangedListener;
+
+ private int mDay;
+ private int mMonth;
+ private int mYear;
+ private boolean mYearOptional = true;
+ private boolean mHasYear;
+
+ /**
+ * The callback used to indicate the user changes the date.
+ */
+ public interface OnDateChangedListener {
+
+ /**
+ * @param view The view associated with this listener.
+ * @param year The year that was set.
+ * @param monthOfYear The month that was set (0-11) for compatibility
+ * with {@link java.util.Calendar}.
+ * @param dayOfMonth The day of the month that was set.
+ */
+ void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth);
+ }
+
+ public DatePicker(Context context) {
+ this(context, null);
+ }
+
+ public DatePicker(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ @SuppressWarnings("deprecation")
+ public DatePicker(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ ContextThemeWrapper themed = new ContextThemeWrapper(context,
+ com.android.internal.R.style.Theme_Holo_Light_Dialog_Alert);
+ LayoutInflater inflater = (LayoutInflater) themed.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.date_picker, this, true);
+
+ mDayPicker = (NumberPicker) findViewById(R.id.day);
+ mDayPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
+ mDayPicker.setOnLongPressUpdateInterval(100);
+ mDayPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+ public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+ mDay = newVal;
+ notifyDateChanged();
+ }
+ });
+ mMonthPicker = (NumberPicker) findViewById(R.id.month);
+ mMonthPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
+ DateFormatSymbols dfs = new DateFormatSymbols();
+ String[] months = dfs.getShortMonths();
+
+ /*
+ * If the user is in a locale where the month names are numeric,
+ * use just the number instead of the "month" character for
+ * consistency with the other fields.
+ */
+ if (months[0].startsWith("1")) {
+ for (int i = 0; i < months.length; i++) {
+ months[i] = String.valueOf(i + 1);
+ }
+ mMonthPicker.setMinValue(1);
+ mMonthPicker.setMaxValue(12);
+ } else {
+ mMonthPicker.setMinValue(1);
+ mMonthPicker.setMaxValue(12);
+ mMonthPicker.setDisplayedValues(months);
+ }
+
+ mMonthPicker.setOnLongPressUpdateInterval(200);
+ mMonthPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+ public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+
+ /* We display the month 1-12 but store it 0-11 so always
+ * subtract by one to ensure our internal state is always 0-11
+ */
+ mMonth = newVal - 1;
+ // Adjust max day of the month
+ adjustMaxDay();
+ notifyDateChanged();
+ updateDaySpinner();
+ }
+ });
+ mYearPicker = (NumberPicker) findViewById(R.id.year);
+ mYearPicker.setOnLongPressUpdateInterval(100);
+ mYearPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+ public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+ mYear = newVal;
+ // Adjust max day for leap years if needed
+ adjustMaxDay();
+ notifyDateChanged();
+ updateDaySpinner();
+ }
+ });
+
+ mYearToggle = (CheckBox) findViewById(R.id.yearToggle);
+ mYearToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ mHasYear = isChecked;
+ adjustMaxDay();
+ notifyDateChanged();
+ updateSpinners();
+ }
+ });
+
+ // attributes
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.DatePicker);
+
+ int mStartYear =
+ a.getInt(com.android.internal.R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
+ int mEndYear =
+ a.getInt(com.android.internal.R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
+ mYearPicker.setMinValue(mStartYear);
+ mYearPicker.setMaxValue(mEndYear);
+
+ a.recycle();
+
+ // initialize to current date
+ Calendar cal = Calendar.getInstance();
+ init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), null);
+
+ // re-order the number pickers to match the current date format
+ reorderPickers(months);
+
+ if (!isEnabled()) {
+ setEnabled(false);
+ }
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ mDayPicker.setEnabled(enabled);
+ mMonthPicker.setEnabled(enabled);
+ mYearPicker.setEnabled(enabled);
+ }
+
+ private void reorderPickers(String[] months) {
+ java.text.DateFormat format;
+ String order;
+
+ /*
+ * If the user is in a locale where the medium date format is
+ * still numeric (Japanese and Czech, for example), respect
+ * the date format order setting. Otherwise, use the order
+ * that the locale says is appropriate for a spelled-out date.
+ */
+
+ if (months[0].startsWith("1")) {
+ format = DateFormat.getDateFormat(getContext());
+ } else {
+ format = DateFormat.getMediumDateFormat(getContext());
+ }
+
+ if (format instanceof SimpleDateFormat) {
+ order = ((SimpleDateFormat) format).toPattern();
+ } else {
+ // Shouldn't happen, but just in case.
+ order = new String(DateFormat.getDateFormatOrder(getContext()));
+ }
+
+ /* Remove the 3 pickers from their parent and then add them back in the
+ * required order.
+ */
+ LinearLayout parent = (LinearLayout) findViewById(R.id.parent);
+ parent.removeAllViews();
+
+ boolean quoted = false;
+ boolean didDay = false, didMonth = false, didYear = false;
+
+ for (int i = 0; i < order.length(); i++) {
+ char c = order.charAt(i);
+
+ if (c == '\'') {
+ quoted = !quoted;
+ }
+
+ if (!quoted) {
+ if (c == DateFormat.DATE && !didDay) {
+ parent.addView(mDayPicker);
+ didDay = true;
+ } else if ((c == DateFormat.MONTH || c == 'L') && !didMonth) {
+ parent.addView(mMonthPicker);
+ didMonth = true;
+ } else if (c == DateFormat.YEAR && !didYear) {
+ parent.addView (mYearPicker);
+ didYear = true;
+ }
+ }
+ }
+
+ // Shouldn't happen, but just in case.
+ if (!didMonth) {
+ parent.addView(mMonthPicker);
+ }
+ if (!didDay) {
+ parent.addView(mDayPicker);
+ }
+ if (!didYear) {
+ parent.addView(mYearPicker);
+ }
+ }
+
+ public void updateDate(int year, int monthOfYear, int dayOfMonth) {
+ if (mYear != year || mMonth != monthOfYear || mDay != dayOfMonth) {
+ mYear = (mYearOptional && year == 0) ? getCurrentYear() : year;
+ mMonth = monthOfYear;
+ mDay = dayOfMonth;
+ updateSpinners();
+ reorderPickers(new DateFormatSymbols().getShortMonths());
+ notifyDateChanged();
+ }
+ }
+
+ private static int getCurrentYear() {
+ return Calendar.getInstance().get(Calendar.YEAR);
+ }
+
+ private static class SavedState extends BaseSavedState {
+
+ private final int mYear;
+ private final int mMonth;
+ private final int mDay;
+ private final boolean mHasYear;
+ private final boolean mYearOptional;
+
+ /**
+ * Constructor called from {@link DatePicker#onSaveInstanceState()}
+ */
+ private SavedState(Parcelable superState, int year, int month, int day, boolean hasYear,
+ boolean yearOptional) {
+ super(superState);
+ mYear = year;
+ mMonth = month;
+ mDay = day;
+ mHasYear = hasYear;
+ mYearOptional = yearOptional;
+ }
+
+ /**
+ * Constructor called from {@link #CREATOR}
+ */
+ private SavedState(Parcel in) {
+ super(in);
+ mYear = in.readInt();
+ mMonth = in.readInt();
+ mDay = in.readInt();
+ mHasYear = in.readInt() != 0;
+ mYearOptional = in.readInt() != 0;
+ }
+
+ public int getYear() {
+ return mYear;
+ }
+
+ public int getMonth() {
+ return mMonth;
+ }
+
+ public int getDay() {
+ return mDay;
+ }
+
+ public boolean hasYear() {
+ return mHasYear;
+ }
+
+ public boolean isYearOptional() {
+ return mYearOptional;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mYear);
+ dest.writeInt(mMonth);
+ dest.writeInt(mDay);
+ dest.writeInt(mHasYear ? 1 : 0);
+ dest.writeInt(mYearOptional ? 1 : 0);
+ }
+
+ @SuppressWarnings("unused")
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Creator<SavedState>() {
+
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+
+ /**
+ * Override so we are in complete control of save / restore for this widget.
+ */
+ @Override
+ protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+ dispatchThawSelfOnly(container);
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+
+ return new SavedState(superState, mYear, mMonth, mDay, mHasYear, mYearOptional);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ mYear = ss.getYear();
+ mMonth = ss.getMonth();
+ mDay = ss.getDay();
+ mHasYear = ss.hasYear();
+ mYearOptional = ss.isYearOptional();
+ updateSpinners();
+ }
+
+ /**
+ * Initialize the state.
+ * @param year The initial year.
+ * @param monthOfYear The initial month.
+ * @param dayOfMonth The initial day of the month.
+ * @param onDateChangedListener How user is notified date is changed by user, can be null.
+ */
+ public void init(int year, int monthOfYear, int dayOfMonth,
+ OnDateChangedListener onDateChangedListener) {
+ init(year, monthOfYear, dayOfMonth, false, onDateChangedListener);
+ }
+
+ /**
+ * Initialize the state.
+ * @param year The initial year or 0 if no year has been specified
+ * @param monthOfYear The initial month.
+ * @param dayOfMonth The initial day of the month.
+ * @param yearOptional True if the user can toggle the year
+ * @param onDateChangedListener How user is notified date is changed by user, can be null.
+ */
+ public void init(int year, int monthOfYear, int dayOfMonth, boolean yearOptional,
+ OnDateChangedListener onDateChangedListener) {
+ mYear = (yearOptional && year == 0) ? getCurrentYear() : year;
+ mMonth = monthOfYear;
+ mDay = dayOfMonth;
+ mYearOptional = yearOptional;
+ mHasYear = !yearOptional || (year != 0);
+ mOnDateChangedListener = onDateChangedListener;
+ updateSpinners();
+ }
+
+ private void updateSpinners() {
+ updateDaySpinner();
+ mYearToggle.setChecked(mHasYear);
+ mYearToggle.setVisibility(mYearOptional ? View.VISIBLE : View.GONE);
+ mYearPicker.setValue(mYear);
+ mYearPicker.setVisibility(mHasYear ? View.VISIBLE : View.GONE);
+
+ /* The month display uses 1-12 but our internal state stores it
+ * 0-11 so add one when setting the display.
+ */
+ mMonthPicker.setValue(mMonth + 1);
+ }
+
+ private void updateDaySpinner() {
+ Calendar cal = Calendar.getInstance();
+ // if year was not set, use 2000 as it was a leap year
+ cal.set(mHasYear ? mYear : 2000, mMonth, 1);
+ int max = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+ mDayPicker.setMinValue(1);
+ mDayPicker.setMaxValue(max);
+ mDayPicker.setValue(mDay);
+ }
+
+ public int getYear() {
+ return (mYearOptional && !mHasYear) ? 0 : mYear;
+ }
+
+ public int getMonth() {
+ return mMonth;
+ }
+
+ public int getDayOfMonth() {
+ return mDay;
+ }
+
+ private void adjustMaxDay(){
+ Calendar cal = Calendar.getInstance();
+ // if year was not set, use 2000 as it was a leap year
+ cal.set(Calendar.YEAR, mHasYear ? mYear : 2000);
+ cal.set(Calendar.MONTH, mMonth);
+ int max = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+ if (mDay > max) {
+ mDay = max;
+ }
+ }
+
+ private void notifyDateChanged() {
+ if (mOnDateChangedListener != null) {
+ int year = (mYearOptional && !mHasYear) ? 0 : mYear;
+ mOnDateChangedListener.onDateChanged(DatePicker.this, year, mMonth, mDay);
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/DatePickerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/DatePickerActivity.java
new file mode 100644
index 0000000..5482ee2
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/DatePickerActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class DatePickerActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ DatePicker picker = new DatePicker(this);
+ picker.init(2012, 3, 3, true, new DatePicker.OnDateChangedListener() {
+ @Override
+ public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
+ }
+ });
+ setContentView(picker);
+ getWindow().setBackgroundDrawable(new ColorDrawable(0xffffffff));
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TransformsAndAnimationsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TransformsAndAnimationsActivity.java
new file mode 100644
index 0000000..684d179
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TransformsAndAnimationsActivity.java
@@ -0,0 +1,201 @@
+/*
+ * 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.test.hwui;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+import android.view.animation.TranslateAnimation;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+
+public class TransformsAndAnimationsActivity extends Activity {
+ Button button1;
+ Button button2;
+ Button button3;
+ Button button1a;
+ Button button2a;
+ Button button3a;
+ Button button1b;
+ Button button2b;
+ Button button3b;
+ Button button4;
+ Button button5;
+ Button button6;
+ Button button7;
+ Button button8;
+ CheckBox layersNoneCB;
+ CheckBox layersHardwareCB;
+ CheckBox layersSoftwareCB;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.transforms_and_animations);
+
+ button1 = (Button) findViewById(R.id.button1);
+ button2 = (Button) findViewById(R.id.button2);
+ button3 = (Button) findViewById(R.id.button3);
+ button1a = (Button) findViewById(R.id.button1a);
+ button2a = (Button) findViewById(R.id.button2a);
+ button3a = (Button) findViewById(R.id.button3a);
+ button1b = (Button) findViewById(R.id.button1b);
+ button2b = (Button) findViewById(R.id.button2b);
+ button3b = (Button) findViewById(R.id.button3b);
+ button4 = (Button) findViewById(R.id.button4);
+ button5 = (Button) findViewById(R.id.button5);
+ button6 = (Button) findViewById(R.id.button6);
+ button7 = (Button) findViewById(R.id.button7);
+ button8 = (Button) findViewById(R.id.button8);
+ layersNoneCB = (CheckBox) findViewById(R.id.layersNoneCB);
+ layersHardwareCB = (CheckBox) findViewById(R.id.layersHwCB);
+ layersSoftwareCB = (CheckBox) findViewById(R.id.layersSwCB);
+
+ layersNoneCB.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ setLayerType(View.LAYER_TYPE_NONE);
+ layersHardwareCB.setChecked(false);
+ layersSoftwareCB.setChecked(false);
+ }
+ }
+ });
+
+ layersSoftwareCB.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ setLayerType(View.LAYER_TYPE_SOFTWARE);
+ layersHardwareCB.setChecked(false);
+ layersNoneCB.setChecked(false);
+ }
+ }
+ });
+
+ layersHardwareCB.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ setLayerType(View.LAYER_TYPE_HARDWARE);
+ layersNoneCB.setChecked(false);
+ layersSoftwareCB.setChecked(false);
+ }
+ }
+ });
+
+ button1a.setAlpha(.5f);
+ button2a.setAlpha(.5f);
+ button3a.setAlpha(.5f);
+ button3.setTranslationX(50);
+ button7.setTranslationX(50);
+ button8.setTranslationX(50);
+
+ final AlphaAnimation alphaAnim = new AlphaAnimation(1, 0);
+ alphaAnim.setDuration(1000);
+ alphaAnim.setRepeatCount(Animation.INFINITE);
+ alphaAnim.setRepeatMode(Animation.REVERSE);
+
+ final TranslateAnimation transAnim = new TranslateAnimation(0, -50, 0, 0);
+ transAnim.setDuration(1000);
+ transAnim.setRepeatCount(Animation.INFINITE);
+ transAnim.setRepeatMode(Animation.REVERSE);
+
+ getWindow().getDecorView().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ button1.startAnimation(alphaAnim);
+ button2.startAnimation(alphaAnim);
+ button3.startAnimation(alphaAnim);
+
+ button1a.startAnimation(alphaAnim);
+ button2a.startAnimation(alphaAnim);
+ button3a.startAnimation(alphaAnim);
+
+ button1b.startAnimation(alphaAnim);
+ button2b.startAnimation(alphaAnim);
+ button3b.startAnimation(alphaAnim);
+ startAnimator(button1b);
+ startAnimator(button2b);
+ startAnimator(button3b);
+
+ button7.startAnimation(transAnim);
+ button8.startAnimation(transAnim);
+ }
+ }, 2000);
+ }
+
+ private void setLayerType(int layerType) {
+ button1.setLayerType(layerType, null);
+ button2.setLayerType(layerType, null);
+ button3.setLayerType(layerType, null);
+ button1a.setLayerType(layerType, null);
+ button2a.setLayerType(layerType, null);
+ button3a.setLayerType(layerType, null);
+ button1b.setLayerType(layerType, null);
+ button2b.setLayerType(layerType, null);
+ button3b.setLayerType(layerType, null);
+ button4.setLayerType(layerType, null);
+ button5.setLayerType(layerType, null);
+ button6.setLayerType(layerType, null);
+ button7.setLayerType(layerType, null);
+ button8.setLayerType(layerType, null);
+ }
+
+ private void startAnimator(View target) {
+ ObjectAnimator anim1b = ObjectAnimator.ofFloat(target, View.ALPHA, 0);
+ anim1b.setRepeatCount(ValueAnimator.INFINITE);
+ anim1b.setRepeatMode(ValueAnimator.REVERSE);
+ anim1b.setDuration(1000);
+ anim1b.start();
+ }
+
+ public static class MyLayout extends LinearLayout {
+
+ public MyLayout(Context context) {
+ super(context);
+ setStaticTransformationsEnabled(true);
+ }
+
+ public MyLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setStaticTransformationsEnabled(true);
+ }
+
+ public MyLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setStaticTransformationsEnabled(true);
+ }
+
+ @Override
+ protected boolean getChildStaticTransformation(View child, Transformation t) {
+ t.clear();
+ t.setAlpha(.35f);
+
+ return true;
+ }
+ }
+}
+
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewPropertyAlphaActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewPropertyAlphaActivity.java
new file mode 100644
index 0000000..738801d
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewPropertyAlphaActivity.java
@@ -0,0 +1,145 @@
+/*
+ * 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.test.hwui;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.ImageSpan;
+import android.text.style.SuggestionSpan;
+import android.text.style.UnderlineSpan;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class ViewPropertyAlphaActivity extends Activity {
+
+ MyView myViewAlphaDefault, myViewAlphaHandled;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.view_properties);
+
+ getWindow().getDecorView().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ startAnim(R.id.button);
+ startAnim(R.id.textview);
+ startAnim(R.id.spantext);
+ startAnim(R.id.edittext);
+ startAnim(R.id.selectedtext);
+ startAnim(R.id.textviewbackground);
+ startAnim(R.id.layout);
+ startAnim(R.id.imageview);
+ startAnim(myViewAlphaDefault);
+ startAnim(myViewAlphaHandled);
+ EditText selectedText = (EditText) findViewById(R.id.selectedtext);
+ selectedText.setSelection(3, 8);
+ }
+ }, 2000);
+
+ Button invalidator = (Button) findViewById(R.id.invalidateButton);
+ invalidator.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ findViewById(R.id.textview).invalidate();
+ findViewById(R.id.spantext).invalidate();
+ }
+ });
+
+ TextView textView = (TextView) findViewById(R.id.spantext);
+ if (textView != null) {
+ SpannableStringBuilder text =
+ new SpannableStringBuilder("Now this is a short text message with spans");
+
+ text.setSpan(new BackgroundColorSpan(Color.RED), 0, 3,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text.setSpan(new ForegroundColorSpan(Color.BLUE), 4, 9,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text.setSpan(new SuggestionSpan(this, new String[]{"longer"}, 3), 11, 16,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text.setSpan(new UnderlineSpan(), 17, 20,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text.setSpan(new ImageSpan(this, R.drawable.icon), 21, 22,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ textView.setText(text);
+ }
+
+ LinearLayout container = (LinearLayout) findViewById(R.id.container);
+ myViewAlphaDefault = new MyView(this, false);
+ myViewAlphaDefault.setLayoutParams(new LinearLayout.LayoutParams(75, 75));
+ container.addView(myViewAlphaDefault);
+ myViewAlphaHandled = new MyView(this, true);
+ myViewAlphaHandled.setLayoutParams(new LinearLayout.LayoutParams(75, 75));
+ container.addView(myViewAlphaHandled);
+ }
+
+ private void startAnim(View target) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(target, View.ALPHA, 0);
+ anim.setRepeatCount(ValueAnimator.INFINITE);
+ anim.setRepeatMode(ValueAnimator.REVERSE);
+ anim.setDuration(1000);
+ anim.start();
+ }
+ private void startAnim(int id) {
+ startAnim(findViewById(id));
+ }
+
+ private static class MyView extends View {
+ private int mMyAlpha = 255;
+ private boolean mHandleAlpha;
+ private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ private MyView(Context context, boolean handleAlpha) {
+ super(context);
+ mHandleAlpha = handleAlpha;
+ mPaint.setColor(Color.RED);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mHandleAlpha) {
+ mPaint.setAlpha(mMyAlpha);
+ }
+ canvas.drawCircle(30, 30, 30, mPaint);
+ }
+
+ @Override
+ protected boolean onSetAlpha(int alpha) {
+ if (mHandleAlpha) {
+ mMyAlpha = alpha;
+ return true;
+ }
+ return super.onSetAlpha(alpha);
+ }
+ }
+
+}
diff --git a/tests/RenderScriptTests/SurfaceTexture/Android.mk b/tests/RenderScriptTests/SurfaceTexture/Android.mk
new file mode 100644
index 0000000..bbd4d55
--- /dev/null
+++ b/tests/RenderScriptTests/SurfaceTexture/Android.mk
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+# TODO: build fails with this set
+# LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := RsSurfaceTextureOpaque
+
+include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/SurfaceTexture/AndroidManifest.xml b/tests/RenderScriptTests/SurfaceTexture/AndroidManifest.xml
new file mode 100644
index 0000000..8aaa239
--- /dev/null
+++ b/tests/RenderScriptTests/SurfaceTexture/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.rs.sto">
+ <uses-sdk android:minSdkVersion="14" />
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-feature android:name="android.hardware.camera" />
+ <uses-feature android:name="android.hardware.camera.autofocus" />
+
+ <application
+ android:label="RsSurfaceTextureOpaque"
+ android:hardwareAccelerated="true"
+ android:icon="@drawable/test_pattern">
+
+ <activity android:name="SurfaceTextureOpaque">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/RenderScriptTests/SurfaceTexture/res/drawable/test_pattern.png b/tests/RenderScriptTests/SurfaceTexture/res/drawable/test_pattern.png
new file mode 100644
index 0000000..e7d1455
--- /dev/null
+++ b/tests/RenderScriptTests/SurfaceTexture/res/drawable/test_pattern.png
Binary files differ
diff --git a/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/CameraCapture.java b/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/CameraCapture.java
new file mode 100644
index 0000000..afdab41
--- /dev/null
+++ b/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/CameraCapture.java
@@ -0,0 +1,234 @@
+/*
+ * 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.example.android.rs.sto;
+
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.List;
+
+public class CameraCapture {
+
+ public interface CameraFrameListener {
+ public void onNewCameraFrame();
+ }
+
+ static final int FRAMES_PER_SEC = 30;
+
+ private Camera mCamera;
+ private SurfaceTexture mSurfaceTexture;
+
+ private int mProgram;
+
+ private int mCameraTransformHandle;
+ private int mTexSamplerHandle;
+ private int mTexCoordHandle;
+ private int mPosCoordHandle;
+
+ private float[] mCameraTransform = new float[16];
+
+ private int mCameraId = 0;
+ private int mWidth;
+ private int mHeight;
+
+ private long mStartCaptureTime = 0;
+
+ private boolean mNewFrameAvailable = false;
+ private boolean mIsOpen = false;
+
+ private CameraFrameListener mListener;
+
+ public synchronized void beginCapture(int cameraId, int width, int height,
+ SurfaceTexture st) {
+ mCameraId = cameraId;
+ mSurfaceTexture = st;
+
+ // Open the camera
+ openCamera(width, height);
+
+ // Start the camera
+ mStartCaptureTime = SystemClock.elapsedRealtime();
+ mCamera.startPreview();
+ mIsOpen = true;
+ }
+
+ public void getCurrentFrame() {
+ if (checkNewFrame()) {
+ if (mStartCaptureTime > 0 && SystemClock.elapsedRealtime() - mStartCaptureTime > 2000) {
+ // Lock white-balance and exposure for effects
+ Log.i("CC", "Locking white-balance and exposure!");
+ Camera.Parameters params = mCamera.getParameters();
+ params.setAutoWhiteBalanceLock(true);
+ params.setAutoExposureLock(true);
+ //mCamera.setParameters(params);
+ mStartCaptureTime = 0;
+ }
+
+ mSurfaceTexture.updateTexImage();
+ mSurfaceTexture.getTransformMatrix(mCameraTransform);
+
+ // display it here
+ }
+ }
+
+ public synchronized boolean hasNewFrame() {
+ return mNewFrameAvailable;
+ }
+
+ public synchronized void endCapture() {
+ mIsOpen = false;
+ if (mCamera != null) {
+ mCamera.release();
+ mCamera = null;
+ mSurfaceTexture = null;
+ }
+ }
+
+ public synchronized boolean isOpen() {
+ return mIsOpen;
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public void setCameraFrameListener(CameraFrameListener listener) {
+ mListener = listener;
+ }
+
+ private void openCamera(int width, int height) {
+ // Setup camera
+ mCamera = Camera.open(mCameraId);
+ mCamera.setParameters(calcCameraParameters(width, height));
+
+ // Create camera surface texture
+ try {
+ mCamera.setPreviewTexture(mSurfaceTexture);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not bind camera surface texture: " +
+ e.getMessage() + "!");
+ }
+
+ // Connect SurfaceTexture to callback
+ mSurfaceTexture.setOnFrameAvailableListener(onCameraFrameAvailableListener);
+ }
+
+ private Camera.Parameters calcCameraParameters(int width, int height) {
+ Camera.Parameters params = mCamera.getParameters();
+ params.setPreviewSize(mWidth, mHeight);
+
+ // Find closest size
+ int closestSize[] = findClosestSize(width, height, params);
+ mWidth = closestSize[0];
+ mHeight = closestSize[1];
+ params.setPreviewSize(mWidth, mHeight);
+
+ // Find closest FPS
+ int closestRange[] = findClosestFpsRange(FRAMES_PER_SEC, params);
+
+ params.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+
+ return params;
+ }
+
+ private int[] findClosestSize(int width, int height, Camera.Parameters parameters) {
+ List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
+ int closestWidth = -1;
+ int closestHeight = -1;
+ int smallestWidth = previewSizes.get(0).width;
+ int smallestHeight = previewSizes.get(0).height;
+ for (Camera.Size size : previewSizes) {
+ // Best match defined as not being larger in either dimension than
+ // the requested size, but as close as possible. The below isn't a
+ // stable selection (reording the size list can give different
+ // results), but since this is a fallback nicety, that's acceptable.
+ if ( size.width <= width &&
+ size.height <= height &&
+ size.width >= closestWidth &&
+ size.height >= closestHeight) {
+ closestWidth = size.width;
+ closestHeight = size.height;
+ }
+ if ( size.width < smallestWidth &&
+ size.height < smallestHeight) {
+ smallestWidth = size.width;
+ smallestHeight = size.height;
+ }
+ }
+ if (closestWidth == -1) {
+ // Requested size is smaller than any listed size; match with smallest possible
+ closestWidth = smallestWidth;
+ closestHeight = smallestHeight;
+ }
+ int[] closestSize = {closestWidth, closestHeight};
+ return closestSize;
+ }
+
+ private int[] findClosestFpsRange(int fps, Camera.Parameters params) {
+ List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
+ int[] closestRange = supportedFpsRanges.get(0);
+ int fpsk = fps * 1000;
+ int minDiff = 1000000;
+ for (int[] range : supportedFpsRanges) {
+ int low = range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
+ int high = range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
+ if (low <= fpsk && high >= fpsk) {
+ int diff = (fpsk - low) + (high - fpsk);
+ if (diff < minDiff) {
+ closestRange = range;
+ minDiff = diff;
+ }
+ }
+ }
+ Log.i("CC", "Found closest range: "
+ + closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] + " - "
+ + closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+ return closestRange;
+ }
+
+ private synchronized void signalNewFrame() {
+ mNewFrameAvailable = true;
+ if (mListener != null) {
+ mListener.onNewCameraFrame();
+ }
+ }
+
+ private synchronized boolean checkNewFrame() {
+ if (mNewFrameAvailable) {
+ mNewFrameAvailable = false;
+ return true;
+ }
+ return false;
+ }
+
+ private SurfaceTexture.OnFrameAvailableListener onCameraFrameAvailableListener =
+ new SurfaceTexture.OnFrameAvailableListener() {
+ @Override
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ signalNewFrame();
+ }
+ };
+}
diff --git a/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/SurfaceTextureOpaque.java b/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/SurfaceTextureOpaque.java
new file mode 100644
index 0000000..a51edaa
--- /dev/null
+++ b/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/SurfaceTextureOpaque.java
@@ -0,0 +1,71 @@
+/*
+ * 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.example.android.rs.sto;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.provider.Settings.System;
+import android.util.Log;
+import android.view.View;
+import android.graphics.SurfaceTexture;
+
+import java.lang.Runtime;
+
+public class SurfaceTextureOpaque extends Activity {
+ private SurfaceTextureOpaqueView mView;
+ CameraCapture mCC;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mView = new SurfaceTextureOpaqueView(this);
+ setContentView(mView);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mView.resume();
+ startCamera();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mView.pause();
+ mCC.endCapture();
+ }
+
+ cfl mCFL;
+ public void startCamera() {
+ mCC = new CameraCapture();
+ mCFL = new cfl();
+
+ mCC.setCameraFrameListener(mCFL);
+
+ mCC.beginCapture(1, 640, 480, mView.getST());
+ }
+
+ public class cfl implements CameraCapture.CameraFrameListener {
+ public void onNewCameraFrame() {
+ mView.mRender.newFrame();
+ }
+ }
+
+}
+
diff --git a/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/SurfaceTextureOpaqueRS.java b/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/SurfaceTextureOpaqueRS.java
new file mode 100644
index 0000000..b638b7d
--- /dev/null
+++ b/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/SurfaceTextureOpaqueRS.java
@@ -0,0 +1,83 @@
+/*
+ * 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.example.android.rs.sto;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.graphics.SurfaceTexture;
+import android.util.Log;
+
+
+public class SurfaceTextureOpaqueRS {
+ static final private int NUM_CAMERA_PREVIEW_BUFFERS = 2;
+
+ public SurfaceTextureOpaqueRS() {
+ }
+
+ private Resources mRes;
+ private RenderScriptGL mRS;
+ private ScriptC_sto mScript;
+ private SurfaceTexture mST;
+ private Allocation mSto;
+ private Allocation mSto2;
+ private Allocation mRto;
+ private ProgramFragment mPF;
+
+ public void init(RenderScriptGL rs, Resources res) {
+ mRS = rs;
+ mRes = res;
+
+ Type.Builder tb = new Type.Builder(mRS, Element.RGBA_8888(mRS));
+ tb.setX(640);
+ tb.setY(480);
+ mSto = Allocation.createTyped(mRS, tb.create(), Allocation.USAGE_GRAPHICS_TEXTURE |
+ Allocation.USAGE_IO_INPUT);
+ mRto = Allocation.createTyped(mRS, tb.create(), Allocation.USAGE_GRAPHICS_RENDER_TARGET |
+ Allocation.USAGE_IO_OUTPUT);
+ mSto2 = Allocation.createTyped(mRS, tb.create(), Allocation.USAGE_GRAPHICS_TEXTURE |
+ Allocation.USAGE_IO_INPUT);
+ mST = mSto.getSurfaceTexture();
+ mRto.setSurfaceTexture(mSto2.getSurfaceTexture());
+
+ ProgramFragmentFixedFunction.Builder pfb = new ProgramFragmentFixedFunction.Builder(rs);
+ pfb.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
+ ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
+ mPF = pfb.create();
+ mPF.bindSampler(Sampler.CLAMP_NEAREST(mRS), 0);
+ rs.bindProgramFragment(mPF);
+
+ mScript = new ScriptC_sto(mRS, mRes, R.raw.sto);
+ mScript.set_sto(mSto);
+ mScript.set_rto(mRto);
+ mScript.set_sto2(mSto2);
+ mScript.set_pf(mPF);
+
+ mRS.bindRootScript(mScript);
+
+
+ android.util.Log.v("sto", "Init complete");
+ }
+
+ SurfaceTexture getST() {
+ return mST;
+ }
+
+ public void newFrame() {
+ mSto.ioReceive();
+ }
+
+}
diff --git a/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/SurfaceTextureOpaqueView.java b/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/SurfaceTextureOpaqueView.java
new file mode 100644
index 0000000..f5e49f2
--- /dev/null
+++ b/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/SurfaceTextureOpaqueView.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.example.android.rs.sto;
+
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScriptGL;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.SurfaceTexture;
+import android.util.Log;
+
+public class SurfaceTextureOpaqueView extends RSSurfaceView {
+
+ public SurfaceTextureOpaqueView(Context context) {
+ super(context);
+ }
+
+ RenderScriptGL mRS;
+ SurfaceTextureOpaqueRS mRender;
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mRS != null) {
+ mRS = null;
+ destroyRenderScriptGL();
+ }
+ }
+
+ SurfaceTexture getST() {
+ RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+ mRS = createRenderScriptGL(sc);
+ mRender = new SurfaceTextureOpaqueRS();
+ mRender.init(mRS, getResources());
+ return mRender.getST();
+ }
+
+}
+
+
diff --git a/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/sto.rs b/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/sto.rs
new file mode 100644
index 0000000..efa901a
--- /dev/null
+++ b/tests/RenderScriptTests/SurfaceTexture/src/com/example/android/rs/sto/sto.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.example.android.rs.sto)
+
+#pragma stateFragment(parent)
+
+#include "rs_graphics.rsh"
+
+
+rs_program_fragment pf;
+rs_allocation sto; // camera in
+rs_allocation sto2;
+rs_allocation rto; // render target
+
+int root() {
+ rsgBindTexture(pf, 0, sto);
+
+#if 1
+ rsgBindColorTarget(rto, 0);
+
+ rsgClearColor(0.f, 1.f, 0.f, 1.f);
+ rsgDrawQuadTexCoords(0, 0, 0, 0,0,
+ 0,500,0, 1,0,
+ 500,500,0, 1, 1,
+ 500, 0, 0, 0, 1 );
+ rsgClearColorTarget(0);
+
+ // io ops
+ rsAllocationIoSend(rto);
+ rsAllocationIoReceive(sto2);
+
+ rsgBindTexture(pf, 0, sto2);
+#endif
+
+ rsgClearColor(0.f, 1.f, 0.f, 1.f);
+ rsgDrawQuadTexCoords(0, 0, 0, 0,0,
+ 0,500,0, 1,0,
+ 500,500,0, 1, 1,
+ 500, 0, 0, 0, 1 );
+
+ return 1;
+}
+
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
index 87baf76..7c03313 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
@@ -23,7 +23,6 @@
import android.webkit.WebSettingsClassic;
import android.webkit.WebView;
import android.webkit.WebViewClassic;
-import android.widget.Toast;
import java.util.ArrayList;
@@ -72,10 +71,7 @@
mContext = c;
}
- /** Show a toast from the web page */
public void animationComplete() {
- Toast.makeText(mContext, "Animation complete!", Toast.LENGTH_SHORT).show();
- //Log.d(LOGTAG, "anim complete");
mAnimationTime = System.currentTimeMillis();
}
}
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 2b9b056..9de685a 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -57,6 +57,13 @@
bool is9Patch;
Res_png_9patch info9Patch;
+ // Layout padding, if relevant
+ bool haveLayoutBounds;
+ int32_t layoutBoundsLeft;
+ int32_t layoutBoundsTop;
+ int32_t layoutBoundsRight;
+ int32_t layoutBoundsBottom;
+
png_uint_32 allocHeight;
png_bytepp allocRows;
};
@@ -129,33 +136,62 @@
&interlace_type, &compression_type, NULL);
}
-static bool is_tick(png_bytep p, bool transparent, const char** outError)
+#define COLOR_TRANSPARENT 0
+#define COLOR_WHITE 0xFFFFFFFF
+#define COLOR_TICK 0xFF000000
+#define COLOR_LAYOUT_BOUNDS_TICK 0xFF0000FF
+
+enum {
+ TICK_TYPE_NONE,
+ TICK_TYPE_TICK,
+ TICK_TYPE_LAYOUT_BOUNDS,
+ TICK_TYPE_BOTH
+};
+
+static int tick_type(png_bytep p, bool transparent, const char** outError)
{
+ png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+
if (transparent) {
if (p[3] == 0) {
- return false;
+ return TICK_TYPE_NONE;
}
+ if (color == COLOR_LAYOUT_BOUNDS_TICK) {
+ return TICK_TYPE_LAYOUT_BOUNDS;
+ }
+ if (color == COLOR_TICK) {
+ return TICK_TYPE_TICK;
+ }
+
+ // Error cases
if (p[3] != 0xff) {
*outError = "Frame pixels must be either solid or transparent (not intermediate alphas)";
- return false;
+ return TICK_TYPE_NONE;
}
if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
- *outError = "Ticks in transparent frame must be black";
+ *outError = "Ticks in transparent frame must be black or red";
}
- return true;
+ return TICK_TYPE_TICK;
}
if (p[3] != 0xFF) {
*outError = "White frame must be a solid color (no alpha)";
}
- if (p[0] == 0xFF && p[1] == 0xFF && p[2] == 0xFF) {
- return false;
+ if (color == COLOR_WHITE) {
+ return TICK_TYPE_NONE;
}
+ if (color == COLOR_TICK) {
+ return TICK_TYPE_TICK;
+ }
+ if (color == COLOR_LAYOUT_BOUNDS_TICK) {
+ return TICK_TYPE_LAYOUT_BOUNDS;
+ }
+
if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
- *outError = "Ticks in white frame must be black";
- return false;
+ *outError = "Ticks in white frame must be black or red";
+ return TICK_TYPE_NONE;
}
- return true;
+ return TICK_TYPE_TICK;
}
enum {
@@ -175,7 +211,7 @@
bool found = false;
for (i=1; i<width-1; i++) {
- if (is_tick(row+i*4, transparent, outError)) {
+ if (TICK_TYPE_TICK == tick_type(row+i*4, transparent, outError)) {
if (state == TICK_START ||
(state == TICK_OUTSIDE_1 && multipleAllowed)) {
*outLeft = i-1;
@@ -224,7 +260,7 @@
bool found = false;
for (i=1; i<height-1; i++) {
- if (is_tick(rows[i]+offset, transparent, outError)) {
+ if (TICK_TYPE_TICK == tick_type(rows[i]+offset, transparent, outError)) {
if (state == TICK_START ||
(state == TICK_OUTSIDE_1 && multipleAllowed)) {
*outTop = i-1;
@@ -262,6 +298,83 @@
return NO_ERROR;
}
+static status_t get_horizontal_layout_bounds_ticks(
+ png_bytep row, int width, bool transparent, bool required,
+ int32_t* outLeft, int32_t* outRight, const char** outError)
+{
+ int i;
+ *outLeft = *outRight = 0;
+
+ // Look for left tick
+ if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + 4, transparent, outError)) {
+ // Starting with a layout padding tick
+ i = 1;
+ while (i < width - 1) {
+ (*outLeft)++;
+ i++;
+ int tick = tick_type(row + i * 4, transparent, outError);
+ if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+ break;
+ }
+ }
+ }
+
+ // Look for right tick
+ if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(row + (width - 2) * 4, transparent, outError)) {
+ // Ending with a layout padding tick
+ i = width - 2;
+ while (i > 1) {
+ (*outRight)++;
+ i--;
+ int tick = tick_type(row+i*4, transparent, outError);
+ if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+ break;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+static status_t get_vertical_layout_bounds_ticks(
+ png_bytepp rows, int offset, int height, bool transparent, bool required,
+ int32_t* outTop, int32_t* outBottom, const char** outError)
+{
+ int i;
+ *outTop = *outBottom = 0;
+
+ // Look for top tick
+ if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[1] + offset, transparent, outError)) {
+ // Starting with a layout padding tick
+ i = 1;
+ while (i < height - 1) {
+ (*outTop)++;
+ i++;
+ int tick = tick_type(rows[i] + offset, transparent, outError);
+ if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+ break;
+ }
+ }
+ }
+
+ // Look for bottom tick
+ if (TICK_TYPE_LAYOUT_BOUNDS == tick_type(rows[height - 2] + offset, transparent, outError)) {
+ // Ending with a layout padding tick
+ i = height - 2;
+ while (i > 1) {
+ (*outBottom)++;
+ i--;
+ int tick = tick_type(rows[i] + offset, transparent, outError);
+ if (tick != TICK_TYPE_LAYOUT_BOUNDS) {
+ break;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+
static uint32_t get_color(
png_bytepp rows, int left, int top, int right, int bottom)
{
@@ -353,6 +466,9 @@
image->info9Patch.paddingLeft = image->info9Patch.paddingRight =
image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
+ image->layoutBoundsLeft = image->layoutBoundsRight =
+ image->layoutBoundsTop = image->layoutBoundsBottom = 0;
+
png_bytep p = image->rows[0];
bool transparent = p[3] == 0;
bool hasColor = false;
@@ -408,6 +524,25 @@
goto getout;
}
+ // Find left and right of layout padding...
+ get_horizontal_layout_bounds_ticks(image->rows[H-1], W, transparent, false,
+ &image->layoutBoundsLeft,
+ &image->layoutBoundsRight, &errorMsg);
+
+ get_vertical_layout_bounds_ticks(image->rows, (W-1)*4, H, transparent, false,
+ &image->layoutBoundsTop,
+ &image->layoutBoundsBottom, &errorMsg);
+
+ image->haveLayoutBounds = image->layoutBoundsLeft != 0
+ || image->layoutBoundsRight != 0
+ || image->layoutBoundsTop != 0
+ || image->layoutBoundsBottom != 0;
+
+ if (image->haveLayoutBounds) {
+ NOISY(printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
+ image->layoutBoundsRight, image->layoutBoundsBottom));
+ }
+
// Copy patch data into image
image->info9Patch.numXDivs = numXDivs;
image->info9Patch.numYDivs = numYDivs;
@@ -845,8 +980,9 @@
int bit_depth, interlace_type, compression_type;
int i;
- png_unknown_chunk unknowns[1];
+ png_unknown_chunk unknowns[2];
unknowns[0].data = NULL;
+ unknowns[1].data = NULL;
png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * png_sizeof(png_bytep));
if (outRows == (png_bytepp) 0) {
@@ -916,23 +1052,42 @@
}
if (imageInfo.is9Patch) {
+ int chunk_count = 1 + (imageInfo.haveLayoutBounds ? 1 : 0);
+ int p_index = imageInfo.haveLayoutBounds ? 1 : 0;
+ int b_index = 0;
+ png_byte *chunk_names = imageInfo.haveLayoutBounds
+ ? (png_byte*)"npLb\0npTc\0"
+ : (png_byte*)"npTc";
NOISY(printf("Adding 9-patch info...\n"));
- strcpy((char*)unknowns[0].name, "npTc");
- unknowns[0].data = (png_byte*)imageInfo.info9Patch.serialize();
- unknowns[0].size = imageInfo.info9Patch.serializedSize();
+ strcpy((char*)unknowns[p_index].name, "npTc");
+ unknowns[p_index].data = (png_byte*)imageInfo.info9Patch.serialize();
+ unknowns[p_index].size = imageInfo.info9Patch.serializedSize();
// TODO: remove the check below when everything works
- checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[0].data);
+ checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data);
+
+ if (imageInfo.haveLayoutBounds) {
+ int chunk_size = sizeof(png_uint_32) * 4;
+ strcpy((char*)unknowns[b_index].name, "npLb");
+ unknowns[b_index].data = (png_byte*) calloc(chunk_size, 1);
+ memcpy(unknowns[b_index].data, &imageInfo.layoutBoundsLeft, chunk_size);
+ unknowns[b_index].size = chunk_size;
+ }
+
png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
- (png_byte*)"npTc", 1);
- png_set_unknown_chunks(write_ptr, write_info, unknowns, 1);
+ chunk_names, chunk_count);
+ png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count);
// XXX I can't get this to work without forcibly changing
// the location to what I want... which apparently is supposed
// to be a private API, but everything else I have tried results
// in the location being set to what I -last- wrote so I never
// get written. :p
png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
+ if (imageInfo.haveLayoutBounds) {
+ png_set_unknown_chunk_location(write_ptr, write_info, 1, PNG_HAVE_PLTE);
+ }
}
+
png_write_info(write_ptr, write_info);
png_bytepp rows;
@@ -954,6 +1109,7 @@
}
free(outRows);
free(unknowns[0].data);
+ free(unknowns[1].data);
png_get_IHDR(write_ptr, write_info, &width, &height,
&bit_depth, &color_type, &interlace_type,
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index eadec02..b76b8cf 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -524,7 +524,8 @@
int nativeInt = sManager.addNewDelegate(delegate);
// and create/return a new Bitmap with it
- return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/, density);
+ return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/,
+ density);
}
/**
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 97afc81..a477fd1 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -453,9 +453,8 @@
public SipSessionGroupExt(SipProfile localProfile,
PendingIntent incomingCallPendingIntent,
ISipSessionListener listener) throws SipException {
- String password = localProfile.getPassword();
- SipProfile p = duplicate(localProfile);
- mSipGroup = createSipSessionGroup(mLocalIp, p, password);
+ mSipGroup = new SipSessionGroup(duplicate(localProfile),
+ localProfile.getPassword(), mTimer, mMyWakeLock);
mIncomingCallPendingIntent = incomingCallPendingIntent;
mAutoRegistration.setListener(listener);
}
@@ -478,27 +477,6 @@
mSipGroup.setWakeupTimer(timer);
}
- // network connectivity is tricky because network can be disconnected
- // at any instant so need to deal with exceptions carefully even when
- // you think you are connected
- private SipSessionGroup createSipSessionGroup(String localIp,
- SipProfile localProfile, String password) throws SipException {
- try {
- return new SipSessionGroup(localIp, localProfile, password,
- mTimer, mMyWakeLock);
- } catch (IOException e) {
- // network disconnected
- Log.w(TAG, "createSipSessionGroup(): network disconnected?");
- if (localIp != null) {
- return createSipSessionGroup(null, localProfile, password);
- } else {
- // recursive
- Log.wtf(TAG, "impossible! recursive!");
- throw new RuntimeException("createSipSessionGroup");
- }
- }
- }
-
private SipProfile duplicate(SipProfile p) {
try {
return new SipProfile.Builder(p).setPassword("*").build();
@@ -530,7 +508,7 @@
throws SipException {
mSipGroup.onConnectivityChanged();
if (connected) {
- resetGroup(mLocalIp);
+ mSipGroup.reset();
if (mOpenedToReceiveCalls) openToReceiveCalls();
} else {
// close mSipGroup but remember mOpenedToReceiveCalls
@@ -541,22 +519,6 @@
}
}
- private void resetGroup(String localIp) throws SipException {
- try {
- mSipGroup.reset(localIp);
- } catch (IOException e) {
- // network disconnected
- Log.w(TAG, "resetGroup(): network disconnected?");
- if (localIp != null) {
- resetGroup(null); // reset w/o local IP
- } else {
- // recursive
- Log.wtf(TAG, "impossible!");
- throw new RuntimeException("resetGroup");
- }
- }
- }
-
public void close() {
mOpenedToReceiveCalls = false;
mSipGroup.close();
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 877a0a4..6acd456 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -40,6 +40,7 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramSocket;
+import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.Collection;
@@ -47,13 +48,11 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
-import java.util.TooManyListenersException;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
-import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.ObjectInUseException;
import javax.sip.RequestEvent;
@@ -132,18 +131,17 @@
private int mExternalPort;
/**
- * @param myself the local profile with password crossed out
+ * @param profile the local profile with password crossed out
* @param password the password of the profile
* @throws IOException if cannot assign requested address
*/
- public SipSessionGroup(String localIp, SipProfile myself, String password,
- SipWakeupTimer timer, SipWakeLock wakeLock) throws SipException,
- IOException {
- mLocalProfile = myself;
+ public SipSessionGroup(SipProfile profile, String password,
+ SipWakeupTimer timer, SipWakeLock wakeLock) throws SipException {
+ mLocalProfile = profile;
mPassword = password;
mWakeupTimer = timer;
mWakeLock = wakeLock;
- reset(localIp);
+ reset();
}
// TODO: remove this method once SipWakeupTimer can better handle variety
@@ -152,43 +150,64 @@
mWakeupTimer = timer;
}
- synchronized void reset(String localIp) throws SipException, IOException {
- mLocalIp = localIp;
- if (localIp == null) return;
-
- SipProfile myself = mLocalProfile;
- SipFactory sipFactory = SipFactory.getInstance();
+ synchronized void reset() throws SipException {
Properties properties = new Properties();
+
+ String protocol = mLocalProfile.getProtocol();
+ int port = mLocalProfile.getPort();
+ String server = mLocalProfile.getProxyAddress();
+
+ if (!TextUtils.isEmpty(server)) {
+ properties.setProperty("javax.sip.OUTBOUND_PROXY",
+ server + ':' + port + '/' + protocol);
+ } else {
+ server = mLocalProfile.getSipDomain();
+ }
+ if (server.startsWith("[") && server.endsWith("]")) {
+ server = server.substring(1, server.length() - 1);
+ }
+
+ String local = null;
+ try {
+ for (InetAddress remote : InetAddress.getAllByName(server)) {
+ DatagramSocket socket = new DatagramSocket();
+ socket.connect(remote, port);
+ if (socket.isConnected()) {
+ local = socket.getLocalAddress().getHostAddress();
+ port = socket.getLocalPort();
+ socket.close();
+ break;
+ }
+ socket.close();
+ }
+ } catch (Exception e) {
+ // ignore.
+ }
+ if (local == null) {
+ // We are unable to reach the server. Just bail out.
+ return;
+ }
+
+ close();
+ mLocalIp = local;
+
properties.setProperty("javax.sip.STACK_NAME", getStackName());
properties.setProperty(
"gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE);
- String outboundProxy = myself.getProxyAddress();
- if (!TextUtils.isEmpty(outboundProxy)) {
- Log.v(TAG, "outboundProxy is " + outboundProxy);
- properties.setProperty("javax.sip.OUTBOUND_PROXY", outboundProxy
- + ":" + myself.getPort() + "/" + myself.getProtocol());
- }
- SipStack stack = mSipStack = sipFactory.createSipStack(properties);
-
+ mSipStack = SipFactory.getInstance().createSipStack(properties);
try {
- SipProvider provider = stack.createSipProvider(
- stack.createListeningPoint(localIp, allocateLocalPort(),
- myself.getProtocol()));
+ SipProvider provider = mSipStack.createSipProvider(
+ mSipStack.createListeningPoint(local, port, protocol));
provider.addSipListener(this);
- mSipHelper = new SipHelper(stack, provider);
- } catch (InvalidArgumentException e) {
- throw new IOException(e.getMessage());
- } catch (TooManyListenersException e) {
- // must never happen
- throw new SipException("SipSessionGroup constructor", e);
+ mSipHelper = new SipHelper(mSipStack, provider);
+ } catch (SipException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new SipException("failed to initialize SIP stack", e);
}
- Log.d(TAG, " start stack for " + myself.getUriString());
- stack.start();
- mCallReceiverSession = null;
- mSessionMap.clear();
-
- resetExternalAddress();
+ Log.d(TAG, " start stack for " + mLocalProfile.getUriString());
+ mSipStack.start();
}
synchronized void onConnectivityChanged() {
@@ -234,6 +253,7 @@
mSipStack = null;
mSipHelper = null;
}
+ resetExternalAddress();
}
public synchronized boolean isClosed() {
@@ -257,17 +277,6 @@
return (isClosed() ? null : new SipSessionImpl(listener));
}
- private static int allocateLocalPort() throws SipException {
- try {
- DatagramSocket s = new DatagramSocket();
- int localPort = s.getLocalPort();
- s.close();
- return localPort;
- } catch (IOException e) {
- throw new SipException("allocateLocalPort()", e);
- }
- }
-
synchronized boolean containsSession(String callId) {
return mSessionMap.containsKey(callId);
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 2fc6c20..c7f6bf0 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -380,6 +380,7 @@
mHandler = new P2pHandler(looper);
mChannelListener = l;
}
+ private final static int INVALID_LISTENER_KEY = 0;
private ChannelListener mChannelListener;
private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>();
private Object mListenerMapLock = new Object();
@@ -450,16 +451,19 @@
}
int putListener(Object listener) {
- if (listener == null) return 0;
+ if (listener == null) return INVALID_LISTENER_KEY;
int key;
synchronized (mListenerMapLock) {
- key = mListenerKey++;
+ do {
+ key = mListenerKey++;
+ } while (key == INVALID_LISTENER_KEY);
mListenerMap.put(key, listener);
}
return key;
}
Object getListener(int key) {
+ if (key == INVALID_LISTENER_KEY) return null;
synchronized (mListenerMapLock) {
return mListenerMap.remove(key);
}