diff options
29 files changed, 324 insertions, 59 deletions
diff --git a/api/current.txt b/api/current.txt index 8ec773241e54..0c3a0c4a21e2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2102,6 +2102,7 @@ package android.app { method public abstract void setCustomView(android.view.View); method public abstract void setCustomView(android.view.View, android.app.ActionBar.LayoutParams); method public abstract void setCustomView(int); + method public abstract void setDisplayDisableHomeEnabled(boolean); method public abstract void setDisplayHomeAsUpEnabled(boolean); method public abstract void setDisplayOptions(int); method public abstract void setDisplayOptions(int, int); @@ -2121,6 +2122,7 @@ package android.app { method public abstract void setTitle(java.lang.CharSequence); method public abstract void setTitle(int); method public abstract void show(); + field public static final int DISPLAY_DISABLE_HOME = 32; // 0x20 field public static final int DISPLAY_HOME_AS_UP = 4; // 0x4 field public static final int DISPLAY_SHOW_CUSTOM = 16; // 0x10 field public static final int DISPLAY_SHOW_HOME = 2; // 0x2 diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index a9e84d7e9a31..cac06ec54eeb 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -107,6 +107,18 @@ public abstract class ActionBar { public static final int DISPLAY_SHOW_CUSTOM = 0x10; /** + * Disable the 'home' element. This may be combined with + * {@link #DISPLAY_SHOW_HOME} to create a non-focusable/non-clickable + * 'home' element. Useful for a level of your app's navigation hierarchy + * where clicking 'home' doesn't do anything. + * + * @see #setDisplayOptions(int) + * @see #setDisplayOptions(int, int) + * @see #setDisplayDisableHomeEnabled(boolean) + */ + public static final int DISPLAY_DISABLE_HOME = 0x20; + + /** * Set the action bar into custom navigation mode, supplying a view * for custom navigation. * @@ -393,6 +405,21 @@ public abstract class ActionBar { public abstract void setDisplayShowCustomEnabled(boolean showCustom); /** + * Set whether the 'home' affordance on the action bar should be disabled. + * If set, the 'home' element will not be focusable or clickable, useful if + * the user is at the top level of the app's navigation hierarchy. + * + * <p>To set several display options at once, see the setDisplayOptions methods. + * + * @param disableHome true to disable the 'home' element. + * + * @see #setDisplayOptions(int) + * @see #setDisplayOptions(int, int) + * @see #DISPLAY_DISABLE_HOME + */ + public abstract void setDisplayDisableHomeEnabled(boolean disableHome); + + /** * Set the ActionBar's background. * * @param d Background drawable diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 96520858fe95..dccfa6c537fb 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -42,8 +42,10 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.view.animation.DecelerateInterpolator; +import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import android.widget.SpinnerAdapter; @@ -71,6 +73,7 @@ public class ActionBarImpl extends ActionBar { private ActionBarContextView mUpperContextView; private LinearLayout mLowerContextView; private View mContentView; + private ViewGroup mExternalTabView; private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>(); @@ -172,6 +175,18 @@ public class ActionBarImpl extends ActionBar { mActionView.setContextView(mUpperContextView); mContextDisplayMode = mLowerContextView == null ? CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT; + + if (!mActionView.hasEmbeddedTabs()) { + HorizontalScrollView tabScroller = new HorizontalScrollView(mContext); + ViewGroup tabContainer = mActionView.createTabContainer(); + tabScroller.setHorizontalFadingEdgeEnabled(true); + tabScroller.addView(tabContainer); + tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ? + View.VISIBLE : View.GONE); + mActionView.setExternalTabLayout(tabContainer); + mContainerView.setTabContainer(tabScroller); + mExternalTabView = tabScroller; + } } /** @@ -239,6 +254,11 @@ public class ActionBarImpl extends ActionBar { } @Override + public void setDisplayDisableHomeEnabled(boolean disableHome) { + setDisplayOptions(disableHome ? DISPLAY_DISABLE_HOME : 0, DISPLAY_DISABLE_HOME); + } + + @Override public void setTitle(int resId) { setTitle(mContext.getString(resId)); } @@ -533,7 +553,7 @@ public class ActionBarImpl extends ActionBar { final int count = mContainerView.getChildCount(); for (int i = 0; i < count; i++) { final View child = mContainerView.getChildAt(i); - if (i == viewIndex) { + if (i == viewIndex || child == mContainerView.getTabContainer()) { continue; } @@ -840,11 +860,17 @@ public class ActionBarImpl extends ActionBar { case NAVIGATION_MODE_TABS: mSavedTabPosition = getSelectedNavigationIndex(); selectTab(null); + if (!mActionView.hasEmbeddedTabs()) { + mExternalTabView.setVisibility(View.GONE); + } break; } mActionView.setNavigationMode(mode); switch (mode) { case NAVIGATION_MODE_TABS: + if (!mActionView.hasEmbeddedTabs()) { + mExternalTabView.setVisibility(View.VISIBLE); + } if (mSavedTabPosition != INVALID_POSITION) { setSelectedNavigationItem(mSavedTabPosition); mSavedTabPosition = INVALID_POSITION; diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index c9b0ec9e838d..3deb036ea89a 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.View; import android.widget.FrameLayout; /** @@ -29,6 +30,7 @@ import android.widget.FrameLayout; */ public class ActionBarContainer extends FrameLayout { private boolean mIsTransitioning; + private View mTabContainer; public ActionBarContainer(Context context) { this(context, null); @@ -65,6 +67,44 @@ public class ActionBarContainer extends FrameLayout { @Override public boolean onTouchEvent(MotionEvent ev) { super.onTouchEvent(ev); + + // An action bar always eats touch events. return true; } + + public void setTabContainer(View tabView) { + if (mTabContainer != null) { + removeView(mTabContainer); + } + mTabContainer = tabView; + addView(tabView); + } + + public View getTabContainer() { + return mTabContainer; + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { + final int mode = MeasureSpec.getMode(heightMeasureSpec); + if (mode == MeasureSpec.AT_MOST) { + final int measuredHeight = getMeasuredHeight(); + final int maxHeight = MeasureSpec.getSize(heightMeasureSpec); + setMeasuredDimension(getMeasuredWidth(), + Math.min(measuredHeight + mTabContainer.getMeasuredHeight(), maxHeight)); + } + } + } + + @Override + public void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { + final int containerHeight = getMeasuredHeight(); + mTabContainer.layout(l, containerHeight - mTabContainer.getMeasuredHeight(), + r, containerHeight); + } + } } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index fa8eb5141674..a572e1168372 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -97,7 +97,7 @@ public class ActionBarView extends ViewGroup { private Spinner mSpinner; private LinearLayout mListNavLayout; private HorizontalScrollView mTabScrollView; - private LinearLayout mTabLayout; + private ViewGroup mTabLayout; private View mCustomNavView; private ProgressBar mProgressView; private ProgressBar mIndeterminateProgressView; @@ -112,6 +112,7 @@ public class ActionBarView extends ViewGroup { private boolean mShowMenu; private boolean mUserTitle; + private boolean mIncludeTabs; private MenuBuilder mOptionsMenu; private ActionMenuView mMenuView; @@ -201,6 +202,8 @@ public class ActionBarView extends ViewGroup { mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0); mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0); + mIncludeTabs = a.getBoolean(R.styleable.ActionBar_embeddedTabs, true); + setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT)); final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0); @@ -242,6 +245,14 @@ public class ActionBarView extends ViewGroup { addView(mIndeterminateProgressView); } + public boolean hasEmbeddedTabs() { + return mIncludeTabs; + } + + public void setExternalTabLayout(ViewGroup tabLayout) { + mTabLayout = tabLayout; + } + @Override public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) { // No starting an action mode for an action bar child! (Where would it go?) @@ -389,6 +400,12 @@ public class ActionBarView extends ViewGroup { public void setDisplayOptions(int options) { final int flagsChanged = options ^ mDisplayOptions; mDisplayOptions = options; + + if ((flagsChanged & ActionBar.DISPLAY_DISABLE_HOME) != 0) { + final boolean disableHome = (options & ActionBar.DISPLAY_DISABLE_HOME) != 0; + mHomeLayout.setEnabled(!disableHome); + } + if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) { final int vis = (options & ActionBar.DISPLAY_SHOW_HOME) != 0 ? VISIBLE : GONE; mHomeLayout.setVisibility(vis); @@ -477,7 +494,7 @@ public class ActionBarView extends ViewGroup { } break; case ActionBar.NAVIGATION_MODE_TABS: - if (mTabLayout != null) { + if (mTabScrollView != null) { removeView(mTabScrollView); } } @@ -502,7 +519,9 @@ public class ActionBarView extends ViewGroup { break; case ActionBar.NAVIGATION_MODE_TABS: ensureTabsExist(); - addView(mTabScrollView); + if (mTabScrollView != null) { + addView(mTabScrollView); + } break; } mNavigationMode = mode; @@ -511,15 +530,24 @@ public class ActionBarView extends ViewGroup { } private void ensureTabsExist() { + if (!mIncludeTabs) return; + if (mTabScrollView == null) { mTabScrollView = new HorizontalScrollView(getContext()); mTabScrollView.setHorizontalFadingEdgeEnabled(true); - mTabLayout = new LinearLayout(getContext(), null, - com.android.internal.R.attr.actionBarTabBarStyle); + mTabLayout = createTabContainer(); mTabScrollView.addView(mTabLayout); } } + public ViewGroup createTabContainer() { + ViewGroup result = new LinearLayout(getContext(), null, + com.android.internal.R.attr.actionBarTabBarStyle); + result.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, + mContentHeight)); + return result; + } + public void setDropdownAdapter(SpinnerAdapter adapter) { mSpinnerAdapter = adapter; if (mSpinner != null) { diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp index df017f5d42c3..26aa7d9af7cc 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -399,6 +399,14 @@ struct GlyphRun { int isRTL; }; +void static reverseGlyphArray(jchar* glyphs, size_t count) { + for (size_t i = 0; i < count / 2; i++) { + jchar temp = glyphs[i]; + glyphs[i] = glyphs[count - 1 - i]; + glyphs[count - 1 - i] = temp; + } +} + void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags, jfloat* outAdvances, jfloat* outTotalAdvance, @@ -424,6 +432,10 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar #endif computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount); + + if (forceRTL && *outGlyphsCount > 1) { + reverseGlyphArray(*outGlyphs, *outGlyphsCount); + } } else { UBiDi* bidi = ubidi_open(); if (bidi) { @@ -438,71 +450,81 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar #if DEBUG_GLYPHS LOGD("computeValuesWithHarfbuzz -- dirFlags=%d run-count=%d paraDir=%d", dirFlags, rc, paraDir); #endif - if (rc == 1 || !U_SUCCESS(status)) { computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount); - ubidi_close(bidi); - return; - } - size_t runIndex = 0; - Vector<GlyphRun> glyphRuns; - for (size_t i = 0; i < rc; ++i) { - int32_t startRun; - int32_t lengthRun; - UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun); - - int newFlags = (runDir == UBIDI_RTL) ? kDirection_RTL : kDirection_LTR; - jfloat runTotalAdvance = 0; + if (dirFlags == 1 && *outGlyphsCount > 1) { + reverseGlyphArray(*outGlyphs, *outGlyphsCount); + } + } else { + Vector<GlyphRun> glyphRuns; jchar* runGlyphs; size_t runGlyphsCount = 0; - + size_t runIndex = 0; + for (size_t i = 0; i < rc; ++i) { + int32_t startRun; + int32_t lengthRun; + UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun); + + int newFlags = (runDir == UBIDI_RTL) ? kDirection_RTL : kDirection_LTR; + jfloat runTotalAdvance = 0; #if DEBUG_GLYPHS - LOGD("computeValuesWithHarfbuzz -- run-start=%d run-len=%d newFlags=%d", - startRun, lengthRun, newFlags); + LOGD("computeValuesWithHarfbuzz -- run-start=%d run-len=%d newFlags=%d", + startRun, lengthRun, newFlags); #endif - computeRunValuesWithHarfbuzz(paint, chars, startRun, - lengthRun, contextCount, newFlags, - outAdvances + runIndex, &runTotalAdvance, - &runGlyphs, &runGlyphsCount); + computeRunValuesWithHarfbuzz(paint, chars, startRun, + lengthRun, contextCount, newFlags, + outAdvances + runIndex, &runTotalAdvance, + &runGlyphs, &runGlyphsCount); - runIndex += lengthRun; - - *outTotalAdvance += runTotalAdvance; - *outGlyphsCount += runGlyphsCount; + runIndex += lengthRun; + *outTotalAdvance += runTotalAdvance; + *outGlyphsCount += runGlyphsCount; #if DEBUG_GLYPHS - LOGD("computeValuesWithHarfbuzz -- run=%d run-glyphs-count=%d", - i, runGlyphsCount); - for (size_t j = 0; j < runGlyphsCount; j++) { - LOGD(" -- glyphs[%d]=%d", j, runGlyphs[j]); - } -#endif - glyphRuns.push(GlyphRun(runGlyphs, runGlyphsCount, newFlags)); - } - -#if DEBUG_GLYPHS - LOGD("computeValuesWithHarfbuzz -- total-glyphs-count=%d", *outGlyphsCount); + LOGD("computeValuesWithHarfbuzz -- run=%d run-glyphs-count=%d", + i, runGlyphsCount); + for (size_t j = 0; j < runGlyphsCount; j++) { + LOGD(" -- glyphs[%d]=%d", j, runGlyphs[j]); + } #endif - *outGlyphs = new jchar[*outGlyphsCount]; - jchar* glyphs = *outGlyphs; - for (size_t i = 0; i < glyphRuns.size(); i++) { - const GlyphRun& glyphRun = glyphRuns.itemAt(i); - if (glyphRun.isRTL) { - for (size_t n = 0; n < glyphRun.glyphsCount; n++) { - glyphs[glyphRun.glyphsCount - n - 1] = glyphRun.glyphs[n]; + glyphRuns.push(GlyphRun(runGlyphs, runGlyphsCount, newFlags)); + } + *outGlyphs = new jchar[*outGlyphsCount]; + + jchar* glyphs = *outGlyphs; + for (size_t i = 0; i < glyphRuns.size(); i++) { + const GlyphRun& glyphRun = glyphRuns.itemAt(i); + if (glyphRun.isRTL) { + for (size_t n = 0; n < glyphRun.glyphsCount; n++) { + glyphs[glyphRun.glyphsCount - n - 1] = glyphRun.glyphs[n]; + } + } else { + memcpy(glyphs, glyphRun.glyphs, glyphRun.glyphsCount * sizeof(jchar)); } - } else { - memcpy(glyphs, glyphRun.glyphs, glyphRun.glyphsCount * sizeof(jchar)); + glyphs += glyphRun.glyphsCount; + delete[] glyphRun.glyphs; } - glyphs += glyphRun.glyphsCount; - delete[] glyphRun.glyphs; } } ubidi_close(bidi); + } else { + // Cannot run BiDi, just consider one Run +#if DEBUG_GLYPHS + LOGD("computeValuesWithHarfbuzz -- cannot run BiDi, considering only one Run"); +#endif + computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, + outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount); + + if (dirFlags == 1 && *outGlyphsCount > 1) { + reverseGlyphArray(*outGlyphs, *outGlyphsCount); + } } } +#if DEBUG_GLYPHS + LOGD("computeValuesWithHarfbuzz -- total-glyphs-count=%d", *outGlyphsCount); +#endif } void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, diff --git a/core/res/res/drawable-hdpi/btn_cab_done_default_holo.9.png b/core/res/res/drawable-hdpi/btn_cab_done_default_holo.9.png Binary files differindex c66716fab76e..f5e60541810b 100644 --- a/core/res/res/drawable-hdpi/btn_cab_done_default_holo.9.png +++ b/core/res/res/drawable-hdpi/btn_cab_done_default_holo.9.png diff --git a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo.9.png b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo.9.png Binary files differindex 0f1d9a08cbe5..1121070d5733 100644 --- a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo.9.png +++ b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo.9.png diff --git a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo.9.png b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo.9.png Binary files differindex 391227efcff8..1a072a905368 100644 --- a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo.9.png +++ b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo.9.png diff --git a/core/res/res/drawable-mdpi/btn_cab_done_default_holo.9.png b/core/res/res/drawable-mdpi/btn_cab_done_default_holo.9.png Binary files differindex 3a113ee2ea23..7af26cabea27 100644 --- a/core/res/res/drawable-mdpi/btn_cab_done_default_holo.9.png +++ b/core/res/res/drawable-mdpi/btn_cab_done_default_holo.9.png diff --git a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo.9.png b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo.9.png Binary files differindex 8496965b5f00..486c37a72c1f 100644 --- a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo.9.png +++ b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo.9.png diff --git a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo.9.png b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo.9.png Binary files differindex dc70c6a50350..3cfb4bd49764 100644 --- a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo.9.png +++ b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo.9.png diff --git a/core/res/res/drawable/btn_cab_done.xml b/core/res/res/drawable/btn_cab_done.xml index 154837d1138c..e3cf461511c0 100644 --- a/core/res/res/drawable/btn_cab_done.xml +++ b/core/res/res/drawable/btn_cab_done.xml @@ -16,11 +16,11 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_window_focused="false" android:state_enabled="true" - android:drawable="@drawable/btn_cab_done_holo" /> + android:drawable="@drawable/btn_cab_done_default_holo" /> <item android:state_pressed="true" android:drawable="@drawable/btn_cab_done_pressed_holo" /> <item android:state_focused="true" android:state_enabled="true" android:drawable="@drawable/btn_cab_done_focused_holo" /> <item android:state_enabled="true" - android:drawable="@drawable/btn_cab_done_holo" /> + android:drawable="@drawable/btn_cab_done_default_holo" /> </selector> diff --git a/core/res/res/values-w480dp/bools.xml b/core/res/res/values-w480dp/bools.xml index ea7eeb58c7d7..c202d90d403b 100644 --- a/core/res/res/values-w480dp/bools.xml +++ b/core/res/res/values-w480dp/bools.xml @@ -18,4 +18,5 @@ --> <resources> <bool name="allow_action_menu_item_text_with_icon">true</bool> + <bool name="action_bar_embed_tabs">true</bool> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index e8767d80a82c..3a44e4f73a82 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4850,6 +4850,7 @@ <flag name="homeAsUp" value="0x4" /> <flag name="showTitle" value="0x8" /> <flag name="showCustom" value="0x10" /> + <flag name="disableHome" value="0x20" /> </attr> <!-- Specifies title text used for navigationMode="normal" --> <attr name="title" /> @@ -4882,6 +4883,9 @@ <!-- Specifies padding that should be applied to the left and right sides of system-provided items in the bar. --> <attr name="itemPadding" format="dimension" /> + <!-- Specifies whether tabs should be embedded within the bar itself (true) + or displayed elsewhere (false). --> + <attr name="embeddedTabs" format="boolean" /> </declare-styleable> <declare-styleable name="ActionMode"> diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml index c7dcb515cd09..6eb006f32f03 100644 --- a/core/res/res/values/bools.xml +++ b/core/res/res/values/bools.xml @@ -18,4 +18,5 @@ --> <resources> <bool name="allow_action_menu_item_text_with_icon">false</bool> + <bool name="action_bar_embed_tabs">false</bool> </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 198ff8b47cbd..8ce35f8aeccc 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1074,6 +1074,7 @@ <item name="android:progressBarStyle">@android:style/Widget.ProgressBar.Horizontal</item> <item name="android:indeterminateProgressStyle">@android:style/Widget.ProgressBar.Small</item> <item name="android:homeLayout">@android:layout/action_bar_home</item> + <item name="android:embeddedTabs">@android:bool/action_bar_embed_tabs</item> </style> <style name="Widget.ActionMode"> diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp index 2fee59c3cc1c..1809619d0262 100644 --- a/drm/drmserver/DrmManager.cpp +++ b/drm/drmserver/DrmManager.cpp @@ -101,6 +101,7 @@ status_t DrmManager::loadPlugIns(const String8& plugInDirPath) { DrmSupportInfo* info = mPlugInManager.getPlugIn(plugInPath).getSupportInfo(0); if (NULL != info) { mSupportInfoToPlugInIdMap.add(*info, plugInPath); + delete info; } } } @@ -178,6 +179,7 @@ status_t DrmManager::installDrmEngine(int uniqueId, const String8& absolutePath) DrmSupportInfo* info = rDrmEngine.getSupportInfo(0); mSupportInfoToPlugInIdMap.add(*info, absolutePath); + delete info; return DRM_NO_ERROR; } diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 4fd41472b308..77cedd5a365f 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -27,9 +27,11 @@ LOCAL_SHARED_LIBRARIES := \ libcamera_client \ libsqlite \ libmtp \ - libusbhost + libusbhost \ + libexif LOCAL_C_INCLUDES += \ + external/jhead \ external/tremor/Tremor \ frameworks/base/core/jni \ frameworks/base/media/libmedia \ diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index 585cd30ceed3..0f3c063bedae 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -35,6 +35,10 @@ #include "MtpUtils.h" #include "mtp.h" +extern "C" { +#include "jhead.h" +} + using namespace android; // ---------------------------------------------------------------------------- @@ -141,6 +145,8 @@ public: virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info); + virtual void* getThumbnail(MtpObjectHandle handle, size_t& outThumbSize); + virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, @@ -776,10 +782,67 @@ MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, info.mName = strdup((const char *)temp); env->ReleaseCharArrayElements(mStringBuffer, str, 0); + // read EXIF data for thumbnail information + if (info.mFormat == MTP_FORMAT_EXIF_JPEG || info.mFormat == MTP_FORMAT_JFIF) { + MtpString path; + int64_t length; + MtpObjectFormat format; + if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK) { + ResetJpgfile(); + // Start with an empty image information structure. + memset(&ImageInfo, 0, sizeof(ImageInfo)); + ImageInfo.FlashUsed = -1; + ImageInfo.MeteringMode = -1; + ImageInfo.Whitebalance = -1; + strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX); + if (ReadJpegFile((const char*)path, READ_METADATA)) { + Section_t* section = FindSection(M_EXIF); + if (section) { + info.mThumbCompressedSize = ImageInfo.ThumbnailSize; + info.mThumbFormat = MTP_FORMAT_EXIF_JPEG; + info.mImagePixWidth = ImageInfo.Width; + info.mImagePixHeight = ImageInfo.Height; + } + } + DiscardData(); + } + } + checkAndClearExceptionFromCallback(env, __FUNCTION__); return MTP_RESPONSE_OK; } +void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) { + MtpString path; + int64_t length; + MtpObjectFormat format; + void* result = NULL; + outThumbSize = 0; + + if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK + && (format == MTP_FORMAT_EXIF_JPEG || format == MTP_FORMAT_JFIF)) { + ResetJpgfile(); + // Start with an empty image information structure. + memset(&ImageInfo, 0, sizeof(ImageInfo)); + ImageInfo.FlashUsed = -1; + ImageInfo.MeteringMode = -1; + ImageInfo.Whitebalance = -1; + strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX); + if (ReadJpegFile((const char*)path, READ_METADATA)) { + Section_t* section = FindSection(M_EXIF); + if (section) { + outThumbSize = ImageInfo.ThumbnailSize; + result = malloc(outThumbSize); + if (result) + memcpy(result, section->Data + ImageInfo.ThumbnailOffset + 8, outThumbSize); + } + DiscardData(); + } + } + + return result; +} + MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 01d0a929b552..8787214f2525 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -2130,6 +2130,11 @@ static bool isCompatibleBrand(uint32_t fourcc) { FOURCC('3', 'g', 'p', '4'), FOURCC('m', 'p', '4', '1'), FOURCC('m', 'p', '4', '2'), + + // Won't promise that the following file types can be played. + // Just give these file types a chance. + FOURCC('q', 't', ' ', ' '), // Apple's QuickTime + FOURCC('M', 'S', 'N', 'V'), // Sony's PSP }; for (size_t i = 0; diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp index 0b0c80d11d8c..817eac055f72 100644 --- a/media/mtp/MtpDataPacket.cpp +++ b/media/mtp/MtpDataPacket.cpp @@ -388,6 +388,16 @@ int MtpDataPacket::writeDataHeader(int fd, uint32_t length) { int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); return (ret < 0 ? ret : 0); } + +int MtpDataPacket::writeData(int fd, void* data, uint32_t length) { + MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length + MTP_CONTAINER_HEADER_SIZE); + MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA); + int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE); + if (ret == MTP_CONTAINER_HEADER_SIZE) + ret = ::write(fd, data, length); + return (ret < 0 ? ret : 0); +} + #endif // MTP_DEVICE #ifdef MTP_HOST diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h index 577cea152f69..8a08948b43e8 100644 --- a/media/mtp/MtpDataPacket.h +++ b/media/mtp/MtpDataPacket.h @@ -100,6 +100,7 @@ public: // write our data to the given file descriptor int write(int fd); int writeDataHeader(int fd, uint32_t length); + int writeData(int fd, void* data, uint32_t length); #endif #ifdef MTP_HOST diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h index d7bde00a4dc7..4e6ac7a8a3d8 100644 --- a/media/mtp/MtpDatabase.h +++ b/media/mtp/MtpDatabase.h @@ -84,6 +84,8 @@ public: virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) = 0; + virtual void* getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) = 0; + virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index b744b5b4c84a..4a8fd3e33d91 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -50,7 +50,7 @@ static const MtpOperationCode kSupportedOperationCodes[] = { MTP_OPERATION_GET_OBJECT_HANDLES, MTP_OPERATION_GET_OBJECT_INFO, MTP_OPERATION_GET_OBJECT, -// MTP_OPERATION_GET_THUMB, + MTP_OPERATION_GET_THUMB, MTP_OPERATION_DELETE_OBJECT, MTP_OPERATION_SEND_OBJECT_INFO, MTP_OPERATION_SEND_OBJECT, @@ -370,6 +370,9 @@ bool MtpServer::handleRequest() { case MTP_OPERATION_GET_OBJECT: response = doGetObject(); break; + case MTP_OPERATION_GET_THUMB: + response = doGetThumb(); + break; case MTP_OPERATION_GET_PARTIAL_OBJECT: case MTP_OPERATION_GET_PARTIAL_OBJECT_64: response = doGetPartialObject(operation); @@ -736,6 +739,22 @@ MtpResponseCode MtpServer::doGetObject() { return MTP_RESPONSE_OK; } +MtpResponseCode MtpServer::doGetThumb() { + MtpObjectHandle handle = mRequest.getParameter(1); + size_t thumbSize; + void* thumb = mDatabase->getThumbnail(handle, thumbSize); + if (thumb) { + // send data + mData.setOperationCode(mRequest.getOperationCode()); + mData.setTransactionID(mRequest.getTransactionID()); + mData.writeData(mFD, thumb, thumbSize); + free(thumb); + return MTP_RESPONSE_OK; + } else { + return MTP_RESPONSE_GENERAL_ERROR; + } +} + MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h index b06eb287ba58..859a18e63b66 100644 --- a/media/mtp/MtpServer.h +++ b/media/mtp/MtpServer.h @@ -133,6 +133,7 @@ private: MtpResponseCode doGetObjectPropList(); MtpResponseCode doGetObjectInfo(); MtpResponseCode doGetObject(); + MtpResponseCode doGetThumb(); MtpResponseCode doGetPartialObject(MtpOperationCode operation); MtpResponseCode doSendObjectInfo(); MtpResponseCode doSendObject(); diff --git a/tests/BiDiTests/res/layout/biditest_main.xml b/tests/BiDiTests/res/layout/biditest_main.xml index 087c9a34f4a5..044a35567c81 100644 --- a/tests/BiDiTests/res/layout/biditest_main.xml +++ b/tests/BiDiTests/res/layout/biditest_main.xml @@ -43,7 +43,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:textSize="32dip" - android:text="@string/edittext_text" /> </LinearLayout> diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml index d20600e1307c..c272df18d42a 100644 --- a/tests/BiDiTests/res/values/strings.xml +++ b/tests/BiDiTests/res/values/strings.xml @@ -20,10 +20,11 @@ <string name="normal_long_text">mmmmmmmmmmmmmmmmmmmmmmmm</string> <string name="normal_long_text_2">nnnnnnnnnnnnnnnnnnnnnnnn</string> <string name="normal_long_text_3">Notify me when an open network is available</string> - <string name="arabic_text">لا انا hello world</string> + <string name="arabic_text">لا انا hello Arabic</string> <string name="chinese_text">利比亚局势或影响美俄关系发展</string> <string name="italic_text">Italic String</string> <string name="bold_text">Bold String - other text</string> <string name="bold_italic_text">Bold Italic String</string> <string name="mixed_text_1">he said in Arabic: لا. Wow this is cool</string> + <string name="hebrew_text">םמ</string> </resources>
\ No newline at end of file diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java index 76031a81d1ec..4f17e529eaae 100644 --- a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java @@ -50,6 +50,7 @@ public class BiDiTestView extends View { private String ARABIC_TEXT; private String CHINESE_TEXT; private String MIXED_TEXT_1; + private String HEBREW_TEXT; private Typeface typeface; @@ -81,6 +82,7 @@ public class BiDiTestView extends View { ARABIC_TEXT = context.getString(R.string.arabic_text); CHINESE_TEXT = context.getString(R.string.chinese_text); MIXED_TEXT_1 = context.getString(R.string.mixed_text_1); + HEBREW_TEXT = context.getString(R.string.hebrew_text); typeface = paint.getTypeface(); paint.setAntiAlias(true); @@ -95,7 +97,9 @@ public class BiDiTestView extends View { public void onDraw(Canvas canvas) { drawInsideRect(canvas, Color.BLACK); - int deltaX = testString(canvas, NORMAL_TEXT, ORIGIN, ORIGIN, + int deltaX = 0; + + deltaX = testString(canvas, NORMAL_TEXT, ORIGIN, ORIGIN, paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); deltaX += testString(canvas, ITALIC_TEXT, ORIGIN + deltaX, ORIGIN, @@ -130,6 +134,10 @@ public class BiDiTestView extends View { // Test Mixed (English and Arabic) deltaX = testString(canvas, MIXED_TEXT_1, ORIGIN, ORIGIN + 12 * currentTextSize, paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); + + // Test Hebrew + deltaX = testString(canvas, HEBREW_TEXT, ORIGIN, ORIGIN + 14 * currentTextSize, + paint, typeface, false, false, Paint.DIRECTION_RTL, currentTextSize); } private int testString(Canvas canvas, String text, int x, int y, Paint paint, Typeface typeface, |