summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk6
-rw-r--r--api/current.txt469
-rw-r--r--core/java/android/app/AppOpsManager.java13
-rw-r--r--core/java/android/app/ContextImpl.java9
-rw-r--r--core/java/android/content/Context.java13
-rw-r--r--core/java/android/content/pm/PackageManager.java18
-rw-r--r--core/java/android/content/pm/PackageParser.java170
-rw-r--r--core/java/android/provider/Settings.java6
-rw-r--r--core/java/android/service/trust/TrustAgentService.java52
-rwxr-xr-xcore/java/android/text/format/DateFormat.java2
-rw-r--r--core/java/android/util/TimeUtils.java5
-rw-r--r--core/java/android/view/View.java6
-rw-r--r--core/java/android/view/ViewAnimationUtils.java2
-rw-r--r--core/java/android/webkit/WebChromeClient.java6
-rw-r--r--core/java/android/widget/TextView.java2
-rw-r--r--core/java/android/widget/VideoView.java2
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp137
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.pngbin487 -> 468 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.pngbin487 -> 468 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.pngbin447 -> 443 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.pngbin447 -> 443 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.pngbin503 -> 420 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.pngbin503 -> 420 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.pngbin486 -> 456 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.pngbin486 -> 458 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.pngbin548 -> 530 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.pngbin548 -> 530 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.pngbin569 -> 567 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.pngbin569 -> 567 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.pngbin577 -> 493 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.pngbin577 -> 493 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.pngbin615 -> 592 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.pngbin615 -> 608 bytes
-rw-r--r--core/res/res/drawable-hdpi/work_icon.pngbin4442 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.pngbin357 -> 351 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.pngbin357 -> 351 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.pngbin333 -> 321 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.pngbin333 -> 321 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.pngbin365 -> 320 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.pngbin365 -> 320 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.pngbin347 -> 349 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.pngbin347 -> 358 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.pngbin381 -> 376 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.pngbin381 -> 376 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.pngbin386 -> 374 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.pngbin386 -> 374 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.pngbin394 -> 364 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.pngbin394 -> 364 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.pngbin406 -> 413 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.pngbin406 -> 427 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.pngbin575 -> 547 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.pngbin575 -> 547 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.pngbin571 -> 535 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.pngbin571 -> 535 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.pngbin596 -> 505 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.pngbin596 -> 505 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.pngbin608 -> 572 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.pngbin608 -> 585 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.pngbin655 -> 660 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.pngbin655 -> 660 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.pngbin713 -> 677 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.pngbin713 -> 677 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.pngbin691 -> 620 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.pngbin691 -> 620 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.pngbin767 -> 728 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.pngbin767 -> 772 bytes
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageManagerTests.java20
-rw-r--r--data/fonts/Android.mk20
-rw-r--r--data/fonts/fallback_fonts.xml20
-rw-r--r--docs/html/auto/index.jd103
-rw-r--r--docs/html/auto/overview.jd9
-rw-r--r--docs/html/index.jd55
-rw-r--r--docs/html/jd_collections.js17
-rw-r--r--docs/html/jd_tag_helpers.js1
-rw-r--r--docs/html/preview/api-overview.jd46
-rw-r--r--docs/html/preview/index.jd3
-rw-r--r--docs/html/preview/material/get-started.jd12
-rw-r--r--docs/html/preview/material/index.jd4
-rw-r--r--docs/html/preview/preview_toc.cs37
-rw-r--r--docs/html/preview/setup-devices.jd89
-rw-r--r--docs/html/preview/setup-sdk.jd30
-rw-r--r--docs/html/preview/tv/adt-1/index.jd319
-rw-r--r--docs/html/preview/tv/adt-1/request.jd47
-rw-r--r--docs/html/preview/tv/games/index.jd70
-rw-r--r--docs/html/preview/tv/images/atv.pngbin0 -> 58390 bytes
-rw-r--r--docs/html/preview/tv/images/browsefragment.pngbin0 -> 109470 bytes
-rw-r--r--docs/html/preview/tv/images/detailsfragment.pngbin0 -> 291412 bytes
-rw-r--r--docs/html/preview/tv/images/home-recommendations.pngbin0 -> 2492651 bytes
-rw-r--r--docs/html/preview/tv/index.jd12
-rw-r--r--docs/html/preview/tv/start/hardware-features.jd174
-rw-r--r--docs/html/preview/tv/start/index.jd193
-rw-r--r--docs/html/preview/tv/ui/browse.jd217
-rw-r--r--docs/html/preview/tv/ui/details.jd230
-rw-r--r--docs/html/preview/tv/ui/in-app-search.jd119
-rw-r--r--docs/html/preview/tv/ui/index.jd44
-rw-r--r--docs/html/preview/tv/ui/layouts.jd298
-rw-r--r--docs/html/preview/tv/ui/navigation.jd144
-rw-r--r--docs/html/preview/tv/ui/recommendations.jd234
-rw-r--r--docs/html/training/tv/index.jd59
-rw-r--r--docs/html/training/tv/optimizing-layouts-tv.jd246
-rw-r--r--docs/html/training/tv/optimizing-navigation-tv.jd206
-rw-r--r--docs/html/training/tv/unsupported-features-tv.jd157
-rw-r--r--docs/html/tv/index.jd59
-rw-r--r--docs/html/wear/images/hero.jpgbin117586 -> 248879 bytes
-rw-r--r--docs/html/wear/index.jd47
-rw-r--r--graphics/java/android/graphics/drawable/Animatable.java2
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java52
-rw-r--r--graphics/java/android/graphics/drawable/AnimationDrawable.java43
-rw-r--r--graphics/java/android/graphics/drawable/BitmapDrawable.java40
-rw-r--r--graphics/java/android/graphics/drawable/ClipDrawable.java14
-rw-r--r--graphics/java/android/graphics/drawable/ColorDrawable.java70
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java36
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java29
-rw-r--r--graphics/java/android/graphics/drawable/InsetDrawable.java33
-rw-r--r--graphics/java/android/graphics/drawable/LayerDrawable.java11
-rw-r--r--graphics/java/android/graphics/drawable/MaterialProgressDrawable.java34
-rw-r--r--graphics/java/android/graphics/drawable/NinePatchDrawable.java38
-rw-r--r--graphics/java/android/graphics/drawable/PaintDrawable.java8
-rw-r--r--graphics/java/android/graphics/drawable/PictureDrawable.java16
-rw-r--r--graphics/java/android/graphics/drawable/RotateDrawable.java7
-rw-r--r--graphics/java/android/graphics/drawable/ScaleDrawable.java7
-rw-r--r--graphics/java/android/graphics/drawable/ShapeDrawable.java89
-rw-r--r--graphics/java/android/graphics/drawable/StateListDrawable.java6
-rw-r--r--graphics/java/android/graphics/drawable/TransitionDrawable.java18
-rw-r--r--graphics/java/android/graphics/drawable/VectorDrawable.java49
-rw-r--r--libs/hwui/TextureCache.cpp56
-rw-r--r--libs/hwui/TextureCache.h2
-rw-r--r--media/java/android/media/AudioManager.java117
-rw-r--r--media/java/android/media/AudioService.java37
-rw-r--r--media/java/android/media/ClosedCaptionRenderer.java1464
-rw-r--r--media/java/android/media/IAudioService.aidl2
-rw-r--r--media/java/android/media/MediaPlayer.java55
-rw-r--r--media/java/android/media/MediaRouter.java66
-rw-r--r--media/java/android/media/PlayerRecord.java2
-rw-r--r--media/java/android/media/RemoteControlClient.java802
-rw-r--r--media/java/android/media/SubtitleTrack.java6
-rw-r--r--media/java/android/media/TtmlRenderer.java47
-rw-r--r--media/java/android/media/WebVttRenderer.java36
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java2
-rw-r--r--packages/Keyguard/res/layout/keyguard_status_view.xml2
-rw-r--r--packages/Keyguard/res/values-sw600dp/dimens.xml7
-rw-r--r--packages/Keyguard/res/values/dimens.xml8
-rw-r--r--packages/Keyguard/res/values/donottranslate.xml2
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java5
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/config.xml21
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml5
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml4
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml10
-rw-r--r--packages/SystemUI/res/values/dimens.xml8
-rw-r--r--packages/SystemUI/res/values/styles.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java3
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java19
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindowManager.java70
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java8
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java13
-rw-r--r--services/core/java/com/android/server/pm/Installer.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java6
-rwxr-xr-xservices/core/java/com/android/server/pm/PackageManagerService.java247
-rw-r--r--telecomm/java/android/telecomm/PhoneApplication.java183
-rw-r--r--telecomm/java/android/telecomm/TelecommConstants.java15
-rw-r--r--telecomm/java/android/telecomm/TelecommManager.java48
-rw-r--r--telecomm/java/com/android/internal/telecomm/ITelecommService.aidl7
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java3
-rw-r--r--wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java3
-rw-r--r--wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java166
172 files changed, 5460 insertions, 3122 deletions
diff --git a/Android.mk b/Android.mk
index 24796cb..3a3756b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -705,7 +705,7 @@ $(full_target): $(framework_built) $(gen)
$(INTERNAL_PLATFORM_API_FILE): $(full_target)
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
-# ==== the private api stubs ===================================
+# ==== the system api stubs ===================================
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES)
@@ -717,11 +717,11 @@ LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-LOCAL_MODULE := private-api-stubs
+LOCAL_MODULE := system-api-stubs
LOCAL_DROIDDOC_OPTIONS:=\
$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
- -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_private_stubs_current_intermediates/src \
+ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_system_stubs_current_intermediates/src \
-showAnnotation android.annotation.SystemApi \
-nodocs
diff --git a/api/current.txt b/api/current.txt
index a09a853..2f813ac 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7041,6 +7041,7 @@ package android.content {
field public static final java.lang.String SEARCH_SERVICE = "search";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String STORAGE_SERVICE = "storage";
+ field public static final java.lang.String TELECOMM_SERVICE = "telecomm";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
field public static final java.lang.String TV_INPUT_SERVICE = "tv_input";
@@ -27675,6 +27676,7 @@ package android.telecomm {
field public static final java.lang.String ACTION_CALL_SERVICE;
field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER;
field public static final java.lang.String ACTION_CALL_SERVICE_SELECTOR;
+ field public static final java.lang.String ACTION_CHANGE_DEFAULT_PHONE = "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
@@ -27682,6 +27684,10 @@ package android.telecomm {
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
+ field public static final java.lang.String EXTRA_PACKAGE_NAME = "package";
+ }
+
+ public class TelecommManager {
}
}
@@ -33084,7 +33090,7 @@ package android.view {
method public abstract boolean onTouch(android.view.View, android.view.MotionEvent);
}
- public class ViewAnimationUtils {
+ public final class ViewAnimationUtils {
method public static final android.animation.ValueAnimator createCircularReveal(android.view.View, int, int, float, float);
}
@@ -35188,12 +35194,25 @@ package android.webkit {
method public void onRequestFocus(android.webkit.WebView);
method public void onShowCustomView(android.view.View, android.webkit.WebChromeClient.CustomViewCallback);
method public deprecated void onShowCustomView(android.view.View, int, android.webkit.WebChromeClient.CustomViewCallback);
+ method public boolean showFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams);
}
public static abstract interface WebChromeClient.CustomViewCallback {
method public abstract void onCustomViewHidden();
}
+ public static class WebChromeClient.FileChooserParams {
+ ctor public WebChromeClient.FileChooserParams();
+ field public static final int MODE_OPEN_FOLDER = 2; // 0x2
+ field public static final int MODE_OPEN_MULTIPLE = 1; // 0x1
+ field public static final int MODE_SAVE = 4; // 0x4
+ field public java.lang.String acceptTypes;
+ field public boolean capture;
+ field public java.lang.String defaultFilename;
+ field public int mode;
+ field public java.lang.String title;
+ }
+
public class WebHistoryItem implements java.lang.Cloneable {
method public android.graphics.Bitmap getFavicon();
method public java.lang.String getOriginalUrl();
@@ -37749,6 +37768,7 @@ package android.widget {
method public float getShadowDx();
method public float getShadowDy();
method public float getShadowRadius();
+ method public final boolean getShowSoftInputOnFocus();
method public java.lang.CharSequence getText();
method public static int getTextColor(android.content.Context, android.content.res.TypedArray, int);
method public final android.content.res.ColorStateList getTextColors();
@@ -37842,6 +37862,7 @@ package android.widget {
method public void setScroller(android.widget.Scroller);
method public void setSelectAllOnFocus(boolean);
method public void setShadowLayer(float, float, float, int);
+ method public final void setShowSoftInputOnFocus(boolean);
method public void setSingleLine();
method public void setSingleLine(boolean);
method public final void setSpannableFactory(android.text.Spannable.Factory);
@@ -40007,109 +40028,6 @@ package java.lang {
field public static final java.lang.Character.UnicodeBlock YI_SYLLABLES;
}
- public static final class Character.UnicodeScript extends java.lang.Enum {
- method public static java.lang.Character.UnicodeScript forName(java.lang.String);
- method public static java.lang.Character.UnicodeScript of(int);
- method public static java.lang.Character.UnicodeScript valueOf(java.lang.String);
- method public static final java.lang.Character.UnicodeScript[] values();
- enum_constant public static final java.lang.Character.UnicodeScript ARABIC;
- enum_constant public static final java.lang.Character.UnicodeScript ARMENIAN;
- enum_constant public static final java.lang.Character.UnicodeScript AVESTAN;
- enum_constant public static final java.lang.Character.UnicodeScript BALINESE;
- enum_constant public static final java.lang.Character.UnicodeScript BAMUM;
- enum_constant public static final java.lang.Character.UnicodeScript BATAK;
- enum_constant public static final java.lang.Character.UnicodeScript BENGALI;
- enum_constant public static final java.lang.Character.UnicodeScript BOPOMOFO;
- enum_constant public static final java.lang.Character.UnicodeScript BRAHMI;
- enum_constant public static final java.lang.Character.UnicodeScript BRAILLE;
- enum_constant public static final java.lang.Character.UnicodeScript BUGINESE;
- enum_constant public static final java.lang.Character.UnicodeScript BUHID;
- enum_constant public static final java.lang.Character.UnicodeScript CANADIAN_ABORIGINAL;
- enum_constant public static final java.lang.Character.UnicodeScript CARIAN;
- enum_constant public static final java.lang.Character.UnicodeScript CHAM;
- enum_constant public static final java.lang.Character.UnicodeScript CHEROKEE;
- enum_constant public static final java.lang.Character.UnicodeScript COMMON;
- enum_constant public static final java.lang.Character.UnicodeScript COPTIC;
- enum_constant public static final java.lang.Character.UnicodeScript CUNEIFORM;
- enum_constant public static final java.lang.Character.UnicodeScript CYPRIOT;
- enum_constant public static final java.lang.Character.UnicodeScript CYRILLIC;
- enum_constant public static final java.lang.Character.UnicodeScript DESERET;
- enum_constant public static final java.lang.Character.UnicodeScript DEVANAGARI;
- enum_constant public static final java.lang.Character.UnicodeScript EGYPTIAN_HIEROGLYPHS;
- enum_constant public static final java.lang.Character.UnicodeScript ETHIOPIC;
- enum_constant public static final java.lang.Character.UnicodeScript GEORGIAN;
- enum_constant public static final java.lang.Character.UnicodeScript GLAGOLITIC;
- enum_constant public static final java.lang.Character.UnicodeScript GOTHIC;
- enum_constant public static final java.lang.Character.UnicodeScript GREEK;
- enum_constant public static final java.lang.Character.UnicodeScript GUJARATI;
- enum_constant public static final java.lang.Character.UnicodeScript GURMUKHI;
- enum_constant public static final java.lang.Character.UnicodeScript HAN;
- enum_constant public static final java.lang.Character.UnicodeScript HANGUL;
- enum_constant public static final java.lang.Character.UnicodeScript HANUNOO;
- enum_constant public static final java.lang.Character.UnicodeScript HEBREW;
- enum_constant public static final java.lang.Character.UnicodeScript HIRAGANA;
- enum_constant public static final java.lang.Character.UnicodeScript IMPERIAL_ARAMAIC;
- enum_constant public static final java.lang.Character.UnicodeScript INHERITED;
- enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PAHLAVI;
- enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PARTHIAN;
- enum_constant public static final java.lang.Character.UnicodeScript JAVANESE;
- enum_constant public static final java.lang.Character.UnicodeScript KAITHI;
- enum_constant public static final java.lang.Character.UnicodeScript KANNADA;
- enum_constant public static final java.lang.Character.UnicodeScript KATAKANA;
- enum_constant public static final java.lang.Character.UnicodeScript KAYAH_LI;
- enum_constant public static final java.lang.Character.UnicodeScript KHAROSHTHI;
- enum_constant public static final java.lang.Character.UnicodeScript KHMER;
- enum_constant public static final java.lang.Character.UnicodeScript LAO;
- enum_constant public static final java.lang.Character.UnicodeScript LATIN;
- enum_constant public static final java.lang.Character.UnicodeScript LEPCHA;
- enum_constant public static final java.lang.Character.UnicodeScript LIMBU;
- enum_constant public static final java.lang.Character.UnicodeScript LINEAR_B;
- enum_constant public static final java.lang.Character.UnicodeScript LISU;
- enum_constant public static final java.lang.Character.UnicodeScript LYCIAN;
- enum_constant public static final java.lang.Character.UnicodeScript LYDIAN;
- enum_constant public static final java.lang.Character.UnicodeScript MALAYALAM;
- enum_constant public static final java.lang.Character.UnicodeScript MANDAIC;
- enum_constant public static final java.lang.Character.UnicodeScript MEETEI_MAYEK;
- enum_constant public static final java.lang.Character.UnicodeScript MONGOLIAN;
- enum_constant public static final java.lang.Character.UnicodeScript MYANMAR;
- enum_constant public static final java.lang.Character.UnicodeScript NEW_TAI_LUE;
- enum_constant public static final java.lang.Character.UnicodeScript NKO;
- enum_constant public static final java.lang.Character.UnicodeScript OGHAM;
- enum_constant public static final java.lang.Character.UnicodeScript OLD_ITALIC;
- enum_constant public static final java.lang.Character.UnicodeScript OLD_PERSIAN;
- enum_constant public static final java.lang.Character.UnicodeScript OLD_SOUTH_ARABIAN;
- enum_constant public static final java.lang.Character.UnicodeScript OLD_TURKIC;
- enum_constant public static final java.lang.Character.UnicodeScript OL_CHIKI;
- enum_constant public static final java.lang.Character.UnicodeScript ORIYA;
- enum_constant public static final java.lang.Character.UnicodeScript OSMANYA;
- enum_constant public static final java.lang.Character.UnicodeScript PHAGS_PA;
- enum_constant public static final java.lang.Character.UnicodeScript PHOENICIAN;
- enum_constant public static final java.lang.Character.UnicodeScript REJANG;
- enum_constant public static final java.lang.Character.UnicodeScript RUNIC;
- enum_constant public static final java.lang.Character.UnicodeScript SAMARITAN;
- enum_constant public static final java.lang.Character.UnicodeScript SAURASHTRA;
- enum_constant public static final java.lang.Character.UnicodeScript SHAVIAN;
- enum_constant public static final java.lang.Character.UnicodeScript SINHALA;
- enum_constant public static final java.lang.Character.UnicodeScript SUNDANESE;
- enum_constant public static final java.lang.Character.UnicodeScript SYLOTI_NAGRI;
- enum_constant public static final java.lang.Character.UnicodeScript SYRIAC;
- enum_constant public static final java.lang.Character.UnicodeScript TAGALOG;
- enum_constant public static final java.lang.Character.UnicodeScript TAGBANWA;
- enum_constant public static final java.lang.Character.UnicodeScript TAI_LE;
- enum_constant public static final java.lang.Character.UnicodeScript TAI_THAM;
- enum_constant public static final java.lang.Character.UnicodeScript TAI_VIET;
- enum_constant public static final java.lang.Character.UnicodeScript TAMIL;
- enum_constant public static final java.lang.Character.UnicodeScript TELUGU;
- enum_constant public static final java.lang.Character.UnicodeScript THAANA;
- enum_constant public static final java.lang.Character.UnicodeScript THAI;
- enum_constant public static final java.lang.Character.UnicodeScript TIBETAN;
- enum_constant public static final java.lang.Character.UnicodeScript TIFINAGH;
- enum_constant public static final java.lang.Character.UnicodeScript UGARITIC;
- enum_constant public static final java.lang.Character.UnicodeScript UNKNOWN;
- enum_constant public static final java.lang.Character.UnicodeScript VAI;
- enum_constant public static final java.lang.Character.UnicodeScript YI;
- }
-
public final class Class implements java.lang.reflect.AnnotatedElement java.lang.reflect.GenericDeclaration java.io.Serializable java.lang.reflect.Type {
method public java.lang.Class<? extends U> asSubclass(java.lang.Class<U>);
method public T cast(java.lang.Object);
@@ -41950,13 +41868,11 @@ package java.net {
method public java.lang.String getValue();
method public int getVersion();
method public boolean hasExpired();
- method public boolean isHttpOnly();
method public static java.util.List<java.net.HttpCookie> parse(java.lang.String);
method public void setComment(java.lang.String);
method public void setCommentURL(java.lang.String);
method public void setDiscard(boolean);
method public void setDomain(java.lang.String);
- method public void setHttpOnly(boolean);
method public void setMaxAge(long);
method public void setPath(java.lang.String);
method public void setPortlist(java.lang.String);
@@ -42347,11 +42263,6 @@ package java.net {
method public abstract java.net.SocketImpl createSocketImpl();
}
- public abstract interface SocketOption {
- method public abstract java.lang.String name();
- method public abstract java.lang.Class<T> type();
- }
-
public abstract interface SocketOptions {
method public abstract java.lang.Object getOption(int) throws java.net.SocketException;
method public abstract void setOption(int, java.lang.Object) throws java.net.SocketException;
@@ -42382,21 +42293,6 @@ package java.net {
ctor public SocketTimeoutException(java.lang.String);
}
- public final class StandardSocketOptions {
- ctor public StandardSocketOptions();
- field public static final java.net.SocketOption IP_MULTICAST_IF;
- field public static final java.net.SocketOption IP_MULTICAST_LOOP;
- field public static final java.net.SocketOption IP_MULTICAST_TTL;
- field public static final java.net.SocketOption IP_TOS;
- field public static final java.net.SocketOption SO_BROADCAST;
- field public static final java.net.SocketOption SO_KEEPALIVE;
- field public static final java.net.SocketOption SO_LINGER;
- field public static final java.net.SocketOption SO_RCVBUF;
- field public static final java.net.SocketOption SO_REUSEADDR;
- field public static final java.net.SocketOption SO_SNDBUF;
- field public static final java.net.SocketOption TCP_NODELAY;
- }
-
public final class URI implements java.lang.Comparable java.io.Serializable {
ctor public URI(java.lang.String) throws java.net.URISyntaxException;
ctor public URI(java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
@@ -42492,7 +42388,6 @@ package java.net {
method public java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
method public java.lang.String getContentEncoding();
method public int getContentLength();
- method public long getContentLengthLong();
method public java.lang.String getContentType();
method public long getDate();
method public static boolean getDefaultAllowUserInteraction();
@@ -42507,7 +42402,6 @@ package java.net {
method public long getHeaderFieldDate(java.lang.String, long);
method public int getHeaderFieldInt(java.lang.String, int);
method public java.lang.String getHeaderFieldKey(int);
- method public long getHeaderFieldLong(java.lang.String, long);
method public java.util.Map<java.lang.String, java.util.List<java.lang.String>> getHeaderFields();
method public long getIfModifiedSince();
method public java.io.InputStream getInputStream() throws java.io.IOException;
@@ -42858,10 +42752,6 @@ package java.nio {
package java.nio.channels {
- public class AlreadyBoundException extends java.lang.IllegalStateException {
- ctor public AlreadyBoundException();
- }
-
public class AlreadyConnectedException extends java.lang.IllegalStateException {
ctor public AlreadyConnectedException();
}
@@ -42909,32 +42799,25 @@ package java.nio.channels {
ctor public ConnectionPendingException();
}
- public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
- method public java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel disconnect() throws java.io.IOException;
- method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
- method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
method public abstract boolean isConnected();
- method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
- method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
method public static java.nio.channels.DatagramChannel open() throws java.io.IOException;
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract java.net.SocketAddress receive(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract int send(java.nio.ByteBuffer, java.net.SocketAddress) throws java.io.IOException;
- method public java.nio.channels.DatagramChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.DatagramSocket socket();
- method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
method public final int validOps();
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
method public final synchronized long write(java.nio.ByteBuffer[]) throws java.io.IOException;
}
- public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel java.nio.channels.SeekableByteChannel {
+ public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected FileChannel();
method public abstract void force(boolean) throws java.io.IOException;
method public final java.nio.channels.FileLock lock() throws java.io.IOException;
@@ -42966,7 +42849,6 @@ package java.nio.channels {
public abstract class FileLock implements java.lang.AutoCloseable {
ctor protected FileLock(java.nio.channels.FileChannel, long, long, boolean);
- method public java.nio.channels.Channel acquiredBy();
method public final java.nio.channels.FileChannel channel();
method public final void close() throws java.io.IOException;
method public final boolean isShared();
@@ -42999,32 +42881,6 @@ package java.nio.channels {
method public abstract void close() throws java.io.IOException;
}
- public abstract class MembershipKey {
- ctor protected MembershipKey();
- method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
- method public abstract java.nio.channels.MulticastChannel channel();
- method public abstract void drop();
- method public abstract java.net.InetAddress group();
- method public abstract boolean isValid();
- method public abstract java.net.NetworkInterface networkInterface();
- method public abstract java.net.InetAddress sourceAddress();
- method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
- }
-
- public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
- method public abstract void close() throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
- method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
- }
-
- public abstract interface NetworkChannel implements java.lang.AutoCloseable java.nio.channels.Channel java.io.Closeable {
- method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
- method public abstract T getOption(java.net.SocketOption<T>) throws java.io.IOException;
- method public abstract java.nio.channels.NetworkChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
- method public abstract java.util.Set<java.net.SocketOption<?>> supportedOptions();
- }
-
public class NoConnectionPendingException extends java.lang.IllegalStateException {
ctor public NoConnectionPendingException();
}
@@ -43075,15 +42931,6 @@ package java.nio.channels {
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
}
- public abstract interface SeekableByteChannel implements java.nio.channels.ByteChannel {
- method public abstract long position() throws java.io.IOException;
- method public abstract java.nio.channels.SeekableByteChannel position(long) throws java.io.IOException;
- method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
- method public abstract long size() throws java.io.IOException;
- method public abstract java.nio.channels.SeekableByteChannel truncate(long) throws java.io.IOException;
- method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
- }
-
public abstract class SelectableChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.Channel {
ctor protected SelectableChannel();
method public abstract java.lang.Object blockingLock();
@@ -43132,27 +42979,18 @@ package java.nio.channels {
method public abstract java.nio.channels.Selector wakeup();
}
- public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.NetworkChannel {
+ public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel {
ctor protected ServerSocketChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.SocketChannel accept() throws java.io.IOException;
- method public final java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
- method public java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
- method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
- method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
method public static java.nio.channels.ServerSocketChannel open() throws java.io.IOException;
- method public java.nio.channels.ServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.ServerSocket socket();
- method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
method public final int validOps();
}
- public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
- method public java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract boolean connect(java.net.SocketAddress) throws java.io.IOException;
method public abstract boolean finishConnect() throws java.io.IOException;
- method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
- method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
method public abstract boolean isConnected();
method public abstract boolean isConnectionPending();
method public static java.nio.channels.SocketChannel open() throws java.io.IOException;
@@ -43160,9 +42998,7 @@ package java.nio.channels {
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
- method public java.nio.channels.SocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.Socket socket();
- method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
method public final int validOps();
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
@@ -47122,7 +46958,6 @@ package java.util {
method public java.lang.String getDisplayName(java.util.Locale);
method public static java.util.Currency getInstance(java.lang.String);
method public static java.util.Currency getInstance(java.util.Locale);
- method public int getNumericCode();
method public java.lang.String getSymbol();
method public java.lang.String getSymbol(java.util.Locale);
}
@@ -47407,13 +47242,6 @@ package java.util {
method public int getWidth();
}
- public class IllformedLocaleException extends java.lang.RuntimeException {
- ctor public IllformedLocaleException();
- ctor public IllformedLocaleException(java.lang.String);
- ctor public IllformedLocaleException(java.lang.String, int);
- method public int getErrorIndex();
- }
-
public class InputMismatchException extends java.util.NoSuchElementException implements java.io.Serializable {
ctor public InputMismatchException();
ctor public InputMismatchException(java.lang.String);
@@ -47528,7 +47356,6 @@ package java.util {
ctor public Locale(java.lang.String, java.lang.String);
ctor public Locale(java.lang.String, java.lang.String, java.lang.String);
method public java.lang.Object clone();
- method public static java.util.Locale forLanguageTag(java.lang.String);
method public static java.util.Locale[] getAvailableLocales();
method public java.lang.String getCountry();
method public static java.util.Locale getDefault();
@@ -47538,24 +47365,15 @@ package java.util {
method public java.lang.String getDisplayLanguage(java.util.Locale);
method public final java.lang.String getDisplayName();
method public java.lang.String getDisplayName(java.util.Locale);
- method public java.lang.String getDisplayScript();
- method public java.lang.String getDisplayScript(java.util.Locale);
method public final java.lang.String getDisplayVariant();
method public java.lang.String getDisplayVariant(java.util.Locale);
- method public java.lang.String getExtension(char);
- method public java.util.Set<java.lang.Character> getExtensionKeys();
method public java.lang.String getISO3Country();
method public java.lang.String getISO3Language();
method public static java.lang.String[] getISOCountries();
method public static java.lang.String[] getISOLanguages();
method public java.lang.String getLanguage();
- method public java.lang.String getScript();
- method public java.util.Set<java.lang.String> getUnicodeLocaleAttributes();
- method public java.util.Set<java.lang.String> getUnicodeLocaleKeys();
- method public java.lang.String getUnicodeLocaleType(java.lang.String);
method public java.lang.String getVariant();
method public static synchronized void setDefault(java.util.Locale);
- method public java.lang.String toLanguageTag();
method public final java.lang.String toString();
field public static final java.util.Locale CANADA;
field public static final java.util.Locale CANADA_FRENCH;
@@ -47573,33 +47391,14 @@ package java.util {
field public static final java.util.Locale KOREA;
field public static final java.util.Locale KOREAN;
field public static final java.util.Locale PRC;
- field public static final char PRIVATE_USE_EXTENSION = 120; // 0x0078 'x'
field public static final java.util.Locale ROOT;
field public static final java.util.Locale SIMPLIFIED_CHINESE;
field public static final java.util.Locale TAIWAN;
field public static final java.util.Locale TRADITIONAL_CHINESE;
field public static final java.util.Locale UK;
- field public static final char UNICODE_LOCALE_EXTENSION = 117; // 0x0075 'u'
field public static final java.util.Locale US;
}
- public static final class Locale.Builder {
- ctor public Locale.Builder();
- method public java.util.Locale.Builder addUnicodeLocaleAttribute(java.lang.String);
- method public java.util.Locale build();
- method public java.util.Locale.Builder clear();
- method public java.util.Locale.Builder clearExtensions();
- method public java.util.Locale.Builder removeUnicodeLocaleAttribute(java.lang.String);
- method public java.util.Locale.Builder setExtension(char, java.lang.String);
- method public java.util.Locale.Builder setLanguage(java.lang.String);
- method public java.util.Locale.Builder setLanguageTag(java.lang.String);
- method public java.util.Locale.Builder setLocale(java.util.Locale);
- method public java.util.Locale.Builder setRegion(java.lang.String);
- method public java.util.Locale.Builder setScript(java.lang.String);
- method public java.util.Locale.Builder setUnicodeLocaleKeyword(java.lang.String, java.lang.String);
- method public java.util.Locale.Builder setVariant(java.lang.String);
- }
-
public abstract interface Map {
method public abstract void clear();
method public abstract boolean containsKey(java.lang.Object);
@@ -48286,35 +48085,6 @@ package java.util.concurrent {
method public V replace(K, V);
}
- public class ConcurrentLinkedDeque extends java.util.AbstractCollection implements java.util.Deque java.io.Serializable {
- ctor public ConcurrentLinkedDeque();
- ctor public ConcurrentLinkedDeque(java.util.Collection<? extends E>);
- method public void addFirst(E);
- method public void addLast(E);
- method public java.util.Iterator<E> descendingIterator();
- method public E element();
- method public E getFirst();
- method public E getLast();
- method public java.util.Iterator<E> iterator();
- method public boolean offer(E);
- method public boolean offerFirst(E);
- method public boolean offerLast(E);
- method public E peek();
- method public E peekFirst();
- method public E peekLast();
- method public E poll();
- method public E pollFirst();
- method public E pollLast();
- method public E pop();
- method public void push(E);
- method public E remove();
- method public E removeFirst();
- method public boolean removeFirstOccurrence(java.lang.Object);
- method public E removeLast();
- method public boolean removeLastOccurrence(java.lang.Object);
- method public int size();
- }
-
public class ConcurrentLinkedQueue extends java.util.AbstractQueue implements java.util.Queue java.io.Serializable {
ctor public ConcurrentLinkedQueue();
ctor public ConcurrentLinkedQueue(java.util.Collection<? extends E>);
@@ -48555,94 +48325,6 @@ package java.util.concurrent {
method public static java.util.concurrent.ScheduledExecutorService unconfigurableScheduledExecutorService(java.util.concurrent.ScheduledExecutorService);
}
- public class ForkJoinPool extends java.util.concurrent.AbstractExecutorService {
- ctor public ForkJoinPool();
- ctor public ForkJoinPool(int);
- ctor public ForkJoinPool(int, java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory, java.lang.Thread.UncaughtExceptionHandler, boolean);
- method public boolean awaitQuiescence(long, java.util.concurrent.TimeUnit);
- method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- method protected int drainTasksTo(java.util.Collection<? super java.util.concurrent.ForkJoinTask<?>>);
- method public void execute(java.util.concurrent.ForkJoinTask<?>);
- method public void execute(java.lang.Runnable);
- method public int getActiveThreadCount();
- method public boolean getAsyncMode();
- method public java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory getFactory();
- method public int getParallelism();
- method public int getPoolSize();
- method public int getQueuedSubmissionCount();
- method public long getQueuedTaskCount();
- method public int getRunningThreadCount();
- method public long getStealCount();
- method public java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionHandler();
- method public boolean hasQueuedSubmissions();
- method public T invoke(java.util.concurrent.ForkJoinTask<T>);
- method public boolean isQuiescent();
- method public boolean isShutdown();
- method public boolean isTerminated();
- method public boolean isTerminating();
- method public static void managedBlock(java.util.concurrent.ForkJoinPool.ManagedBlocker) throws java.lang.InterruptedException;
- method protected java.util.concurrent.ForkJoinTask<?> pollSubmission();
- method public void shutdown();
- method public java.util.List<java.lang.Runnable> shutdownNow();
- method public java.util.concurrent.ForkJoinTask<T> submit(java.util.concurrent.ForkJoinTask<T>);
- field public static final java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory defaultForkJoinWorkerThreadFactory;
- }
-
- public static abstract interface ForkJoinPool.ForkJoinWorkerThreadFactory {
- method public abstract java.util.concurrent.ForkJoinWorkerThread newThread(java.util.concurrent.ForkJoinPool);
- }
-
- public static abstract interface ForkJoinPool.ManagedBlocker {
- method public abstract boolean block() throws java.lang.InterruptedException;
- method public abstract boolean isReleasable();
- }
-
- public abstract class ForkJoinTask implements java.util.concurrent.Future java.io.Serializable {
- ctor public ForkJoinTask();
- method public static java.util.concurrent.ForkJoinTask<?> adapt(java.lang.Runnable);
- method public static java.util.concurrent.ForkJoinTask<T> adapt(java.lang.Runnable, T);
- method public static java.util.concurrent.ForkJoinTask<T> adapt(java.util.concurrent.Callable<? extends T>);
- method public boolean cancel(boolean);
- method public void complete(V);
- method public void completeExceptionally(java.lang.Throwable);
- method protected abstract boolean exec();
- method public final java.util.concurrent.ForkJoinTask<V> fork();
- method public final V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
- method public final V get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
- method public final java.lang.Throwable getException();
- method public static java.util.concurrent.ForkJoinPool getPool();
- method public static int getQueuedTaskCount();
- method public abstract V getRawResult();
- method public static int getSurplusQueuedTaskCount();
- method public static void helpQuiesce();
- method public static boolean inForkJoinPool();
- method public final V invoke();
- method public static void invokeAll(java.util.concurrent.ForkJoinTask<?>, java.util.concurrent.ForkJoinTask<?>);
- method public static void invokeAll(java.util.concurrent.ForkJoinTask<?>...);
- method public static java.util.Collection<T> invokeAll(java.util.Collection<T>);
- method public final boolean isCancelled();
- method public final boolean isCompletedAbnormally();
- method public final boolean isCompletedNormally();
- method public final boolean isDone();
- method public final V join();
- method protected static java.util.concurrent.ForkJoinTask<?> peekNextLocalTask();
- method protected static java.util.concurrent.ForkJoinTask<?> pollNextLocalTask();
- method protected static java.util.concurrent.ForkJoinTask<?> pollTask();
- method public final void quietlyInvoke();
- method public final void quietlyJoin();
- method public void reinitialize();
- method protected abstract void setRawResult(V);
- method public boolean tryUnfork();
- }
-
- public class ForkJoinWorkerThread extends java.lang.Thread {
- ctor protected ForkJoinWorkerThread(java.util.concurrent.ForkJoinPool);
- method public java.util.concurrent.ForkJoinPool getPool();
- method public int getPoolIndex();
- method protected void onStart();
- method protected void onTermination(java.lang.Throwable);
- }
-
public abstract interface Future {
method public abstract boolean cancel(boolean);
method public abstract V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
@@ -48727,52 +48409,6 @@ package java.util.concurrent {
method public E take() throws java.lang.InterruptedException;
}
- public class LinkedTransferQueue extends java.util.AbstractQueue implements java.io.Serializable java.util.concurrent.TransferQueue {
- ctor public LinkedTransferQueue();
- ctor public LinkedTransferQueue(java.util.Collection<? extends E>);
- method public int drainTo(java.util.Collection<? super E>);
- method public int drainTo(java.util.Collection<? super E>, int);
- method public int getWaitingConsumerCount();
- method public boolean hasWaitingConsumer();
- method public java.util.Iterator<E> iterator();
- method public boolean offer(E, long, java.util.concurrent.TimeUnit);
- method public boolean offer(E);
- method public E peek();
- method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- method public E poll();
- method public void put(E);
- method public int remainingCapacity();
- method public int size();
- method public E take() throws java.lang.InterruptedException;
- method public void transfer(E) throws java.lang.InterruptedException;
- method public boolean tryTransfer(E);
- method public boolean tryTransfer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- }
-
- public class Phaser {
- ctor public Phaser();
- ctor public Phaser(int);
- ctor public Phaser(java.util.concurrent.Phaser);
- ctor public Phaser(java.util.concurrent.Phaser, int);
- method public int arrive();
- method public int arriveAndAwaitAdvance();
- method public int arriveAndDeregister();
- method public int awaitAdvance(int);
- method public int awaitAdvanceInterruptibly(int) throws java.lang.InterruptedException;
- method public int awaitAdvanceInterruptibly(int, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException;
- method public int bulkRegister(int);
- method public void forceTermination();
- method public int getArrivedParties();
- method public java.util.concurrent.Phaser getParent();
- method public final int getPhase();
- method public int getRegisteredParties();
- method public java.util.concurrent.Phaser getRoot();
- method public int getUnarrivedParties();
- method public boolean isTerminated();
- method protected boolean onAdvance(int, int);
- method public int register();
- }
-
public class PriorityBlockingQueue extends java.util.AbstractQueue implements java.util.concurrent.BlockingQueue java.io.Serializable {
ctor public PriorityBlockingQueue();
ctor public PriorityBlockingQueue(int);
@@ -48793,22 +48429,6 @@ package java.util.concurrent {
method public E take() throws java.lang.InterruptedException;
}
- public abstract class RecursiveAction extends java.util.concurrent.ForkJoinTask {
- ctor public RecursiveAction();
- method protected abstract void compute();
- method protected final boolean exec();
- method public final java.lang.Void getRawResult();
- method protected final void setRawResult(java.lang.Void);
- }
-
- public abstract class RecursiveTask extends java.util.concurrent.ForkJoinTask {
- ctor public RecursiveTask();
- method protected abstract V compute();
- method protected final boolean exec();
- method public final V getRawResult();
- method protected final void setRawResult(V);
- }
-
public class RejectedExecutionException extends java.lang.RuntimeException {
ctor public RejectedExecutionException();
ctor public RejectedExecutionException(java.lang.String);
@@ -48847,14 +48467,12 @@ package java.util.concurrent {
method protected java.util.concurrent.RunnableScheduledFuture<V> decorateTask(java.util.concurrent.Callable<V>, java.util.concurrent.RunnableScheduledFuture<V>);
method public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy();
method public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy();
- method public boolean getRemoveOnCancelPolicy();
method public java.util.concurrent.ScheduledFuture<?> schedule(java.lang.Runnable, long, java.util.concurrent.TimeUnit);
method public java.util.concurrent.ScheduledFuture<V> schedule(java.util.concurrent.Callable<V>, long, java.util.concurrent.TimeUnit);
method public java.util.concurrent.ScheduledFuture<?> scheduleAtFixedRate(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit);
method public java.util.concurrent.ScheduledFuture<?> scheduleWithFixedDelay(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit);
method public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean);
method public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean);
- method public void setRemoveOnCancelPolicy(boolean);
}
public class Semaphore implements java.io.Serializable {
@@ -48900,15 +48518,6 @@ package java.util.concurrent {
method public abstract java.lang.Thread newThread(java.lang.Runnable);
}
- public class ThreadLocalRandom extends java.util.Random {
- method public static java.util.concurrent.ThreadLocalRandom current();
- method public double nextDouble(double);
- method public double nextDouble(double, double);
- method public int nextInt(int, int);
- method public long nextLong(long);
- method public long nextLong(long, long);
- }
-
public class ThreadPoolExecutor extends java.util.concurrent.AbstractExecutorService {
ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>);
ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>, java.util.concurrent.ThreadFactory);
@@ -48996,14 +48605,6 @@ package java.util.concurrent {
ctor public TimeoutException(java.lang.String);
}
- public abstract interface TransferQueue implements java.util.concurrent.BlockingQueue {
- method public abstract int getWaitingConsumerCount();
- method public abstract boolean hasWaitingConsumer();
- method public abstract void transfer(E) throws java.lang.InterruptedException;
- method public abstract boolean tryTransfer(E);
- method public abstract boolean tryTransfer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
- }
-
}
package java.util.concurrent.atomic {
@@ -49213,7 +48814,6 @@ package java.util.concurrent.locks {
method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
method public final boolean hasContended();
- method public final boolean hasQueuedPredecessors();
method public final boolean hasQueuedThreads();
method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
method protected boolean isHeldExclusively();
@@ -49260,7 +48860,6 @@ package java.util.concurrent.locks {
method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
method public final boolean hasContended();
- method public final boolean hasQueuedPredecessors();
method public final boolean hasQueuedThreads();
method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
method protected boolean isHeldExclusively();
@@ -50205,10 +49804,8 @@ package java.util.zip {
public class ZipFile implements java.io.Closeable {
ctor public ZipFile(java.io.File) throws java.io.IOException, java.util.zip.ZipException;
- ctor public ZipFile(java.io.File, java.nio.charset.Charset) throws java.io.IOException, java.util.zip.ZipException;
ctor public ZipFile(java.lang.String) throws java.io.IOException;
ctor public ZipFile(java.io.File, int) throws java.io.IOException;
- ctor public ZipFile(java.io.File, int, java.nio.charset.Charset) throws java.io.IOException;
method public void close() throws java.io.IOException;
method public java.util.Enumeration<? extends java.util.zip.ZipEntry> entries();
method public java.lang.String getComment();
@@ -50262,7 +49859,6 @@ package java.util.zip {
public class ZipInputStream extends java.util.zip.InflaterInputStream {
ctor public ZipInputStream(java.io.InputStream);
- ctor public ZipInputStream(java.io.InputStream, java.nio.charset.Charset);
method public void closeEntry() throws java.io.IOException;
method protected java.util.zip.ZipEntry createZipEntry(java.lang.String);
method public java.util.zip.ZipEntry getNextEntry() throws java.io.IOException;
@@ -50310,7 +49906,6 @@ package java.util.zip {
public class ZipOutputStream extends java.util.zip.DeflaterOutputStream {
ctor public ZipOutputStream(java.io.OutputStream);
- ctor public ZipOutputStream(java.io.OutputStream, java.nio.charset.Charset);
method public void closeEntry() throws java.io.IOException;
method public void putNextEntry(java.util.zip.ZipEntry) throws java.io.IOException;
method public void setComment(java.lang.String);
@@ -51809,7 +51404,6 @@ package javax.net.ssl {
method public abstract boolean getEnableSessionCreation();
method public abstract java.lang.String[] getEnabledCipherSuites();
method public abstract java.lang.String[] getEnabledProtocols();
- method public javax.net.ssl.SSLSession getHandshakeSession();
method public abstract javax.net.ssl.SSLEngineResult.HandshakeStatus getHandshakeStatus();
method public abstract boolean getNeedClientAuth();
method public java.lang.String getPeerHost();
@@ -51883,12 +51477,10 @@ package javax.net.ssl {
ctor public SSLParameters(java.lang.String[]);
ctor public SSLParameters(java.lang.String[], java.lang.String[]);
method public java.lang.String[] getCipherSuites();
- method public java.lang.String getEndpointIdentificationAlgorithm();
method public boolean getNeedClientAuth();
method public java.lang.String[] getProtocols();
method public boolean getWantClientAuth();
method public void setCipherSuites(java.lang.String[]);
- method public void setEndpointIdentificationAlgorithm(java.lang.String);
method public void setNeedClientAuth(boolean);
method public void setProtocols(java.lang.String[]);
method public void setWantClientAuth(boolean);
@@ -51989,7 +51581,6 @@ package javax.net.ssl {
method public abstract boolean getEnableSessionCreation();
method public abstract java.lang.String[] getEnabledCipherSuites();
method public abstract java.lang.String[] getEnabledProtocols();
- method public javax.net.ssl.SSLSession getHandshakeSession();
method public abstract boolean getNeedClientAuth();
method public javax.net.ssl.SSLParameters getSSLParameters();
method public abstract javax.net.ssl.SSLSession getSession();
@@ -52045,14 +51636,6 @@ package javax.net.ssl {
method public java.lang.String chooseEngineServerAlias(java.lang.String, java.security.Principal[], javax.net.ssl.SSLEngine);
}
- public abstract class X509ExtendedTrustManager implements javax.net.ssl.X509TrustManager {
- ctor public X509ExtendedTrustManager();
- method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException;
- method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException;
- method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException;
- method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException;
- }
-
public abstract interface X509KeyManager implements javax.net.ssl.KeyManager {
method public abstract java.lang.String chooseClientAlias(java.lang.String[], java.security.Principal[], java.net.Socket);
method public abstract java.lang.String chooseServerAlias(java.lang.String, java.security.Principal[], java.net.Socket);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 5867232..a480219 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -189,7 +189,9 @@ public class AppOpsManager {
/** @hide Retrieve current usage stats via {@link UsageStatsManager}. */
public static final int OP_GET_USAGE_STATS = 43;
/** @hide */
- public static final int _NUM_OP = 44;
+ public static final int OP_MUTE_MICROPHONE = 44;
+ /** @hide */
+ public static final int _NUM_OP = 45;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION =
@@ -257,6 +259,7 @@ public class AppOpsManager {
OP_COARSE_LOCATION,
OP_COARSE_LOCATION,
OP_GET_USAGE_STATS,
+ OP_MUTE_MICROPHONE
};
/**
@@ -308,6 +311,7 @@ public class AppOpsManager {
OPSTR_MONITOR_LOCATION,
OPSTR_MONITOR_HIGH_POWER_LOCATION,
null,
+ null,
};
/**
@@ -358,7 +362,8 @@ public class AppOpsManager {
"WAKE_LOCK",
"MONITOR_LOCATION",
"MONITOR_HIGH_POWER_LOCATION",
- "GET_USAGE_STATS"
+ "GET_USAGE_STATS",
+ "OP_MUTE_MICROPHONE",
};
/**
@@ -410,6 +415,7 @@ public class AppOpsManager {
null, // no permission for generic location monitoring
null, // no permission for high power location monitoring
android.Manifest.permission.PACKAGE_USAGE_STATS,
+ null, // no permission for muting/unmuting microphone
};
/**
@@ -462,6 +468,7 @@ public class AppOpsManager {
null, //MONITOR_LOCATION
null, //MONITOR_HIGH_POWER_LOCATION
null, //GET_USAGE_STATS
+ UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE
};
/**
@@ -512,6 +519,7 @@ public class AppOpsManager {
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
+ AppOpsManager.MODE_ALLOWED,
};
/**
@@ -566,6 +574,7 @@ public class AppOpsManager {
false,
false,
false,
+ false,
};
private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 52d4585..ab3bb49 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -118,6 +118,7 @@ import android.print.PrintManager;
import android.service.fingerprint.FingerprintManager;
import android.service.fingerprint.FingerprintManagerReceiver;
import android.service.fingerprint.FingerprintService;
+import android.telecomm.TelecommManager;
import android.telephony.TelephonyManager;
import android.content.ClipboardManager;
import android.util.AndroidRuntimeException;
@@ -142,6 +143,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
import com.android.internal.appwidget.IAppWidgetService.Stub;
import com.android.internal.os.IDropBoxManagerService;
+import com.android.internal.telecomm.ITelecommService;
import java.io.File;
import java.io.FileInputStream;
@@ -554,6 +556,13 @@ class ContextImpl extends Context {
return new TelephonyManager(ctx.getOuterContext());
}});
+ registerService(TELECOMM_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(TELECOMM_SERVICE);
+ return new TelecommManager(ctx.getOuterContext(),
+ ITelecommService.Stub.asInterface(b));
+ }});
+
registerService(UI_MODE_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return new UiModeManager();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ccf8451..fd19b40 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2035,6 +2035,7 @@ public abstract class Context {
AUDIO_SERVICE,
MEDIA_ROUTER_SERVICE,
TELEPHONY_SERVICE,
+ TELECOMM_SERVICE,
CLIPBOARD_SERVICE,
INPUT_METHOD_SERVICE,
TEXT_SERVICES_MANAGER_SERVICE,
@@ -2163,6 +2164,8 @@ public abstract class Context {
* @see android.media.MediaRouter
* @see #TELEPHONY_SERVICE
* @see android.telephony.TelephonyManager
+ * @see #TELECOMM_SERVICE
+ * @see android.telecomm.TelecommManager
* @see #INPUT_METHOD_SERVICE
* @see android.view.inputmethod.InputMethodManager
* @see #UI_MODE_SERVICE
@@ -2494,6 +2497,16 @@ public abstract class Context {
/**
* Use with {@link #getSystemService} to retrieve a
+ * {@link android.telecomm.TelecommManager} to manage telecomm-related features
+ * of the device.
+ *
+ * @see #getSystemService
+ * @see android.telecomm.TelecommManager
+ */
+ public static final String TELECOMM_SERVICE = "telecomm";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a
* {@link android.text.ClipboardManager} for accessing and modifying
* the contents of the global clipboard.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 550c1f1..8d9b8d9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.PackageParser.PackageParserException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -2873,16 +2874,17 @@ public abstract class PackageManager {
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
final File sourceFile = new File(archiveFilePath);
- PackageParser.Package pkg = packageParser.parsePackage(
- sourceFile, archiveFilePath, metrics, 0);
- if (pkg == null) {
+ try {
+ PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics,
+ 0);
+ if ((flags & GET_SIGNATURES) != 0) {
+ packageParser.collectCertificates(pkg, 0);
+ }
+ PackageUserState state = new PackageUserState();
+ return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
+ } catch (PackageParserException e) {
return null;
}
- if ((flags & GET_SIGNATURES) != 0) {
- packageParser.collectCertificates(pkg, 0);
- }
- PackageUserState state = new PackageUserState();
- return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4cac7fd..91895ff 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -18,6 +18,7 @@ package android.content.pm;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import android.content.ComponentName;
import android.content.Intent;
@@ -31,6 +32,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Base64;
import android.util.DisplayMetrics;
@@ -81,6 +83,8 @@ public class PackageParser {
private static final boolean DEBUG_PARSER = false;
private static final boolean DEBUG_BACKUP = false;
+ // TODO: switch outError users to PackageParserException
+
/** File name in an APK for the Android manifest. */
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
@@ -207,10 +211,10 @@ public class PackageParser {
}
}
- /* Light weight package info.
- * @hide
+ /**
+ * Lightweight parsed details about a single APK file.
*/
- public static class PackageLite {
+ public static class ApkLite {
public final String packageName;
public final String splitName;
public final int versionCode;
@@ -218,7 +222,7 @@ public class PackageParser {
public final VerifierInfo[] verifiers;
public final Signature[] signatures;
- public PackageLite(String packageName, String splitName, int versionCode,
+ public ApkLite(String packageName, String splitName, int versionCode,
int installLocation, List<VerifierInfo> verifiers, Signature[] signatures) {
this.packageName = packageName;
this.splitName = splitName;
@@ -247,6 +251,10 @@ public class PackageParser {
mArchiveSourcePath = archiveSourcePath;
}
+ public PackageParser(File archiveSource) {
+ this(archiveSource.getAbsolutePath());
+ }
+
public void setSeparateProcesses(String[] procs) {
mSeparateProcesses = procs;
}
@@ -255,6 +263,10 @@ public class PackageParser {
mOnlyCoreApps = onlyCoreApps;
}
+ private static final boolean isPackageFilename(File file) {
+ return isPackageFilename(file.getName());
+ }
+
private static final boolean isPackageFilename(String name) {
return name.endsWith(".apk");
}
@@ -497,26 +509,84 @@ public class PackageParser {
public final static int PARSE_IS_PRIVILEGED = 1<<7;
public final static int PARSE_GET_SIGNATURES = 1<<8;
- public int getParseError() {
- return mParseError;
+ /**
+ * Parse all APK files under the given directory as a single package.
+ */
+ public Package parseSplitPackage(File apkDir, DisplayMetrics metrics, int flags,
+ boolean trustedOverlay) throws PackageParserException {
+ final File[] files = apkDir.listFiles();
+ if (ArrayUtils.isEmpty(files)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "No packages found in split");
+ }
+
+ File baseFile = null;
+ for (File file : files) {
+ if (file.isFile() && isPackageFilename(file)) {
+ ApkLite lite = parseApkLite(file.getAbsolutePath(), 0);
+ if (lite == null) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + file);
+ }
+
+ if (TextUtils.isEmpty(lite.splitName)) {
+ baseFile = file;
+ break;
+ }
+ }
+ }
+
+ if (baseFile == null) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Missing base APK in " + apkDir);
+ }
+
+ final Package pkg = parseBaseApk(baseFile, metrics, flags, trustedOverlay);
+ if (pkg == null) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Failed to parse base APK: " + baseFile);
+ }
+
+ for (File file : files) {
+ if (file.isFile() && isPackageFilename(file) && !file.equals(baseFile)) {
+ parseSplitApk(pkg, file, metrics, flags, trustedOverlay);
+ }
+ }
+
+ // Always use a well-defined sort order
+ if (pkg.splitCodePaths != null) {
+ Arrays.sort(pkg.splitCodePaths);
+ }
+
+ return pkg;
+ }
+
+ public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags)
+ throws PackageParserException {
+ return parseMonolithicPackage(apkFile, metrics, flags, false);
}
- public Package parsePackage(File sourceFile, String destCodePath,
- DisplayMetrics metrics, int flags) {
- return parsePackage(sourceFile, destCodePath, metrics, flags, false);
+ public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags,
+ boolean trustedOverlay) throws PackageParserException {
+ final Package pkg = parseBaseApk(apkFile, metrics, flags, trustedOverlay);
+ if (pkg != null) {
+ return pkg;
+ } else {
+ throw new PackageParserException(mParseError, "Failed to parse " + apkFile);
+ }
}
- public Package parsePackage(File sourceFile, String destCodePath,
- DisplayMetrics metrics, int flags, boolean trustedOverlay) {
+ private Package parseBaseApk(File apkFile, DisplayMetrics metrics, int flags,
+ boolean trustedOverlay) {
mParseError = PackageManager.INSTALL_SUCCEEDED;
- mArchiveSourcePath = sourceFile.getPath();
- if (!sourceFile.isFile()) {
+ mArchiveSourcePath = apkFile.getAbsolutePath();
+ if (!apkFile.isFile()) {
Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);
mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
return null;
}
- if (!isPackageFilename(sourceFile.getName())
+ if (!isPackageFilename(apkFile.getName())
&& (flags&PARSE_MUST_BE_APK) != 0) {
if ((flags&PARSE_IS_SYSTEM) == 0) {
// We expect to have non-.apk files in the system dir,
@@ -560,13 +630,12 @@ public class PackageParser {
Exception errorException = null;
try {
// XXXX todo: need to figure out correct configuration.
- pkg = parsePackage(res, parser, flags, trustedOverlay, errorText);
+ pkg = parseBaseApk(res, parser, flags, trustedOverlay, errorText);
} catch (Exception e) {
errorException = e;
mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
}
-
if (pkg == null) {
// If we are only parsing core apps, then a null with INSTALL_SUCCEEDED
// just means to skip this app so don't make a fuss about it.
@@ -590,22 +659,25 @@ public class PackageParser {
parser.close();
assmgr.close();
- // Set code and resource paths
- pkg.mPath = destCodePath;
- pkg.mScanPath = mArchiveSourcePath;
- //pkg.applicationInfo.sourceDir = destCodePath;
- //pkg.applicationInfo.publicSourceDir = destRes;
+ pkg.codePath = mArchiveSourcePath;
pkg.mSignatures = null;
return pkg;
}
+ private void parseSplitApk(Package pkg, File apkFile, DisplayMetrics metrics, int flags,
+ boolean trustedOverlay) throws PackageParserException {
+ // TODO: expand split APK parsing
+ pkg.splitCodePaths = ArrayUtils.appendElement(String.class, pkg.splitCodePaths,
+ apkFile.getAbsolutePath());
+ }
+
/**
* Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the
* APK. If it successfully scanned the package and found the
* {@code AndroidManifest.xml}, {@code true} is returned.
*/
- public boolean collectManifestDigest(Package pkg) {
+ public void collectManifestDigest(Package pkg) throws PackageParserException {
try {
final StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath);
try {
@@ -616,13 +688,19 @@ public class PackageParser {
} finally {
jarFile.close();
}
- return true;
} catch (IOException e) {
- return false;
+ throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Failed to collect manifest digest");
+ }
+ }
+
+ public void collectCertificates(Package pkg, int flags) throws PackageParserException {
+ if (!collectCertificatesInternal(pkg, flags)) {
+ throw new PackageParserException(mParseError, "Failed to collect certificates");
}
}
- public boolean collectCertificates(Package pkg, int flags) {
+ private boolean collectCertificatesInternal(Package pkg, int flags) {
pkg.mSignatures = null;
WeakReference<byte[]> readBufferRef;
@@ -808,7 +886,7 @@ public class PackageParser {
* @param flags Special parse flags
* @return PackageLite object with package information or null on failure.
*/
- public static PackageLite parsePackageLite(String packageFilePath, int flags) {
+ public static ApkLite parseApkLite(String packageFilePath, int flags) {
AssetManager assmgr = null;
final XmlResourceParser parser;
final Resources res;
@@ -844,9 +922,9 @@ public class PackageParser {
final AttributeSet attrs = parser;
final String errors[] = new String[1];
- PackageLite packageLite = null;
+ ApkLite packageLite = null;
try {
- packageLite = parsePackageLite(res, parser, attrs, flags, signatures, errors);
+ packageLite = parseApkLite(res, parser, attrs, flags, signatures, errors);
} catch (PackageParserException e) {
Slog.w(TAG, packageFilePath, e);
} catch (IOException e) {
@@ -930,7 +1008,7 @@ public class PackageParser {
(splitName != null) ? splitName.intern() : splitName);
}
- private static PackageLite parsePackageLite(Resources res, XmlPullParser parser,
+ private static ApkLite parseApkLite(Resources res, XmlPullParser parser,
AttributeSet attrs, int flags, Signature[] signatures, String[] outError)
throws IOException, XmlPullParserException, PackageParserException {
final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);
@@ -972,7 +1050,7 @@ public class PackageParser {
}
}
- return new PackageLite(packageSplit.first, packageSplit.second, versionCode,
+ return new ApkLite(packageSplit.first, packageSplit.second, versionCode,
installLocation, verifiers, signatures);
}
@@ -988,9 +1066,8 @@ public class PackageParser {
return new Signature(sig);
}
- private Package parsePackage(
- Resources res, XmlResourceParser parser, int flags, boolean trustedOverlay,
- String[] outError) throws XmlPullParserException, IOException {
+ private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
+ boolean trustedOverlay, String[] outError) throws XmlPullParserException, IOException {
AttributeSet attrs = parser;
mParseInstrumentationArgs = null;
@@ -1019,7 +1096,13 @@ public class PackageParser {
}
}
- final Package pkg = new Package(pkgName, splitName);
+ if (!TextUtils.isEmpty(splitName)) {
+ outError[0] = "Expected base APK, but found split " + splitName;
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+ return null;
+ }
+
+ final Package pkg = new Package(pkgName);
boolean foundApp = false;
TypedArray sa = res.obtainAttributes(attrs,
@@ -3580,10 +3663,17 @@ public class PackageParser {
return true;
}
+ /**
+ * Representation of a full package parsed from APK files on disk. A package
+ * consists of a single base APK, and zero or more split APKs.
+ */
public final static class Package {
public String packageName;
- public String splitName;
+
+ // TODO: work towards making these paths invariant
+ public String codePath;
+ public String[] splitCodePaths;
// For now we only support one application per package.
public final ApplicationInfo applicationInfo = new ApplicationInfo();
@@ -3615,9 +3705,6 @@ public class PackageParser {
// We store the application meta-data independently to avoid multiple unwanted references
public Bundle mAppMetaData = null;
- // If this is a 3rd party app, this is the path of the zip file.
- public String mPath;
-
// The version code declared for this package.
public int mVersionCode;
@@ -3637,10 +3724,6 @@ public class PackageParser {
// preferred up order.
public int mPreferredOrder = 0;
- // For use by the package manager to keep track of the path to the
- // file an app came from.
- public String mScanPath;
-
// For use by package manager to keep track of where it needs to do dexopt.
public boolean mDexOptNeeded = true;
@@ -3700,9 +3783,8 @@ public class PackageParser {
public Set<PublicKey> mSigningKeys;
public Map<String, Set<PublicKey>> mKeySetMapping;
- public Package(String packageName, String splitName) {
+ public Package(String packageName) {
this.packageName = packageName;
- this.splitName = splitName;
applicationInfo.packageName = packageName;
applicationInfo.uid = -1;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2a5e9fd..06c05ee 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4537,6 +4537,12 @@ public final class Settings {
public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
/**
+ * Specifies the package name currently configured to be the primary phone application
+ * @hide
+ */
+ public static final String PHONE_DEFAULT_APPLICATION = "phone_default_application";
+
+ /**
* Name of a package that the current user has explicitly allowed to see all of that
* user's notifications.
*
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index a6cddae..8fa45e6 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -80,6 +80,11 @@ public class TrustAgentService extends Service {
private ITrustAgentServiceCallback mCallback;
+ private Runnable mPendingGrantTrustTask;
+
+ // Lock used to access mPendingGrantTrustTask and mCallback.
+ private final Object mLock = new Object();
+
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
@@ -127,12 +132,24 @@ public class TrustAgentService extends Service {
* @param initiatedByUser indicates that the user has explicitly initiated an action that proves
* the user is about to use the device.
*/
- public final void grantTrust(CharSequence message, long durationMs, boolean initiatedByUser) {
- if (mCallback != null) {
- try {
- mCallback.grantTrust(message.toString(), durationMs, initiatedByUser);
- } catch (RemoteException e) {
- onError("calling enableTrust()");
+ public final void grantTrust(
+ final CharSequence message, final long durationMs, final boolean initiatedByUser) {
+ synchronized (mLock) {
+ if (mCallback != null) {
+ try {
+ mCallback.grantTrust(message.toString(), durationMs, initiatedByUser);
+ } catch (RemoteException e) {
+ onError("calling enableTrust()");
+ }
+ } else {
+ // Remember trust has been granted so we can effectively grant it once the service
+ // is bound.
+ mPendingGrantTrustTask = new Runnable() {
+ @Override
+ public void run() {
+ grantTrust(message, durationMs, initiatedByUser);
+ }
+ };
}
}
}
@@ -141,11 +158,16 @@ public class TrustAgentService extends Service {
* Call to revoke trust on the device.
*/
public final void revokeTrust() {
- if (mCallback != null) {
- try {
- mCallback.revokeTrust();
- } catch (RemoteException e) {
- onError("calling revokeTrust()");
+ synchronized (mLock) {
+ if (mPendingGrantTrustTask != null) {
+ mPendingGrantTrustTask = null;
+ }
+ if (mCallback != null) {
+ try {
+ mCallback.revokeTrust();
+ } catch (RemoteException e) {
+ onError("calling revokeTrust()");
+ }
}
}
}
@@ -164,7 +186,13 @@ public class TrustAgentService extends Service {
}
public void setCallback(ITrustAgentServiceCallback callback) {
- mCallback = callback;
+ synchronized (mLock) {
+ mCallback = callback;
+ if (mPendingGrantTrustTask != null) {
+ mPendingGrantTrustTask.run();
+ mPendingGrantTrustTask = null;
+ }
+ }
}
}
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index d1f35dd..9fec9a1 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -195,7 +195,7 @@ public class DateFormat {
* @return a string pattern suitable for use with {@link java.text.SimpleDateFormat}.
*/
public static String getBestDateTimePattern(Locale locale, String skeleton) {
- return ICU.getBestDateTimePattern(skeleton, locale.toString());
+ return ICU.getBestDateTimePattern(skeleton, locale);
}
/**
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 8f4b710..f7d2821 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -62,10 +62,7 @@ public class TimeUtils {
*/
public static TimeZone getTimeZone(int offset, boolean dst, long when, String country) {
TimeZone best = null;
-
- Resources r = Resources.getSystem();
- XmlResourceParser parser = r.getXml(com.android.internal.R.xml.time_zones_by_country);
- Date d = new Date(when);
+ final Date d = new Date(when);
TimeZone current = TimeZone.getDefault();
String currentName = current.getID();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 41037a5..b9e56f3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6116,6 +6116,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
protected boolean fitSystemWindows(Rect insets) {
if ((mPrivateFlags3 & PFLAG3_APPLYING_INSETS) == 0) {
+ if (insets == null) {
+ // Null insets by definition have already been consumed.
+ // This call cannot apply insets since there are none to apply,
+ // so return false.
+ return false;
+ }
// If we're not in the process of dispatching the newer apply insets call,
// that means we're not in the compatibility path. Dispatch into the newer
// apply insets path and take things from there.
diff --git a/core/java/android/view/ViewAnimationUtils.java b/core/java/android/view/ViewAnimationUtils.java
index 3854f34..29e865f 100644
--- a/core/java/android/view/ViewAnimationUtils.java
+++ b/core/java/android/view/ViewAnimationUtils.java
@@ -23,7 +23,7 @@ import android.animation.ValueAnimator;
* Defines common utilities for working with View's animations.
*
*/
-public class ViewAnimationUtils {
+public final class ViewAnimationUtils {
private ViewAnimationUtils() {}
/**
* Returns a ValueAnimator which can animate a clipping circle.
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index ec396aa..470d413 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -408,7 +408,6 @@ public class WebChromeClient {
* @return true if filePathCallback will be invoked, false to use default handling.
*
* @see FileChooserParams
- * @hide For API approval
*/
public boolean showFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
@@ -416,11 +415,8 @@ public class WebChromeClient {
}
/**
- * Parameters used in the {@link #showFileChooser(WebView,ValueCallback<String[]>,FileChooserParams)}
- * method.
- *
+ * Parameters used in the {@link #showFileChooser} method.
* This is intended to be used as a read-only data struct by the application.
- * @hide For API approval
*/
public static class FileChooserParams {
// Flags for mode
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 22f5e23..71817c2 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2727,7 +2727,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Sets whether the soft input method will be made visible when this
* TextView gets focused. The default is true.
- * @hide
*/
@android.view.RemotableViewMethod
public final void setShowSoftInputOnFocus(boolean show) {
@@ -2738,7 +2737,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Returns whether the soft input method will be made visible when this
* TextView gets focused. The default is true.
- * @hide
*/
public final boolean getShowSoftInputOnFocus() {
// When there is no Editor, return default true value
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 2b62552..8ee0a1b 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -22,6 +22,7 @@ import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.media.AudioManager;
+import android.media.ClosedCaptionRenderer;
import android.media.MediaFormat;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
@@ -316,6 +317,7 @@ public class VideoView extends SurfaceView
context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer);
controller.registerRenderer(new WebVttRenderer(context));
controller.registerRenderer(new TtmlRenderer(context));
+ controller.registerRenderer(new ClosedCaptionRenderer(context));
mMediaPlayer.setSubtitleAnchor(controller, this);
if (mAudioSession != 0) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index c58bf04..0cdddba 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -14,46 +14,40 @@
* limitations under the License.
*/
-#include "android_runtime/AndroidRuntime.h"
+#define LOG_TAG "Zygote"
// sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
#include <sys/mount.h>
#include <linux/fs.h>
#include <grp.h>
+#include <fcntl.h>
#include <paths.h>
#include <signal.h>
#include <stdlib.h>
+#include <unistd.h>
+#include <sys/capability.h>
+#include <sys/personality.h>
+#include <sys/prctl.h>
#include <sys/resource.h>
-#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
#include <sys/wait.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include "cutils/fs.h"
-#include "cutils/multiuser.h"
-#include "cutils/sched_policy.h"
-#include "utils/String8.h"
+
+#include <cutils/fs.h>
+#include <cutils/multiuser.h>
+#include <cutils/sched_policy.h>
+#include <utils/String8.h>
+#include <selinux/android.h>
+
+#include "android_runtime/AndroidRuntime.h"
#include "JNIHelp.h"
#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
-#if defined(HAVE_PRCTL)
-#include <sys/prctl.h>
-#endif
-
-#include <selinux/android.h>
-
-#if defined(__linux__)
-#include <sys/personality.h>
-#include <sys/utsname.h>
-#if defined(HAVE_ANDROID_OS)
-#include <sys/capability.h>
-#endif
-#endif
-
namespace {
using android::String8;
@@ -97,11 +91,9 @@ static void SigChldHandler(int /*signal_number*/) {
if (WTERMSIG(status) != SIGKILL) {
ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status));
}
-#ifdef WCOREDUMP
if (WCOREDUMP(status)) {
ALOGI("Process %d dumped core.", pid);
}
-#endif /* ifdef WCOREDUMP */
}
// If the just-crashed process is the system_server, bring down zygote
@@ -199,8 +191,6 @@ static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) {
}
}
-#if defined(HAVE_ANDROID_OS)
-
// The debug malloc library needs to know whether it's the zygote or a child.
extern "C" int gMallocLeakZygoteChild;
@@ -254,17 +244,6 @@ static void SetSchedulerPolicy(JNIEnv* env) {
}
}
-#else
-
-static int gMallocLeakZygoteChild = 0;
-
-static void EnableKeepCapabilities(JNIEnv*) {}
-static void DropCapabilitiesBoundingSet(JNIEnv*) {}
-static void SetCapabilities(JNIEnv*, int64_t, int64_t) {}
-static void SetSchedulerPolicy(JNIEnv*) {}
-
-#endif
-
// Create a private mount namespace and bind mount appropriate emulated
// storage for the given user.
static bool MountEmulatedStorage(uid_t uid, jint mount_mode) {
@@ -337,7 +316,6 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode) {
return true;
}
-#if defined(__linux__)
static bool NeedsNoRandomizeWorkaround() {
#if !defined(__arm__)
return false;
@@ -357,7 +335,6 @@ static bool NeedsNoRandomizeWorkaround() {
return (major < 3) || ((major == 3) && (minor < 4));
#endif
}
-#endif
// Utility to close down the Zygote socket file descriptors while
// the child is still running as root with Zygote's privileges. Each
@@ -474,7 +451,6 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
RuntimeAbort(env);
}
-#if defined(__linux__)
if (NeedsNoRandomizeWorkaround()) {
// Work around ARM kernel ASLR lossage (http://b/5817320).
int old_personality = personality(0xffffffff);
@@ -483,58 +459,49 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
ALOGW("personality(%d) failed", new_personality);
}
}
-#endif
SetCapabilities(env, permittedCapabilities, effectiveCapabilities);
SetSchedulerPolicy(env);
-#if defined(HAVE_ANDROID_OS)
- { // NOLINT(whitespace/braces)
- const char* se_info_c_str = NULL;
- ScopedUtfChars* se_info = NULL;
- if (java_se_info != NULL) {
- se_info = new ScopedUtfChars(env, java_se_info);
- se_info_c_str = se_info->c_str();
- if (se_info_c_str == NULL) {
- ALOGE("se_info_c_str == NULL");
- RuntimeAbort(env);
- }
- }
- const char* se_name_c_str = NULL;
- ScopedUtfChars* se_name = NULL;
- if (java_se_name != NULL) {
- se_name = new ScopedUtfChars(env, java_se_name);
- se_name_c_str = se_name->c_str();
- if (se_name_c_str == NULL) {
- ALOGE("se_name_c_str == NULL");
- RuntimeAbort(env);
- }
- }
- rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
- if (rc == -1) {
- ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
- is_system_server, se_info_c_str, se_name_c_str);
- RuntimeAbort(env);
- }
-
- // Make it easier to debug audit logs by setting the main thread's name to the
- // nice name rather than "app_process".
- if (se_info_c_str == NULL && is_system_server) {
- se_name_c_str = "system_server";
- }
- if (se_info_c_str != NULL) {
- SetThreadName(se_name_c_str);
- }
+ const char* se_info_c_str = NULL;
+ ScopedUtfChars* se_info = NULL;
+ if (java_se_info != NULL) {
+ se_info = new ScopedUtfChars(env, java_se_info);
+ se_info_c_str = se_info->c_str();
+ if (se_info_c_str == NULL) {
+ ALOGE("se_info_c_str == NULL");
+ RuntimeAbort(env);
+ }
+ }
+ const char* se_name_c_str = NULL;
+ ScopedUtfChars* se_name = NULL;
+ if (java_se_name != NULL) {
+ se_name = new ScopedUtfChars(env, java_se_name);
+ se_name_c_str = se_name->c_str();
+ if (se_name_c_str == NULL) {
+ ALOGE("se_name_c_str == NULL");
+ RuntimeAbort(env);
+ }
+ }
+ rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
+ if (rc == -1) {
+ ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
+ is_system_server, se_info_c_str, se_name_c_str);
+ RuntimeAbort(env);
+ }
- delete se_info;
- delete se_name;
+ // Make it easier to debug audit logs by setting the main thread's name to the
+ // nice name rather than "app_process".
+ if (se_info_c_str == NULL && is_system_server) {
+ se_name_c_str = "system_server";
}
-#else
- UNUSED(is_system_server);
- UNUSED(java_se_info);
- UNUSED(java_se_name);
-#endif
+ if (se_info_c_str != NULL) {
+ SetThreadName(se_name_c_str);
+ }
+
+ delete se_info;
+ delete se_name;
UnsetSigChldHandler();
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
index 17acfc5..c5c0e97 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
index 17acfc5..c5c0e97 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
index 9b8ca22..3b31225 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
index 9b8ca22..3b31225 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
index bc20f6c..b65009e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
index bc20f6c..b65009e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
index 571819b..a2dfcae 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
index 571819b..c3fda0e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
index 1f83b5a..bae60a7 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
index 1f83b5a..bae60a7 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
index 733cf45..a9653b0 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
index 733cf45..a9653b0 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
index 2265de4..394cb5e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
index 2265de4..394cb5e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
index f3ada58..aa23c6e 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
index f3ada58..028b3b8 100644
--- a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/work_icon.png b/core/res/res/drawable-hdpi/work_icon.png
deleted file mode 100644
index be8a36e..0000000
--- a/core/res/res/drawable-hdpi/work_icon.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
index 3fdd3bc..4e6d076 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
index 3fdd3bc..4e6d076 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
index eaa02b3..ca61cb2 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
index eaa02b3..ca61cb2 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
index 28c8b94..b5999be 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
index 28c8b94..b5999be 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
index 6090cce..8392ac3 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
index 6090cce..522bafd 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
index 3f2e982..ebb2f8b 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
index 3f2e982..ebb2f8b 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
index 14b958b..3fa20ca 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
index 14b958b..3fa20ca 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
index 4db22d4..6cc59ed 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
index 4db22d4..6cc59ed 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
index a11e1c7..a1fcd08 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
index a11e1c7..c6c0224 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
index c08deab..1e45530 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png
index c08deab..1e45530 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png
index 8b1a55c..2c63c5d 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png
index 8b1a55c..2c63c5d 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png
index 77cd1fa..dd5e26e 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png
index 77cd1fa..dd5e26e 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png
index e0e3540..aa9b3c5 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png
index e0e3540..367c25a 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
index 324e490..df28ad0 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png
index 324e490..df28ad0 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png
index e126cc6..3a27831 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png
index e126cc6..3a27831 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png
index 4c1f1b9..d68bdf4 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png
index 4c1f1b9..d68bdf4 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png
index 219d37b..da03ec9 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png
index 219d37b..482b249 100644
--- a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 07a6a10..7251e7c 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -29,6 +29,7 @@ import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageParser.PackageParserException;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.net.Uri;
@@ -60,7 +61,6 @@ import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -303,14 +303,13 @@ public class PackageManagerTests extends AndroidTestCase {
return Uri.fromFile(outFile);
}
- private PackageParser.Package parsePackage(Uri packageURI) {
+ private PackageParser.Package parsePackage(Uri packageURI) throws PackageParserException {
final String archiveFilePath = packageURI.getPath();
PackageParser packageParser = new PackageParser(archiveFilePath);
File sourceFile = new File(archiveFilePath);
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
- PackageParser.Package pkg = packageParser.parsePackage(sourceFile, archiveFilePath,
- metrics, 0);
+ PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics, 0);
packageParser = null;
return pkg;
}
@@ -579,18 +578,18 @@ public class PackageManagerTests extends AndroidTestCase {
PackageParser.Package pkg;
- InstallParams(String outFileName, int rawResId) {
+ InstallParams(String outFileName, int rawResId) throws PackageParserException {
this.pkg = getParsedPackage(outFileName, rawResId);
- this.packageURI = Uri.fromFile(new File(pkg.mScanPath));
+ this.packageURI = Uri.fromFile(new File(pkg.codePath));
}
InstallParams(PackageParser.Package pkg) {
- this.packageURI = Uri.fromFile(new File(pkg.mScanPath));
+ this.packageURI = Uri.fromFile(new File(pkg.codePath));
this.pkg = pkg;
}
long getApkSize() {
- File file = new File(pkg.mScanPath);
+ File file = new File(pkg.codePath);
return file.length();
}
}
@@ -691,7 +690,8 @@ public class PackageManagerTests extends AndroidTestCase {
}
}
- private PackageParser.Package getParsedPackage(String outFileName, int rawResId) {
+ private PackageParser.Package getParsedPackage(String outFileName, int rawResId)
+ throws PackageParserException {
PackageManager pm = mContext.getPackageManager();
File filesDir = mContext.getFilesDir();
File outFile = new File(filesDir, outFileName);
@@ -1343,7 +1343,7 @@ public class PackageManagerTests extends AndroidTestCase {
assertUninstalled(info);
}
} finally {
- File outFile = new File(ip.pkg.mScanPath);
+ File outFile = new File(ip.pkg.codePath);
if (outFile != null && outFile.exists()) {
outFile.delete();
}
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index abb960c..6437e07 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -42,12 +42,21 @@ $(eval $(call create-font-symlink,DroidSerif-Bold.ttf,NotoSerif-Bold.ttf))
$(eval $(call create-font-symlink,DroidSerif-Italic.ttf,NotoSerif-Italic.ttf))
$(eval $(call create-font-symlink,DroidSerif-BoldItalic.ttf,NotoSerif-BoldItalic.ttf))
+extra_font_files := \
+ DroidSans.ttf \
+ DroidSans-Bold.ttf
+
################################
# On space-constrained devices, we include a subset of fonts:
ifeq ($(SMALLER_FONT_FOOTPRINT),true)
+
droidsans_fallback_src := DroidSansFallback.ttf
-extra_font_files := DroidSans.ttf DroidSans-Bold.ttf
-else
+
+else # !SMALLER_FONT_FOOTPRINT
+
+droidsans_fallback_src := DroidSansFallbackFull.ttf
+
+ifneq ($(EXTENDED_FONT_FOOTPRINT),true)
include $(CLEAR_VARS)
LOCAL_MODULE := MTLmr3m.ttf
LOCAL_SRC_FILES := $(LOCAL_MODULE)
@@ -55,12 +64,9 @@ LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
include $(BUILD_PREBUILT)
+extra_font_files += MTLmr3m.ttf
+endif # !EXTENDED_FONT_FOOTPRINT
-droidsans_fallback_src := DroidSansFallbackFull.ttf
-extra_font_files := \
- DroidSans.ttf \
- DroidSans-Bold.ttf \
- MTLmr3m.ttf
endif # SMALLER_FONT_FOOTPRINT
################################
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index c2d5afe..1eaae65 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -219,6 +219,26 @@
</family>
<family>
<fileset>
+ <file lang="zh-CN">NotoSansHans-Regular.otf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file lang="zh-TW">NotoSansHant-Regular.otf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file lang="ja">NotoSansJP-Regular.otf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file lang="ko">NotoSansKR-Regular.otf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file>NanumGothic.ttf</file>
</fileset>
</family>
diff --git a/docs/html/auto/index.jd b/docs/html/auto/index.jd
index 2a527b3..c7a3a4e 100644
--- a/docs/html/auto/index.jd
+++ b/docs/html/auto/index.jd
@@ -2,6 +2,7 @@ page.title=Android Auto
page.viewport_width=970
fullpage=true
no_footer_links=true
+page.type=about
@jd:body
@@ -12,29 +13,23 @@ no_footer_links=true
}
</style>
-
-
-
-<div id="video-container">
- <div id="video-frame">
- <div class="video-close">
- <span id="icon-video-close">&nbsp;</span>
- </div>
- <script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
- <div id="ytapiplayer">
- <a href="http://www.youtube.com/watch?v=0xQ3y902DEQ"><img width=940
- src="https://i1.ytimg.com/vi/0xQ3y902DEQ/maxresdefault.jpg"></a><!--You need Flash player 8+ and JavaScript enabled to view this video. -->
- </div>
- </div>
-</div>
-
+<style>
+#footer {
+ display: none;
+}
+.content-footer {
+ display: none;
+}
+#hero-height {
+ height:calc(100% - 100px);
+}
+</style>
<div class="landing-body-content">
<div class="landing-hero-container">
- <div class="landing-section auto-hero">
-
+ <div id="hero-height" class="landing-section auto-hero">
<div class="landing-hero-scrim"></div>
<div class="landing-hero-wrap">
<div class="vertical-center-outer">
@@ -44,12 +39,11 @@ no_footer_links=true
<div class="landing-section-header">
<div class="landing-h1 hero">Android Auto</div>
<div class="landing-subhead hero">Entertainment and services on your dashboard</div>
- </div>
- <div class="landing-hero-description">
- <p>Maps, music, search and more in your vehicle, powered by your Android phone.
- Voice actions and feedback. Useful local information. Clean user interfaces to
- keep your eyes on the road.</p>
- </div>
+ <div class="landing-hero-description">
+ <p>Display and control your handheld app in vehicles.
+ Build apps with easy-to-use UI templates that
+ let users keep their eyes on the road.</p>
+ </div>
<div class="landing-body">
<a href="{@docRoot}auto/overview.html" class="landing-button landing-primary" style="margin-top: 40px;">
@@ -91,7 +85,26 @@ no_footer_links=true
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
- <div class="landing-section" >
+
+ <div class="landing-section">
+ <div class="wrap">
+ <div class="landing-section-header">
+ <div class="landing-h1">One Platform</div>
+ </div>
+
+ <div class="landing-body">
+ <p>Android Auto is an extension of the Android platform. You can easily adapt
+ existing apps for Android Auto and reuse many of the Android APIs and services you
+ are already familiar with.</p>
+ <p>The Android Auto platform and SDK let you write your apps only once, without having
+ to worry about vehicle-specific hardware differences like screen resolution, software
+ interfaces, knobs and touch controls. Your app runs the same on any compatible
+ vehicle.</p>
+ </div>
+ </div> <!-- end .wrap -->
+ </div> <!-- end .landing-section -->
+
+ <div class="landing-section landing-gray-background" >
<div class="wrap">
<div class="landing-section-header">
<div class="landing-h1">Minimizing Distraction</div>
@@ -107,21 +120,18 @@ no_footer_links=true
<p>Android Auto locks any device that users connect to a compatible vehicle, so drivers
interact with the device using voice actions and the vehicle’s input controls.</p>
</div>
-
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
<div class="landing-section landing-white-background">
<div class="wrap">
<div class="landing-section-header">
- <div class="landing-h2">Building an Ecosystem</div>
- <div class="landing-body landing-align-center">
- <p class="landing-small">
+ <div class="landing-h1">Building an Ecosystem</div>
+ </div>
+ <div class="landing-body landing-align-center">
+ <p style="margin-bottom:20px">
Android Auto is coming soon to new cars from these manufacturers:
</p>
- </div>
- </div>
-
</div>
<div style="width:800px;margin:0 auto">
<img src="{@docRoot}auto/images/carlogos.png" alt="Partners" />
@@ -129,6 +139,24 @@ no_footer_links=true
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
+
+ <div class="landing-section" >
+ <div class="wrap">
+ <div class="landing-section-header">
+ <div class="landing-h1">Developer Stories</div>
+ </div>
+
+ <div class="landing-body">
+ <p>We're working with developers to bring many popular apps to Android Auto:</p>
+ <p>
+
+ </p>
+ </div>
+
+ </div> <!-- end .wrap -->
+ </div> <!-- end .landing-section -->
+
+
<div class="landing-section" style="background-color:#f5f5f5">
<div class="wrap">
<div class="landing-section-header">
@@ -144,18 +172,19 @@ no_footer_links=true
and tools to make your existing apps compatible with Android Auto. The first version
of the SDK will provide templates and APIs for music, podcast, live radio, and audio
news apps, as well as limited voice actions.</p>
- <p>Future versions of the Android Auto SDK will include support for a selection of
+ <p style="margin-bottom:40px">
+ Future versions of the Android Auto SDK will include support for a selection of
Android notifications, additional voice actions, and templates and APIs for
messaging, communication, local search, and more.</p>
- <a target="_blank" href="http://g.co/androidweardev">
+ <a target="_blank" href="http://g.co/androidautodev">
<img class="landing-social-image" src="//www.google.com/images/icons/product/gplus-128.png" alt="+Android Auto Developers">
</a>
- <p>G+ Community</p>
+ <p style="margin-bottom:5px">G+ Community</p>
<p class="landing-small">
Join the Android Auto developer community on Google+ to stay involved, get the
latest updates, and exchange experiences with other developers.
- <a target="_blank" href="http://g.co/androidweardev">+Android Auto Developers</a>
+ <a target="_blank" href="http://g.co/androidautodev">+Android Auto Developers</a>
</p>
</div>
@@ -187,7 +216,7 @@ no_footer_links=true
<script>
$("a.landing-down-arrow").on("click", function(e) {
$("body").animate({
- scrollTop: $(".wear-hero").height() + 76
+ scrollTop: $(".auto-hero").height() + 120
}, 1000, "easeOutQuint");
e.preventDefault();
});
diff --git a/docs/html/auto/overview.jd b/docs/html/auto/overview.jd
index ee0629a..8748bff 100644
--- a/docs/html/auto/overview.jd
+++ b/docs/html/auto/overview.jd
@@ -6,6 +6,15 @@ page.metaDescription=Android Auto
@jd:body
+<style>
+.jd-descr {
+ height:auto;
+}
+#copyright {
+ margin-top:-35px;
+}
+</style>
+
<div style="width:780px; margin:0 auto;">
<div id="qv-wrapper">
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 6394b73..606baf8 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -7,23 +7,35 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3
@jd:body
-<div class="wrap">
- <div style="margin-top:40px" class="resource-widget resource-flow-layout col-16" data-query="collection:launch/static" data-sortorder=""
-data-cardsizes="18x12,6x2, 6x2, 6x2" data-maxresults="4"></div>
-</div>
-<div style="height:30px"></div>
+<!-- Top full-bleed carousel -->
+<div class="home-new-carousel-1">
+ <div class="fullscreen-carousel-content">
+ <div class="vcenter">
+ <div class="wrap clearfix">
+ <div class="resource-widget resource-flow-layout wrap col-16"
+ data-query="collection:index/primary"
+ data-resourceStyle="card"
+ data-sortOrder="-timestamp"
+ data-numStacks="3"
+ data-maxResults="4"
+ data-cardSizes="18x6,6x2,6x2,6x2">
+ </div> <!-- end .resource-widget -->
+ </div> <!-- end .wrap -->
+ </div> <!-- end .vcenter -->
+ </div> <!-- end .fullscreen-carousel-content -->
+</div> <!-- end .fullscreen-carousel -->
+
+<div class="actions-bar">
+ <div class="wrap">
+ <div class="actions">
+ <div><a href="{@docRoot}sdk/index.html">Get the SDK</a></div>
+ <div><a href="{@docRoot}samples/index.html">Browse Samples</a></div>
+ <div><a href="//www.youtube.com/user/androiddevelopers">Watch Videos</a></div>
+ <div><a href="{@docRoot}distribute/googleplay/developer-console.html">Manage Your Apps</a></div>
+ </div><!-- end .actions -->
+ </div><!-- end .wrap -->
+</div><!-- end .actions-bar -->
- <div style="margin-top:600px" class="actions-bar">
- <div class="wrap">
- <div class="actions">
- <div><a href="http://developer.android.com/sdk/index.html">Download SDK</a></div>
- <div><a href="http://developer.android.com/about/start.html">Get Started</a></div>
- <div><a href="http://developer.android.com/samples/index.html">Sample Code</a></div>
- <div><a href="http://developer.android.com/design/videos/index.html">Videos</a></div>
- <div><a href="https://developer.android.com/distribute/googleplay/developer-console.html">Developer Console</a></div>
- </div><!-- end .actions -->
- </div><!-- end .wrap -->
- </div><!-- end .actions-bar -->
<div class="landing-rest-of-page">
@@ -35,12 +47,14 @@ data-cardsizes="18x12,6x2, 6x2, 6x2" data-maxresults="4"></div>
Android runs on hundreds of millions of handheld devices around the world, <br /> and it now supports these exciting, new form-factors.
</div>
</div>
-
<div class="landing-body">
<div class="landing-breakout cols">
+ <!-- <div class="resource-widget resource-flow-layout col-16" data-query="collection:index/devices"
+ data-sortorder="" data-cardsizes="6x6, 6x6, 6x6" data-maxresults="3"></div>-->
+
<div class="col-3-wide">
- <img src="{@docRoot}tv/images/placeholder-square.png" alt="">
+ <img src="" alt="">
<p>Wear</p>
<p class="landing-small">
@@ -52,7 +66,7 @@ data-cardsizes="18x12,6x2, 6x2, 6x2" data-maxresults="4"></div>
</div>
<div class="col-3-wide">
- <img src="{@docRoot}tv/images/placeholder-square.png" alt="">
+ <img src="" alt="">
<p>TV</p>
<p class="landing-small">
@@ -64,7 +78,7 @@ data-cardsizes="18x12,6x2, 6x2, 6x2" data-maxresults="4"></div>
</div>
<div class="col-3-wide">
- <img src="{@docRoot}tv/images/placeholder-square.png" alt="">
+ <img src="" alt="">
<p>Auto</p>
<p class="landing-small">
@@ -78,6 +92,5 @@ data-cardsizes="18x12,6x2, 6x2, 6x2" data-maxresults="4"></div>
</div>
</div>
-
</div> <!-- end .wrap -->
</div> <!-- end .landing-section --> \ No newline at end of file
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index 5ee01f3..ca19c02 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -1,4 +1,21 @@
var RESOURCE_COLLECTIONS = {
+ "index/primary": {
+ "title": "",
+ "resources": [
+ "preview/index.html",
+ "preview/material/index.html",
+ "preview/material/index.html",
+ "preview/material/index.html"
+ ]
+ },
+ "index/devices": {
+ "title": "",
+ "resources": [
+ "wear/index.html",
+ "tv/index.html",
+ "auto/index.html"
+ ]
+ },
"launch/static": {
"title": "",
"resources": [
diff --git a/docs/html/jd_tag_helpers.js b/docs/html/jd_tag_helpers.js
index ca01386..b0fe67a 100644
--- a/docs/html/jd_tag_helpers.js
+++ b/docs/html/jd_tag_helpers.js
@@ -7,6 +7,7 @@ function mergeArrays() {
}
var ALL_RESOURCES = mergeArrays(
+ ABOUT_RESOURCES,
DESIGN_RESOURCES,
DISTRIBUTE_RESOURCES,
GOOGLE_RESOURCES,
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index 7c48340..40618a3 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -170,65 +170,63 @@ information.</p>
<h3 id="MaterialDesign">Material design support</h3>
-<p>L adds support for the material design style. You can create
+<p>The L Developer Preview adds support for the material design style. You can create
material design apps that are visually dynamic and have UI element transitions
which feel natural and delightful to users. This support includes:</p>
<ul>
- <li>Material design theme
- <li>View shadows
- <li>{@code RecyclerView} widget
- <li>Drawable animation and styling effects
- <li>Material design animation and activity transitions effects
- <li>Ability to define animators to run on the view, with
-{@code android.animation.StateListAnimator}.
- <li>Ability to change your status bar color to match the action bar and other
-UI elements with {@code android.view.Window.setStatusBarColor()}.
+ <li>The Material theme</li>
+ <li>View shadows</li>
+ <li>The {@code RecyclerView} widget</li>
+ <li>Drawable animation and styling effects</li>
+ <li>Material design animation and activity transition effects</li>
+ <li>Animators for view properties based on the state of a view</li>
+ <li>Customizable UI widgets and app bars with color palettes that you control</li>
</ul>
<p>To learn more about adding material design functionality to your app, see
<a href="{@docRoot}preview/material/index.html">Material design on Android</a>.</p>
<h3 id="LockscreenNotifications">Lockscreen notifications</h3>
-<p>Lockscreens in L Developer Preview have the ability to present notifications.
-User can choose via <em>Settings</em> whether to allow sensitive notification
+<p>Lockscreens in the L Developer Preview have the ability to present notifications.
+Users can choose via <em>Settings</em> whether to allow sensitive notification
content to be shown over a secure lockscreen.</p>
<p>Your app can control the level of detail visible when its notifications are
-displayed over the secure lockscreen.To control the visibility level, call
+displayed over the secure lockscreen. To control the visibility level, call
{@code android.app.Notification.Builder.setVisibility()} and specify one of these
values:</p>
<ul>
<li>{@code VISIBILITY_PRIVATE}. Shows basic information, such as the
notification’s icon, but hides the notification’s full content. If you want to
provide a redacted public version of your notification for the system to display
-on a secure lockscreen, set the public notification object in the publicVersion
-field.
+on a secure lockscreen, set the public notification object in the <code>publicVersion</code>
+field.</li>
<li>{@code VISIBILITY_PUBLIC}. Shows the notification’s full content. This is
- the system default if visibility is left unspecified.
+ the system default if visibility is left unspecified.</li>
<li>{@code VISIBILITY_SECRET}. Shows only the most minimal information,
-excluding even the notification’s icon.
+excluding even the notification’s icon.</li>
</ul>
<h3 id="NotificationsMetadata">Notifications metadata</h3>
<p>The L Developer Preview uses metadata associated with your app notifications
to more intelligently sort your notifications. The metadata you set also
-controls how the system presents your app notifications when the user is in Do
-not disturb mode. When constructing your notification, you can call the
+controls how the system presents your app notifications when the user is in <em>Do
+not disturb</em> mode. When constructing your notification, you can call the
following methods in {@code android.app.Notification.Builder}:</p>
<ul>
<li>{@code setCategory()}. Allows the system to handle your app notifications
-in Do not disturb mode (for example, if your notification represents an
-incoming call, instant message, or alarm).
+in <em>Do not disturb mode</em> (for example, if your notification represents an
+incoming call, instant message, or alarm).</li>
<li>{@code setPriority()}. Notifications with the priority field set to
{@code PRIORITY_MAX} or {@code PRIORITY_HIGH} will appear in a small floating
-window if the notification also has sound or vibration.
+window if the notification also has sound or vibration.</li>
<li>{@code addPerson()}. Allows you to add a list of people to a notification.
Your app can use this to signal to the system that it should group together
notifications from the specified people, or rank notifications from these
-people as being more important.
+people as being more important.</li>
</ul>
-<h3 id="Recents">Concurrent documents and activities in Recents screen</h3>
+<h3 id="Recents">Concurrent documents and activities in the Recents screen</h3>
<p>In previous releases, the
<a href="{@docRoot}design/get-started/ui-overview.html">Recents screen</a>
diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd
index 3b5a4f8..84a2d85 100644
--- a/docs/html/preview/index.jd
+++ b/docs/html/preview/index.jd
@@ -1,7 +1,8 @@
-heropage.title=Android L Developer Preview
+page.title=Android L Developer Preview
page.viewport_width=970
fullpage=true
no_footer_links=true
+page.type=about
@jd:body
diff --git a/docs/html/preview/material/get-started.jd b/docs/html/preview/material/get-started.jd
index 27daf5c..c527550 100644
--- a/docs/html/preview/material/get-started.jd
+++ b/docs/html/preview/material/get-started.jd
@@ -73,6 +73,18 @@ For more information, see <a href="{@docRoot}preview/material/compatibility.html
the theme for the element and any elements inflated below it, which is useful to alter theme
color palettes in a specific portion of an interface.</p>
+<h3 style="margin-top:25px;">Customize the Status Bar</h3>
+
+<p>The Material theme lets you easily customize the status bar, so you can specify a
+color which fits your brand and provides enough contrast to show the white status icons. To
+set a custom color for the status bar, use the <code>android:statusBarColor</code> attribute when
+you extend the Material theme.</p>
+
+<p>To handle the color of the status bar yourself (for example, by adding a gradient in the
+background), set the <code>android:statusBarColor</code> attribute to
+<code>&#64;android:color/transparent</code>. You can also use the
+<code>Window.setStatusBarColor</code> method for animations or fading.</p>
+
<h2 style="margin-top:35px">Material Design</h2>
diff --git a/docs/html/preview/material/index.jd b/docs/html/preview/material/index.jd
index e54ba08..468e4bd 100644
--- a/docs/html/preview/material/index.jd
+++ b/docs/html/preview/material/index.jd
@@ -1,9 +1,9 @@
page.title=Material Design
+page.type=design
@jd:body
-
-<p>The Android L Developer Preview includes support for material design apps. Material design
+<p itemprop="description">The Android L Developer Preview includes support for material design apps. Material design
is a comprehensive guide for visual, motion, and interaction design across platforms and devices.
To use material design in your Android apps, follow the guidelines defined in the
<a href="">material design specification</a> and use the new components and functionality
diff --git a/docs/html/preview/preview_toc.cs b/docs/html/preview/preview_toc.cs
index 377c487..8f6f8c1 100644
--- a/docs/html/preview/preview_toc.cs
+++ b/docs/html/preview/preview_toc.cs
@@ -22,8 +22,45 @@
<li><a href="<?cs var:toroot ?>preview/material/ui-widgets.html">UI Widgets</a></li>
<li><a href="<?cs var:toroot ?>preview/material/views-shadows.html">Views and Shadows</a></li>
<li><a href="<?cs var:toroot ?>preview/material/animations.html">Animations</a></li>
+ <li><a href="<?cs var:toroot ?>preview/material/compatibility.html">Compatibility</a></li>
</ul>
</li>
+
+ <li class="nav-section">
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot ?>preview/tv/index.html">TV</a>
+ </div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>preview/tv/start/index.html">
+ Get Started</a></li>
+ <li class="nav-section">
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot ?>preview/tv/ui/index.html">
+ User Interface</a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>preview/tv/ui/layouts.html">
+ Layouts</a></li>
+ <li><a href="<?cs var:toroot ?>preview/tv/ui/navigation.html">
+ Navigation</a></li>
+ <li><a href="<?cs var:toroot ?>preview/tv/ui/browse.html">
+ BrowseFragment</a></li>
+ <li><a href="<?cs var:toroot ?>preview/tv/ui/details.html">
+ DetailsFragment</a></li>
+ <li><a href="<?cs var:toroot ?>preview/tv/ui/in-app-search.html">
+ In-App Search</a></li>
+ <li><a href="<?cs var:toroot ?>preview/tv/ui/recommendations.html">
+ Recommendations</a></li>
+ </ul>
+ </li>
+ <li><a href="<?cs var:toroot ?>preview/tv/games/index.html">
+ Games on TV</a></li>
+ <li><a href="<?cs var:toroot ?>preview/tv/start/hardware-features.html">
+ Hardware Features</a></li>
+ <li><a href="<?cs var:toroot ?>preview/tv/adt-1/index.html">
+ ADT-1</a></li>
+ </ul>
+ </li>
+
<li class="nav-section">
<div class="nav-section-header empty">
<a href="<?cs var:toroot ?>preview/samples.html">Samples</a>
diff --git a/docs/html/preview/setup-devices.jd b/docs/html/preview/setup-devices.jd
index 9ffe9f4..0654685 100644
--- a/docs/html/preview/setup-devices.jd
+++ b/docs/html/preview/setup-devices.jd
@@ -1,8 +1,7 @@
page.title=Setting Up Hardware and AVDs
-
@jd:body
-<p>The Android 5.0 developer preview provides you with 32-bit system images
+<p>The Android L developer preview provides you with 32-bit system images
to flash the following devices:
</p>
@@ -15,8 +14,9 @@ to flash the following devices:
experimental 64-bit system images along with standard 32-bit system images.
</p>
-<h2>Flashing Devices</h2>
+<h2>Installing the L Preview System Image</h2>
+<!-- Will we get an official warning text from the lawyercats? Is this it? -->
<p class="warning"><b>Warning</b>: This is a preview version of the Android
system image, and is subject to change. Your use of this system image is
governed by the Android SDK Preview License Agreement. The Android preview
@@ -26,56 +26,71 @@ Android system image is not subject to the same testing as the factory OS and
can cause your phone and installed services and applications to stop working.
</p>
-<p>
-Click through Terms of Service:
-https://docs.google.com/a/google.com/document/d/1OixnM1Q890ExOzDB3Z-FDD6Sb2kF4uZQiMxsYVII8F0/edit?usp=sharing
+<p><!-- Will this link change before we publish (to a clean version of the doc)?
+ Or will we scrub the doc's comments & revision history? -->
+<a href="https://docs.google.com/a/google.com/document/d/1OixnM1Q890ExOzDB3Z-FDD6Sb2kF4uZQiMxsYVII8F0/edit?usp=sharing">L
+Preview Terms of Service</a>
</p>
-<h3>To the Developer Preview</h3>
+
<ol>
<li>Download and extract the Android Developer Preview package to a directory
- referred to as <code>&lt;extracted_images&gt;</code>.</li>
- <li>Connect your powered-off Android device to your development machine and
- press and hold the following buttons until your device is in fastboot mode:
+ (which we'll call <code>&lt;l_download_dir&gt;</code> in these
+ instructions).</li>
+ <li>Connect your powered-off Android device to your development machine. Put
+ the device in fastboot mode by pressing and holding the following buttons:
<ul>
- <li>For Nexus 5: vol down + vol up + power</li>
- <li>For Nexus 7: vol down + power </li>
+ <li><strong>Nexus 5:</strong> <i>volume down</i> + <i>volume up</i> +
+ <i>power</i></li>
+ <li><strong>Nexus 7:</strong> <i>volume down</i> + <i>power</i> </li>
</ul>
- <p class="note">Alternatively, run <code>adb reboot bootloader</code>
- with USB debugging turned on to enter fastboot mode.
- The device must be booted up if you’re using this method.</p>
+ <p class="note">Alternatively, you can enter fastboot mode by booting up
+ the device and running <code>adb reboot bootloader</code> with USB debugging
+ turned on.</p>
</li>
<li>Follow the instructions at
- <a href="https://developers.google.com/android/nexus/images">developers.google.com/android</a>
+ <a href="https://developers.google.com/android/nexus/images#instructions">developers.google.com/android</a>
to set up your system for flashing devices.</li>
- <li>Run the <code>&lt;extracted-images&gt;/flash-all</code> script
+ <li>Run the <code>&lt;l_download_dir&gt;/flash-all</code> script
corresponding to your platform. This script flashes all of the system data
- onto the phone.</li>
- <li>(Optional) After flashing is complete, run <code>fastboot oem lock</code>
- with the device in fastboot mode to lock it again. This does not wipe your
- device, but if you want to flash again, you need to run <code>fastboot oem
- unlock</code>, which wipes your device. We recommend you leave the
+ onto the phone.</li> <!-- Confirm names of flash scripts -->
+ <li>(Optional) After flashing is complete, lock your device's bootloader by
+ putting it in fastboot mode and running <code>fastboot oem lock</code>.
+ (This does not wipe your device.) Once you do this, you will not be able to
+ flash your device until you run run <code>fastboot oem unlock</code>,
+ which unlocks the bootloader and wipes your device. We recommend you leave the
bootloader unlocked until you are done with flashing the device.</li>
</ol>
-<h3>To factory specifications</h3>
+<h3>Reverting a Device to Factory Specifications</h3>
- <p>Go to <a href="http://developers.google.com/android/nexus/images">developers.google.com/android</a>
- and download the image you want to flash to for your device.
- Follow the instructions there to flash the image to your device.</p>
+ <p>If you want to uninstall the L Preview and revert the device to factory
+specifications, go to <a href="http://developers.google.com/android
+/nexus/images">developers.google.com/android</a> and download the image you want
+to flash to for your device. Follow the instructions on that page to flash the
+image to your device.</p>
<h2>Setting up an AVD</h2>
-You can set up Android Virtual Devices (AVD) and use the emulator to
-build and test apps with:
+
+<p>You can set up <a href="{@docRoot}tools/devices/">Android Virtual Devices
+(AVD)</a> and use the emulator to build and test apps with the L Preview.</p>
+
+<p>To create an AVD with the AVD Manager:</p>
<ol>
- <li></li>
- <li></li>
- <li></li>
- <li></li>
- <li></li>
- <li></li>
- <li></li>
- <li></li>
-</ol> \ No newline at end of file
+ <li>Install the L Preview SDK in your development environment, as described
+ in <a href="{@docRoot}preview/setup-sdk.html">Setting Up the Preview
+ SDK.</a></li>
+ <li>Follow the steps in
+ <a href="{@docRoot}tools/devices/managing-avds.html">Managing AVDs with AVD
+ Manager</a>. Use the following settings:
+ <ul>
+ <li><b>Device:</b> Either Nexus 5 or Nexus 7</li>
+ <li><b>Target:</b> <!-- Confirm exact text when we have final distro -->
+ Android L (Preview) - API Level L</li>
+ </ul>
+ <!-- Confirm this works when you can download image through SDK manager! -->
+ </li>
+</ol>
+
diff --git a/docs/html/preview/setup-sdk.jd b/docs/html/preview/setup-sdk.jd
index e0ef40d..32a33b6 100644
--- a/docs/html/preview/setup-sdk.jd
+++ b/docs/html/preview/setup-sdk.jd
@@ -1,32 +1,36 @@
page.title=Setting Up the Preview SDK
-
@jd:body
-<p>The Preview SDK is available from the Android SDK Manager. This document assumes that you
-are familiar with Android Development, such as using the Android SDK Manager and creating
-projects. If you're new to Android, see
-<a href="{@docRoot}training/basics/firstapp/index.html">Building Your First App</a> training lesson first.</a></p>
+<p>The Preview SDK is available from the Android SDK Manager. <!-- Not yet! -->
+This document assumes that you are familiar with Android app development, such
+as using the Android SDK Manager and creating projects. If you're new to
+Android, see <a href="/training/basics/firstapp/index.html">Building Your First
+App</a> training lesson first.</a></p>
<h2>Download the SDK</h2>
<ol>
<li>Start the Android SDK Manager.</li>
- <li>In the <b>Tools</b> section, select the latest Android <b>SDK Tools</b>, <b>Platform-tools</b>, and <b>Build-tools</b>.</li>
- <li>Select everything under the <b>Android L Developer Preview</b> section and click <b>Install packages...</b></li>
- <li>Accept the Licensing Agreement for all of the packages and click <b>Install</b>.</li>
+ <li>In the <b>Tools</b> section, select the latest Android <b>SDK Tools</b>,
+ <b>Platform-tools</b>, and <b>Build-tools</b>.</li>
+ <!-- Android L not yet showing up in Android SDK Manager... -->
+ <li>Select everything under the <b>Android L Developer Preview</b> section and
+ click <b>Install packages...</b></li>
+ <li>Accept the Licensing Agreement for all of the packages and click
+ <b>Install</b>.</li>
</ol>
<h2>Set up your environment</h2>
<ol>
<li>Create a new Android project with the following properties:
- <dl>
+ <ul>
<li>Minimum SDK Version: L</li>
<li>Target SDK Version: L</li>
<li>Build Target: L</li>
- </dl>
+ </ul>
</li>
- <li>Set the theme for your app to use <code>Theme.Material</code>
- <li>etc</li>
-</ol>
+ <li>Choose the theme <code>Theme.Material</code>
+ <!-- put in name as it appears in Eclipse menu? -->
+</ol>
diff --git a/docs/html/preview/tv/adt-1/index.jd b/docs/html/preview/tv/adt-1/index.jd
new file mode 100644
index 0000000..062968e
--- /dev/null
+++ b/docs/html/preview/tv/adt-1/index.jd
@@ -0,0 +1,319 @@
+page.title=ADT-1 Developer Kit
+page.tags="emote","e-mote","adt"
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#faq">ADT-1 Frequently Asked Questions</a></li>
+ <li><a href="#regulatory">Regulatory Disclosures</a></li>
+ <li><a href="#safety">Important Safety Instructions</a></li>
+ </ol>
+</div>
+</div>
+
+<p class="note">
+ <strong>!FIX: FOR REVIEW ONLY:</strong> link to <a href="request.html">ADT-1 Request</a> page.
+</p>
+
+
+<p>The ADT-1 Developer Kit is streaming media player and game controller designed for running
+and testing app built for Android TV. The kit is provided to a limited number of developers
+who are interested in building new apps or extending their existing apps to run on the Android TV
+platform.</p>
+
+<p class="note">
+ <strong>Note:</strong> The ADT-1 kit <em>is not required</em> for building and testing apps
+ for Android TV. You can build apps for TV and test them using an emulator for TV devices. The
+ L-Preview SDK includes all the software needed to build TV apps and an emulator for running and
+ testing them. For more information, see the
+ <a href="{@docRoot}preview/tv/start/index.html">Get Started</a> guide for TV apps.
+</p>
+
+<h2 id="faq">ADT-1 Frequently Asked Questions</h2>
+
+<p>
+ <strong>How do I put the gamepad that came with my ADT-1 into pairing mode?</strong>
+</p>
+<p>Press and hold the Back and Home buttons together for about three seconds, until all four
+ blue LEDs flash together. When the LEDs are flashing, the gamepad is in pairing mode.</p>
+
+<p>
+ <strong>How do I use the gamepad with the on-screen keyboard?</strong>
+</p>
+<p>Use the D-pad or left joystick to move the cursor, and press A to select. Press X to delete a
+ character, and press Y to insert a space. Also, you can press the right joystick to toggle caps
+ lock, and press the left joystick to show additional symbols.</p>
+
+<p>
+ <strong>How do I perform a hard reset of ADT-1?</strong>
+</p>
+<p>Unplug the power cable from the back of ADT-1. Press and hold the small, round button on the
+ back of ADT-1 as you re-insert the power cable, and continue to hold the small round button. The
+ LED will begin flashing red for a few seconds, then change to multi-color cycle. When the LED
+ starts the multi-color cycle, release the small, round button, and ADT-1 will boot. Note: this is
+ a factory data reset, thus all downloaded apps, system and app data, and account settings will be
+ lost.</p>
+
+<p>
+ <strong>How do I do a soft reset?</strong>
+</p>
+<p>Go to Settings - Device - Factory data reset, and select ‘Reset device’. Note: this is a
+ factory data reset, thus all downloaded apps, system and app data, and account settings will be
+ lost.</p>
+
+<p>
+ <strong>How do I turn my device on?</strong>
+</p>
+<p>Plug in the included power cable into the back of ADT-1. There is no on/off switch.</p>
+
+<p>
+ <strong>How do I completely turn my device off? </strong>
+</p>
+<p>Unplug in the included power cable from the back of ADT-1. There is no on/off switch.
+ However, ADT-1 will begin sleeping (daydream) based on user settings in Display -&gt; Daydream.</p>
+
+<p>
+ <strong>How do I connect to the network?</strong>
+</p>
+<p>ADT-1 has both Wi-Fi and Ethernet for connecting to your network. To change your Wi-Fi
+ network, go to Settings -&gt; Wi-Fi. To use an Ethernet network connection, simply plug in an
+ Ethernet cable (that is connected to your network) into the port on the back of ADT-1.</p>
+
+<p>
+ <strong>How do I use the developer cable?</strong>
+</p>
+<p>The developer cable has three connectors: a small, male power connector that plugs into the
+ power port on the back of ADT-1, a standard male USB-A connector that connects your PC, and a
+ small, female power connector that the included power supply plugs into.</p>
+
+<p>
+ <strong>Is there an app for phone and tablet that I can use to control ADT-1?</strong>
+</p>
+<p>Yes, you can download the remote control app from Android phones and tablets here.</p>
+
+<p>
+ <strong>Can I connect a USB keyboard/mouse to ADT-1?</strong>
+</p>
+<p>Yes, you can connect a USB keyboard/mouse to the USB port on the back of ADT-1. Note: not all
+ manufacturers/models are guaranteed to work.</p>
+<hr />
+<p>
+ Press the small, round button on the back of ADT-1 to make it search for Bluetooth devices in
+ pairing mode. If multiple accessories are found, press the small, round button to select the
+ device you want to pair. Pairing will happen automatically after a few seconds. To pair Bluetooth
+ devices to ADT-1 from the UI, go to <strong>Settings &gt; Remote &amp; Accessories &gt;
+ Add accessory</strong>.
+</p>
+
+
+
+<h2 id="regulatory">Regulatory Disclosures</h2>
+
+
+<p>Model: W2</p>
+<p>FCC ID: A4R-W2</p>
+<p>IC: 10395A-W2</p>
+<p>U.S. Federal Communications Commission Notices</p>
+<p>To satisfy FCC and IC exposure requirements, a separation distance of at least 20 cm should
+ be maintained between the antenna of this device and persons during device operation. Operations
+ at closer than this distance are not recommended.</p>
+<p>The antenna used for this transmitter must not be co-located in conjunction with any other
+ antenna or transmitter.</p>
+<p>This equipment has been tested and found to comply with the limits for a Class B digital
+ device, pursuant to part 15 of the FCC Rules. These limits are designed to provide reasonable
+ protection against harmful interference in a residential installation. This equipment generates,
+ uses and can radiate radio frequency energy and, if not installed and used in accordance with the
+ instructions, may cause harmful interference to radio communications. However, there is no
+ guarantee that interference will not occur in a particular installation. If this equipment does
+ cause harmful interference to radio or television reception, which can be determined by turning
+ the equipment off and on, the user is encouraged to try to correct the interference by one or more
+ of the following measures:</p>
+<p>—Reorient or relocate the receiving antenna.</p>
+<p>—Increase the separation between the equipment and receiver.</p>
+<p>—Connect the equipment into an outlet on a circuit different from that to which the receiver
+ is connected.</p>
+<p>—Consult the dealer or an experienced radio/ TV technician for help.</p>
+<p>This device complies with part 15 of the FCC Rules. Operation is subject to the following two
+ conditions: (1) This device may not cause harmful interference, and (2) this device must accept
+ any interference received, including interference that may cause undesired operation.</p>
+<p>Changes or modifications not expressly approved by Google Inc. could void the user's
+ authority to operate the equipment.</p>
+<p>Industry Canada Notices</p>
+<p>This device complies with Industry Canada licence-exempt RSS standard(s). Operation is
+ subject to the following two conditions: (1) this device may not cause interference, and (2) this
+ device must accept any interference, including interference that may cause undesired operation of
+ the device.</p>
+<p>Under Industry Canada regulations, this radio transmitter may only operate using an antenna
+ of a type and maximum (or lesser) gain approved for the transmitter by Industry Canada. To reduce
+ potential radio interference to other users, the antenna type and its gain should be so chosen
+ that the equivalent isotropically radiated power (e.i.r.p.) is not more than that necessary for
+ successful communication.</p>
+<p>The radiated output power of the Wireless Device is below the Industry Canada (IC) radio
+ frequency exposure limits. The Wireless Device should be used in such a manner such that the
+ potential for human contact during normal operation is minimized.</p>
+
+<hr />
+
+<p>CAN ICES-3 (B)/NMB-3(B)</p>
+<p>
+ <u>Avis d’<em>Industrie Canada</em></u>
+</p>
+<p>
+ Le présent appareil est conforme aux <em>CNR</em> d'Industrie Canada applicables aux appareils
+ radio exempts de licence. L'exploitation est autorisée aux deux conditions suivantes : (1)
+ l'appareil ne doit pas produire de brouillage, et (2) l'appareil doit accepter tout brouillage
+ radioélectrique subi, même si le brouillage est susceptible d'en compromettre le fonctionnement.
+</p>
+<p>
+ En vertu de la règlementation d’<em>Industrie Canada</em>, cet émetteur radio peut
+ fonctionner avec une antenne d'un type et d'un gain maximal (ou inférieur) approuvé pour
+ l'émetteur par <em>Industrie Canada</em>. Dans le but de réduire les risques de brouillage
+ radioélectrique à l'intention des autres utilisateurs, il faut choisir le type d'antenne et son
+ gain de sorte que la puissance isotrope rayonnée équivalente (p.i.r.e.) ne dépasse pas l'intensité
+ nécessaire à l'établissement d'une communication satisfaisante.
+</p>
+<p>
+ La puissance rayonnée en sortie de l'appareil sans fil est inférieure aux limites fixées par
+ <em>Industrie Canada</em> en matière d'exposition aux radiofréquences. L'appareil sans fil
+ doit être utilisé de sorte que la possibilité d'un contact humain pendant le fonctionnement
+ normal soit limitée.
+</p>
+
+
+<h2 id="safety">Important Safety Instructions</h2>
+
+<p>
+ <strong>WARNING:</strong> Read all safety information below before using this device to avoid
+ injury.
+</p>
+<ul>
+ <li><p>Do not install near heat sources, such as heaters and other devices.</p></li>
+ <li><p>Use in a well-ventilated area and plug power adapter into an easily accessible
+ outlet. Only use this device with the provided power adapter.</p></li>
+ <li><p>The device has no on/off switch. To disconnect from power, you must unplug the
+ power adapter.</p></li>
+ <li><p>Only use indoors and do not expose to rain, liquid, moisture, excessive heat, or
+ naked flame.</p></li>
+ <li><p>Clean only with a dry cloth.</p></li>
+</ul>
+<p>
+ <strong>WARNING:</strong> Playing video games has been linked to injuries in some
+ users. Read all safety and health information below before using the gamepad to avoid possible
+ injury.
+</p>
+
+<p><u>Photosensitive Seizures</u></p>
+
+<p>
+ A very small percentage of people may experience a seizure when exposed to certain visual images,
+ including flashing lights or patterns that may appear in some video games, even people who have no
+ history of seizures or epilepsy. These seizures have a variety of symptoms, including
+ lightheadedness, altered vision, disorientation, loss of awareness, involuntary movements, loss of
+ consciousness, or convulsions. If you experience any of these symptoms, <u>stop gaming
+ immediately and consult your doctor</u>.
+</p>
+
+<p><u>Ergonomics</u></p>
+
+<p>Long periods of repetitive motion using incorrect body positioning may be associated with
+ physical discomfort and injuries to nerves, tendons, and muscles. If during or after gaming you
+ feel pain, numbness, weakness, swelling, burning, cramping, or stiffness,<u>stop gaming
+ and consult your doctor</u>.
+
+<p>
+ <strong>Healthy Gaming</strong>
+</p>
+
+<p>To reduce risk of seizures or injury, take the following precautions:</p>
+
+<ul>
+ <li><p>Sit as far away from the TV screen as possible.</p></li>
+ <li><p>Play in a well-lit room.</p></li>
+ <li><p>Do not play when you are drowsy or fatigued.</p></li>
+ <li><p>Take 10-15 minute breaks every hour if playing video games and avoid prolonged
+ gaming.</p></li>
+</ul>
+
+<p>
+ <strong>Do Not Attempt Repairs Yourself</strong>
+</p>
+
+<p>There are no user-serviceable parts inside. Do not attempt to open or disassemble.</p>
+
+<p>Failure to follow these safety instructions could result in fire, electric shock, damage to
+ the device or other property, or personal injury.</p>
+
+<hr />
+
+<p>
+ <strong>Importantes instructions concernant la sécurité</strong>
+</p>
+
+<p>
+ <strong>ATTENTION:</strong> Veuillez lire toutes les informations de sécurité énoncées ci-bas
+ avant d’utiliser l’appareil pour éviter des blessures.
+</p>
+
+<ul>
+ <li><p>Ne pas installer à proximité d’une source de chaleur telle une chaufferette ou un
+ autre appareil similaire.</p></li>
+ <li><p>Utiliser dans un endroit bien aéré et brancher l’adaptateur électrique dans une
+ prise de courant facilement accessible.</p></li>
+ <li><p>L’appareil ne possède aucun interrupteur marché/arrêt. Pour mettre l’appareil hors
+ tension, il faut débrancher l’appareil de la prise de courant.</p></li>
+ <li><p>Utiliser l’appareil uniquement à l’intérieur et ne pas l’exposer à la pluie, à des
+ substances liquides, à l’humidité, à la chaleur excessive ou à une flamme.</p></li>
+ <li><p>Nettoyer uniquement avec un linge sec.</p></li>
+</ul>
+
+<p>
+ <strong>ATTENTION:</strong> Le fait de jouer à des jeux vidéo a été relié à des blessures chez certains
+ utilisateurs. Afin d’éviter de possibles blessures, veuillez lire toutes les informations
+ concernant la sécurité et la santé énoncées ci-bas avant d’utiliser la tablette de jeu.
+</p>
+
+<p><u>Épilepsie photosensible</u></p>
+
+<p>L’exposition à certaines images visuelles, incluant les lumières ou motifs clignotants qui
+ peuvent apparaître dans certains jeux vidéo, peut provoquer chez un très faible pourcentage de
+ personnes une crise d’épilepsie, et ce, même si ces personnes n’ont aucun historique de crises ou
+ d’épilepsie. Ces crises comportent divers symptômes tels que des étourdissements, une vision
+ altérée, un sentiment de désorientation, la perte de conscience, des mouvements involontaires, la
+ perte de connaissance ou de conscience ou des convulsions. Si vous ressentez quelconque de ces
+ symptômes, <u>cessez de jouer immédiatement et consultez votre médecin</u>.</p>
+
+<p><u>Ergonomie</u></p>
+
+<p>Les longues périodes de mouvements répétitifs effectués dans une position corporelle
+ inadéquate peuvent mener à un inconfort physique et à des blessures aux nerfs, tendons et muscles.
+ Si durant ou après avoir joué à des jeux vidéo, vous ressentez de la douleur, de
+ l’engourdissement, une faiblesse, de l’inflammation, une sensation de brûlure, des crampes ou de
+ la rigidité, <u>cessez de jouer immédiatement et consultez votre médecin</u>.</p>
+
+<p>
+ <strong>Le jeu sécuritaire</strong>
+</p>
+
+<p>Afin de réduire les risques de crises d’épilepsie ou de blessures, veuillez prendre les
+ précautions suivantes :</p>
+
+<ul>
+ <li>Asseyez-vous aussi loin de l’écran de télévision que possible.</li>
+ <li>Jouez dans une pièce munie d’un éclairage adéquat.</li>
+ <li>Ne jouez pas lorsque vous êtes étourdi ou fatigué.</li>
+ <li>Prenez 10 à 15 minutes de pause après chaque heure de jeu et évitez les périodes de jeu
+ prolongées.</li>
+</ul>
+
+<p>
+ <strong>Ne pas tenter d’effectuer des réparations par vous-même</strong>
+</p>
+
+<p>L’Appareil ne contient aucune pièce pouvant être réparée par l’utilisateur. Ne pas tenter
+ d’ouvrir ou de désassembler l’Appareil.</p>
+
+<p>Le défaut de suivre ces instructions de sécurité pourrait provoquer un feu, un choc
+ électrique, un dommage à l’Appareil ou à d’autres objets ou des lésions corporelles.</p>
diff --git a/docs/html/preview/tv/adt-1/request.jd b/docs/html/preview/tv/adt-1/request.jd
new file mode 100644
index 0000000..69e3e4e
--- /dev/null
+++ b/docs/html/preview/tv/adt-1/request.jd
@@ -0,0 +1,47 @@
+page.title=Request ADT-1 Developer Kit
+
+@jd:body
+
+
+<p>The ADT-1 Developer Kit is streaming media player and game controller designed for running
+and testing app built for Android TV. The kit is offered to developers who are interested in
+building new apps or extending their existing apps to run on the Android TV platform before
+the commercial release of Android TV devices. Supplies of the ADT-1 kit are limited and
+requesting one not guarantee it will be delivered to you.</p>
+
+<p class="note">
+ <strong>Note:</strong> The ADT-1 kit <em>is not required</em> for building and testing apps
+ for Android TV. You can build apps for TV and test them using an emulator for TV devices. The
+ L-Preview SDK includes all the software needed to build TV apps and an emulator for running and
+ testing them. For more information, see the
+ <a href="{@docRoot}preview/tv/start/index.html">Get Started</a> guide for TV apps.
+ For more information about the ADT-1 kit, see the
+ <a href="{@docRoot}preview/tv/adt-1/index.html">ADT-1 Developer Kit</a> information page.
+</p>
+
+<div class="sdk-terms" style="width:678px" onfocus="this.blur()">
+<div class="sdk-terms-padding">
+Android ADT-1 Developer Kit Request Agreement.
+
+1. These are the terms.
+
+2. You must agree to the terms.
+
+3. Otherwise you have not agreed to the terms.
+
+4. And then we don't want to talk to you.
+</div>
+</div>
+
+
+<p class="caution">
+ <strong>Important:</strong> The email address your provide in this form is used to verify
+ you as an Android developer. Please provide a Google account email address that is associated
+ with the Google Play app you enter. We may also use your email address to provide you with
+ updates about the ADT-1 Developer Kit and Android TV.
+</p>
+
+<iframe src="https://docs.google.com/a/google.com/forms/d/1MLhC39rf3aAJw-KhZw9cyjT1dWuz_k3_iC5QXpC4Cbw/viewform?embedded=true"
+ width="100%" height="540" frameborder="0" marginheight="0" marginwidth="0"
+ id="signupform">Loading...</iframe>
+
diff --git a/docs/html/preview/tv/games/index.jd b/docs/html/preview/tv/games/index.jd
new file mode 100644
index 0000000..b9de3a4
--- /dev/null
+++ b/docs/html/preview/tv/games/index.jd
@@ -0,0 +1,70 @@
+page.title=Games on TV
+page.tags="controller"
+
+@jd:body
+
+<p>This section complements the [larger best-practices guidance for designing for Android TV](TODO, use formal name of referenced doc, and add link). It assumes that you have read that guidance, and seeks to minimize repetition.</p>
+
+<h2>Overview</h2>
+<p>Because of factors including its large size, its control scheme, and its nature as a shared display, the television screen presents a number of considerations that may be new to mobile developers. This document breaks these considerations down into five sections:</p>
+<ul>
+<li>Display</li>
+<li>Control</li>
+<li>Manifest</li>
+<li>Google Play Game Services</li>
+<li>Web</li>
+</ul>
+<h2>Display</h2>
+<p>Large and centrally situated, the television screen imposes limitations, but also opens up new opportunities for immersive gameplay.</p>
+<h3>A shared display</h3>
+<p>A living-room TV poses design challenges for multiplayer games, in that all players can see everything. This issue is especially germane to games (such as card games or strategy games) that rely on each player’s possession of hidden information.</p>
+<p>Some mechanisms you can implement to address the problem of one player’s “eavesdropping” on another’s information are:</p>
+<ul>
+<li>A player might place a "blinder" on the screen to help conceal information. For example, in a turn-based game like a word or card game, one player at a time might view the display. When the player finishes a move, the game allows him or her to cover the screen with a “blinder” that blocks anyone from viewing secret information. When the next player begins a turn, the blinder opens to reveal his or her own information.</li>
+<li>A second screen, such as a handset or larger device, can enable a player to conceal information. For information on implementing second-screen support, see <a href="http://developer.android.com/reference/android/app/Presentation.html">Presentation</a> on the Android developer site.</li>
+</ul>
+<h3>No touch interface</h3>
+<p>A television does not have a touch interface. Your game design, therefore, need not take into account the possibility that a player’s controlling fingers might block the on-screen action. You can assume constant visibility of the entire viewing area.</p>
+<p>See the <a href=#control>Control</a> section in this document and in [Design for TV](TODO, use formal name of referenced doc, and add link) for more implications of the lack of touch interface.</p>
+<h3>Landscape display</h3>
+<p>In mobile-device terms, a TV is always “sideways.” You can’t turn it, and there is no portrait orientation. You should always be designing your TV games to be displayed in landscape mode.</p>
+<a id=control><h2>Control</h2>
+<p>Without a touch interface, it's even more important than usual to get your controls right, so that players find them intuitive and fun to use. The separation of controller from device also introduces some other issues to pay attention to, like keeping track of multiple players' controllers, and handling disconnects gracefully.</p>
+<h3>D-pad</h3>
+<p>Because of the lack of touch interface, you should be planning your control scheme based on a D-pad. Some key points to keep in mind include:</p>
+<p>The player needs to use the gamepad in all aspects of the game&ndash;not just controlling core gameplay, but also navigating menus and ads. For this reason, you should also ensure that your Android TV game does not refer to a touch interface: for example, an Android TV game cannot tell a player to "Tap to skip".</p>
+<p>You can avoid unhappy surprises (and resulting low ratings) by using your Play Store description to communicate to the player any expectations about controllers. If a game is better suited to a gamepad with a joystick than one with only a D-pad, you should make this clear. A player who uses an ill-suited controller for a game is likely to have a subpar experience&ndash;and penalize your game in the ratings.</p>
+<p>You can also help ensure a good player experience by ensuring that button mapping is intuitive and flexible. For example, you can adhere to accepted custom by using the A button to <code>Accept</code>, and the B button to <code>Cancel</code>. You can also offer flexibility in the form of remappability. For more information on button mapping, see <a href="http://developer.android.com/training/game-controllers/controller-input.html">Handling Controller Actions</a>.</p>
+<p>Your game can also contribute to a good match between controller and game by querying the controller about its capabilities. For example, you may intend for a player to steer an object by waving the controller in the air. If a player's controller lacks accelerometer and gyroscope hardware, however, waving will not work. But when your game queries the controller and discovers that motion detection is not supported, it can switch over to an alternative, available control scheme.</p>
+<p>For more information on querying controller capabilities, see <a href="http://developer.android.com/training/game-controllers/compatibility.html">Supporting Controllers Across Android Versions</a>.</p>
+<h3>Back-button behavior</h3>
+<p>The Back button should never act as a toggle. For example, do not use it to both open and close a menu. Its behavior should only be linear. For example: Game play &gt; Game pause screen &gt; Game main screen &gt; Android home screen.</p>
+<p>With this principle of "linear navigation" in mind, you <b>may</b> use the back button to leave an in-game menu (opened by a different button) and return to gameplay.</p>
+<h3>Handling multiple controllers</h3>
+<p>When multiple players are playing a game, each with his or her own controller, it is important to map each player-controller pair. For information on how to implement controller-number identification, see <a href="http://developer.android.com/reference/android/view/InputDevice.html#getControllerNumber(">Input Devices</a>) on the Android developer site.</p>
+<h3>Handling disconnects</h3>
+<p>When a controller is disconnected in the middle of gameplay, the game should pause, and a dialog should appear prompting the disconnected player to reconnect his or her controller.</p>
+<p>The dialog should also offer troubleshooting tips (e.g., "Check your Bluetooth connection").</p>
+<h2>Manifest</h2>
+<p>Games are displayed in a separate row from regular apps in the launcher. Android TV uses the <code>android:isGame</code> flag to differentiate games from non-game apps. You can assign it a value of either <code>true</code> or <code>false</code>. For example:</p>
+<pre class="fragment">&lt;application&gt;
+ . . .
+ &lt;meta-data android:name="isGame" android:value=["true" | "false"]/&gt;
+android:isGame=["true" | "false"] &gt;
+ . . .
+&lt;/application&gt;
+</pre><h2>Google Play Game Services</h2>
+<p>If your game integrates Google Play Game Services, you should keep in mind a number of considerations pertaining to achievements, sign-on, saving games, and multiplayer play.</p>
+<h3>Achievements</h3>
+<p>Your game should include at least five (earnable) achievements. Only a user controlling gameplay from a supported input device should be able to earn achievements.</p>
+<h3>Sign-on</h3>
+<p>Your game should attempt to sign the user in on launch. If the player declines sign-in several times in a row, your game should stop asking.</p>
+<h3>Saving</h3>
+<p>We highly recommend using Play Services cloud save to store your game save. Your game should bind game saves to a specific Google account, so as to be uniquely identifiable even across devices: Whether the player is using a handset or a TV, the game should be able to pull the same game-save information from his or her account.</p>
+<p>You should also provide an option in your game's UI to prompt the player to destroy save data. You might put the option in the game's <code>Settings</code> screen.</p>
+<h3>Multiplayer experience</h3>
+<p>A game offering a multiplayer experience must allow at least two players to enter a room.</p>
+<h2>Web</h2>
+<p>Android TV games do not support a full web browser. You should therefore avoid using generic URLs in your game.</p>
+<p>Webviews will work for logins to services like Google+ and Facebook. </p>
+
diff --git a/docs/html/preview/tv/images/atv.png b/docs/html/preview/tv/images/atv.png
new file mode 100644
index 0000000..cd96164
--- /dev/null
+++ b/docs/html/preview/tv/images/atv.png
Binary files differ
diff --git a/docs/html/preview/tv/images/browsefragment.png b/docs/html/preview/tv/images/browsefragment.png
new file mode 100644
index 0000000..8998b13
--- /dev/null
+++ b/docs/html/preview/tv/images/browsefragment.png
Binary files differ
diff --git a/docs/html/preview/tv/images/detailsfragment.png b/docs/html/preview/tv/images/detailsfragment.png
new file mode 100644
index 0000000..014ab23
--- /dev/null
+++ b/docs/html/preview/tv/images/detailsfragment.png
Binary files differ
diff --git a/docs/html/preview/tv/images/home-recommendations.png b/docs/html/preview/tv/images/home-recommendations.png
new file mode 100644
index 0000000..2c18827
--- /dev/null
+++ b/docs/html/preview/tv/images/home-recommendations.png
Binary files differ
diff --git a/docs/html/preview/tv/index.jd b/docs/html/preview/tv/index.jd
new file mode 100644
index 0000000..da40985
--- /dev/null
+++ b/docs/html/preview/tv/index.jd
@@ -0,0 +1,12 @@
+page.title=Android on TV
+
+@jd:body
+
+<img src="{@docRoot}preview/tv/images/atv.png" align="middle"/>
+
+<p>Android offers a rich user experience that's optimized for apps running on large screen
+ devices, such as high-definition televisions. Apps on TV offer new opportunities to
+ delight your users from the comfort of their couch.</p>
+
+<a href="{@docRoot}preview/tv/start/index.html">Get Started &gt;</a>
+
diff --git a/docs/html/preview/tv/start/hardware-features.jd b/docs/html/preview/tv/start/hardware-features.jd
new file mode 100644
index 0000000..f3b51bb
--- /dev/null
+++ b/docs/html/preview/tv/start/hardware-features.jd
@@ -0,0 +1,174 @@
+page.title=Hardware Features on TV
+page.tags="unsupported"
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#unsupported-features">Unsupported Hardware Features</a></li>
+ <li><a href="#workaround-features">Handling Unsupported Features</a></li>
+ <li><a href="#check-features">Checking Available Features</a>
+ <ol>
+ <li><a href="#no-touchscreen">Touch screen</a></li>
+ <li><a href="#no-camera">Camera</a></li>
+ <li><a href="#no-gps">GPS</a></li>
+ </ol>
+
+ </li>
+ </ol>
+</div>
+</div>
+
+<p>TVs do not have some of the hardware features found on other Android devices.
+Touch screens, cameras and GPS receivers are some of the most commonly used hardware features
+which are typically not available on a TV. When you build an app for TV, you must carefully
+consider if your app can handle not having these features and, if necessary, work around them.</p>
+
+<p>This guide discusses the hardware features not available on TV devices and shows you how to
+work around those limitations in your app.</p>
+
+
+<h2 id="unsupported-features">Unsupported Hardware Features</h2>
+
+<p>TVs have a different purpose from other devices, and so they do not 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>
+
+
+<h2 id="check-features">Checking Available Features</h2>
+
+<p>To check if a feature is available at runtime, call {@link
+ android.content.pm.PackageManager#hasSystemFeature(String)}. This method takes a single string
+ argument that specifies the feature you want to check. For example, to check for a touch screen,
+ use {@link android.content.pm.PackageManager#hasSystemFeature(String)} with the argument
+ {@link android.content.pm.PackageManager#FEATURE_TOUCHSCREEN}.</p>
+
+<p>The following code example demonstrates how to detect the availability of a hardware features
+ at runtime:</p>
+
+<pre>
+// Check if the telephony hardware 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 telephony but "+
+ "do have a touch screen.");
+} else {
+ Log.d("TV Test", "Running on a TV!");
+}
+</pre>
+
+
+<h2 id="workaround-features">Handling Unsupported Features</h2>
+
+<p>Depending on the design and functionality of your app, you may be able to work around certain
+ hardware features being unavailable. This section discusses how to workaround specific hardware
+ features.</p>
+
+
+<h3 id="no-touchscreen">Touch screen</h3>
+
+<p>Android doesn't support touch screen interaction for TV devices, since most TVs don't have touch
+ screens, and using a touch screen is not consistent with a viewing environment where the user is
+ seated 10 feet away from the display.</p>
+
+<p>On TV devices, you should workaround this limitation by supporting navigation using a directional
+ pad (D-pad) on TV remote control. For more information on properly supporting navigation using
+ TV-friendly controls, see <a href="{@docRoot}preview/tv/ui/navigation.html">Navigation for
+ TV</a>.</p>
+
+<p>You can explicitly declare if your application requires (or does not require) a touch screen
+ by including the following entry in your manifest:</p>
+
+<pre>
+&lt;uses-feature android:name="android.hardware.touchscreen"
+ android:required="false"/&gt;
+</pre>
+
+
+<h3 id="no-camera">Camera</h3>
+
+<p>Although a TV typically does not 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. If you decide that you want to enable your camera-related application to work on a
+ TV device without a camera, you can add an attribute to your app manifest declaring that
+ a camera is not required by your app:</p>
+
+<pre>
+&lt;uses-feature android:name="android.hardware.camera" android:required="false" /&gt;
+</pre>
+
+<p>If you enable your application to run without a camera, you should add code to your application
+that detects if the camera feature is available and make adjustments to the operation of your app.
+The following code example demonstrates how to detect the presence of a camera:</p>
+
+<pre>
+// Check if the camera hardware feature is available.
+if (getPackageManager().hasSystemFeature("android.hardware.camera")) {
+ Log.d("Camera test", "Camera available!");
+} else {
+ Log.d("Camera test", "No camera available. View and edit features only.");
+}
+</pre>
+
+
+<h3 id="no-gps">GPS</h3>
+
+<p>TVs are stationary, indoor devices, and do not have built-in global positioning system (GPS)
+ receivers. If your application uses location information, you can still allow users to search
+ for a location or use a static location provider such as a zip code configured during the
+ TV device setup.</p>
+
+<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>
+
diff --git a/docs/html/preview/tv/start/index.jd b/docs/html/preview/tv/start/index.jd
new file mode 100644
index 0000000..7f726bd
--- /dev/null
+++ b/docs/html/preview/tv/start/index.jd
@@ -0,0 +1,193 @@
+page.title=Get Started with TV Apps
+page.tags="leanback","recyclerview","launcher"
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#prerequisites">Prerequisites</a></li>
+ <li><a href="#dev-project">Setup a TV Project</a>
+ <ul>
+ <li><a href="#tv-activity">Create a TV Activity</a></li>
+ <li><a href="#tv-libraries">Add TV Support Libraries</a></li>
+ </ul>
+ </li>
+ <li><a href="#build-it">Build TV Apps</a></li>
+ </ol>
+</div>
+</div>
+
+<p>This guide describes how to prepare your development environment and projects for building
+ TV apps, including updating your existing app to run on TV devices.</p>
+
+
+<h2 id="prerequisites">Prerequisites</h2>
+
+<p>Before you begin setting up to build apps for TV, you must:</p>
+
+<ul>
+ <li><strong><a href="{@docRoot}preview/setup-sdk.html">
+ Setup the Preview SDK</a></strong>
+ <br>
+ The preview SDK provides the developer tools needed to build and test apps for TV.
+ </li>
+ <li><strong><a href="{@docRoot}preview/setup-sdk.html#project">
+ Create a Preview SDK Project</a></strong>
+ <br>
+ In order to access new APIs for TV devices, you must create a project that targets the preview
+ release level or modify an existing project to target the preview release.
+ </li>
+</ul>
+
+
+<h2 id="dev-project">Setup a TV Project</h2>
+
+<p>TV apps use the same structure as those for phones and tablets. This means you can modify
+ your existing apps to also run on TV devices or create new apps based on what you already know
+ about building apps for Android. This section discusses how to modify an existing app or create a
+ new app that runs on TV devices.</p>
+
+<p>There are the main steps to creating an app that runs on TV devices, only the first of these
+ is required:</p>
+
+<ul>
+ <li><strong>Activity for TV</strong> - (Required) Your application must have an activity that is
+ is declared to run on TV devices through an app manifest entry.</li>
+ <li><strong>TV Support Libraries</strong> - (Optional) There are several Support Libraries
+ available for TV devices that provide user interface widgets for building user interfaces
+ for use on TV.</li>
+</ul>
+
+
+<h3 id="tv-activity">Create a TV Activity</h3>
+
+<p>Applications that are intended to run on TV devices must declare a launcher activity for TV
+ in their manifest using a the {@code android.intent.category.LEANBACK_LAUNCHER} intent filter.
+ This filter identifies your app as being built for TV, enabling your app to be displayed in the
+ Google Play app running on TV devices. Declaring this intent also identifies which activity in
+ your app should be launched when a user selects your app icon on a TV.</p>
+
+<p class="caution">
+ <strong>Caution:</strong> If you do not include the LEANBACK_LAUNCHER intent filter in your app,
+ it will not be visible to users running the Google Play store on TV devices. If your load an app
+ without this intent filter onto a TV device using developer tools, the app does not appear in
+ the TV user interface.
+</p>
+
+<p>The following code snippet shows how to include this intent filter in your manifest:</p>
+
+<pre>
+&lt;application&gt;
+ ...
+ &lt;activity
+ android:name=&quot;com.example.android.MainActivity&quot;
+ android:label=&quot;@string/app_name&quot; &gt;
+
+ &lt;intent-filter&gt;
+ &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
+ &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
+ &lt;/intent-filter&gt;
+ &lt;/activity&gt;
+
+ &lt;activity
+ android:name=&quot;com.example.android.<strong>TvActivity</strong>&quot;
+ android:label=&quot;&#64;string/app_name&quot;
+ android:theme=&quot;&#64;android:style/Theme.NoTitleBar&quot;&gt;
+
+ &lt;intent-filter&gt;
+ &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
+ &lt;category android:name="<strong>android.intent.category.LEANBACK_LAUNCHER</strong>" /&gt;
+ &lt;/intent-filter&gt;
+
+ &lt;/activity&gt;
+&lt;/application&gt;
+</pre>
+
+<p>The second activity manifest entry in the example above specifies that it should be used as
+ the main activity when your app launched on an TV device.</p>
+
+<p>If you have an existing app that you are modifying for TV use, your app should not use the same
+ activity layout for TV that you do for phones and tablets. The user interface of your TV app (or
+ TV portion of your existing app) should provide a simpler interface that can be easily navigated
+ using a remote control from a couch. For guidelines on designing an app for TV, see the
+ <a href="{@docRoot}design/tv/index.html">TV Design</a> guide. For more instructions on
+ developing a user interface that is appropriate to TV, see the
+ <a href="{@docRoot}preview/tv/ui/index.html">TV User Interface</a> guide.
+</p>
+
+
+<h3 id="tv-libraries">Add TV Support Libraries</h3>
+
+<p>The Preview SDK includes support libraries that are intended for use with TV apps. These
+ libraries provide APIs and user interface widgets for use on TV devices. The libraries are
+ located in the {@code &lt;sdk&gt;/extras/android/support/} directory where you installed the
+ Preview SDK. Here is a list of the libraries and their general purpose:</p>
+
+<ul>
+ <li><strong>v17 leanback library</strong> - Provides user interface widgets for TV, including
+ {@code BrowseFragment}, {@code DetailsFragment}, and {@code SearchFragment}.
+ <ul>
+ <li>SDK location: {@code &lt;sdk&gt;/extras/android/support/v17/leanback}</li>
+ <li>Gradle dependency: {@code com.android.support:leanback-v17:20.0.+}</li>
+ <li>Contains resources: Yes</li>
+ </ul>
+ </li>
+ <li><strong>v7 recyclerview library</strong> - Provides classes for managing display of long
+ lists in a memory efficient manner. Several classes in the v17 leanback library depend on the
+ classes in this library.
+ <ul>
+ <li>SDK location: {@code &lt;sdk&gt;/extras/android/support/v7/recyclerview}</li>
+ <li>Gradle dependency: {@code com.android.support:recyclerview-v7:20.0.+}</li>
+ <li>Contains resources: No</li>
+ </ul>
+ </li>
+</ul>
+
+<p class="note">
+ <strong>Note:</strong> You are not required to use these support libraries for your TV app.
+ However, we strongly recommend using them, particularly for apps that provide a media catalog
+ browsing interface.
+</p>
+
+<p>If you decide to use the v17 leanback library for your application, you should note that it is
+ dependent on the <a href="{@docRoot}tools/support-library/features.html#v7-appcompat">v7
+ appcompat library</a>, which is, in turn, dependent on the
+ <a href="{@docRoot}tools/support-library/features.html#v4">v4 support library</a>. This means
+ that apps that use the leanback support library should include all of these support
+ libraries:</p>
+
+<ul>
+ <li>v17 leanback support library</li>
+ <li>v7 recyclerview support library</li>
+ <li>v7 appcompat support library</li>
+ <li>v4 support library</li>
+</ul>
+
+<p>Two of these libraries (v17 leanback and v7 appcompat) contain resources, which require
+ you to take specific steps to include them in app projects. For instructions on
+ importing a support library with resources, see
+ <a href="http://developer.android.com/tools/support-library/setup.html#libs-with-res">
+ Support Library Setup</a>.
+</p>
+
+
+<h2 id="build-it">Build TV Apps</h2>
+
+<p>After you have completed the steps described above, it's time to start building apps for
+ the big screen! Check out these additional topics to help you build your app for TV:
+
+<ul>
+ <li><a href="{@docRoot}preview/tv/ui/index.html">User Interface</a> - The user interface of
+ TV devices is different from those of other Android devices. See this topic to find out how
+ to build TV user interfaces and the widgets provided to make it easier to build them.
+ </li>
+ <li><a href="{@docRoot}preview/tv/games/index.html">Games for TV</a> - TV devices are great
+ platforms for games. See this topic for information on building great game experiences for
+ TV.</li>
+ <li><a href="{@docRoot}preview/tv/start/hardware-features.html">Hardware features</a> - TV
+ devices do not contain hardware features normally found on other Android devices. See this
+ topic for information on unsupported hardware features and what to do about them.
+ </li>
+</ul>
diff --git a/docs/html/preview/tv/ui/browse.jd b/docs/html/preview/tv/ui/browse.jd
new file mode 100644
index 0000000..9d878b0
--- /dev/null
+++ b/docs/html/preview/tv/ui/browse.jd
@@ -0,0 +1,217 @@
+page.title=BrowseFragment
+parent.title=User Interfaces for TV
+parent.link=index.html
+
+trainingnavtop=true
+next.title=DetailsFragment
+next.link=details.html
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#layout">Media Browse Layout</a></li>
+ <li><a href="#lists">Displaying Media Lists</a></li>
+ <li><a href="#background">Updating the Background</a></li>
+ </ol>
+
+</div>
+</div>
+
+<p>The <a href="{@docRoot}preview/tv/start/index.html#tv-libraries">Leanback support library</a>
+ provides several APIs for displaying and browsing media catalogs
+ on the TV devices. This guide discusses how to use the classes provided by this library to
+ implement a user interface for browsing music or videos from your app's media catalog.</p>
+
+
+<h2 id="layout">Media Browse Layout</h2>
+
+<p>The BrowseFragment class in the Leanback support library allows you to create a primary
+ layout for browsing categories and rows of media items with a minimum of code. The following
+ example layout shows how to create a layout that contains a BrowseFragment:</p>
+
+<pre>
+&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width=&quot;match_parent&quot;
+ android:layout_height=&quot;match_parent&quot;
+ android:orientation=&quot;vertical&quot;
+ &gt;
+
+ &lt;fragment
+ <strong>android:name="android.support.v17.leanback.app.BrowseFragment"</strong>
+ android:id=&quot;@+id/browse_fragment&quot;
+ android:layout_width=&quot;match_parent&quot;
+ android:layout_height=&quot;match_parent&quot;
+ /&gt;
+&lt;/LinearLayout&gt;
+</pre>
+
+<p>In order to work with this layout in an activity, retrieve the BrowseFragment element from
+ the layout. Use the BrowseFragment.Params class to set display parameters such as the icon, title
+ and whether category headers are enabled. The following code sample demonstrates how to set the
+ layout parameters for a BrowseFragment in a layout:</p>
+
+<pre>
+public class BrowseMediaActivity extends Activity {
+
+ public static final String TAG ="BrowseActivity";
+
+ protected BrowseFragment mBrowseFragment;
+
+ &#64;Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.browse_fragment);
+
+ final FragmentManager fragmentManager = getFragmentManager();
+ <strong>mBrowseFragment = (BrowseFragment) fragmentManager.findFragmentById(
+ R.id.browse_fragment);</strong>
+
+ // Set display parameters for the BrowseFragment
+ BrowseFragment.Params params = mBrowseFragment.getBrowseParams();
+ if (params == null) {
+ params = new BrowseFragment.Params();
+ }
+ params.setHeadersState(BrowseFragment.HEADERS_ENABLED);
+ params.setTitle(getString(R.string.app_name));
+ params.setBadgeImage(getResources().getDrawable(R.drawable.ic_launcher));
+ mBrowseFragment.setBrowseParams(params);
+
+ }
+}
+</pre>
+
+
+<h2 id="lists">Displaying Media Lists</h2>
+
+<p>The BrowseFragment allows you to define and display browseable media content categories and
+ media items from a media catalog using adapters and presenters. Adapters enable you to connect to
+ local or online data sources that contain your media catalog information. Presenter classes hold
+ data about media items and provide layout information for displaying an item on screen.</p>
+
+<p>The following example code shows an implementation of a presenter for displaying string
+ data:</p>
+
+<pre>
+public class StringPresenter extends Presenter {
+ private static final String TAG = "StringPresenter";
+
+ public ViewHolder onCreateViewHolder(ViewGroup parent) {
+ TextView textView = new TextView(parent.getContext());
+ textView.setFocusable(true);
+ textView.setFocusableInTouchMode(true);
+ textView.setBackground(
+ parent.getContext().getResources().getDrawable(R.drawable.text_bg));
+ return new ViewHolder(textView);
+ }
+
+ public void onBindViewHolder(ViewHolder viewHolder, Object item) {
+ ((TextView) viewHolder.view).setText(item.toString());
+ }
+
+ public void onUnbindViewHolder(ViewHolder viewHolder) {
+ Log.d(TAG, "onUnbindViewHolder");
+ }
+}
+</pre>
+
+<p>Once you have constructed a presenter class for your media items, you can build and attach an
+ adapter to the BrowseFragment to display those items on screen for browsing by the user. The
+ following example code demonstrates how to construct an adapter to display categories and items
+ in those categories using the StringPresenter class shown in the previous code example:</p>
+
+<pre>
+private ArrayObjectAdapter mRowsAdapter;
+private static final int NUM_ROWS = 4;
+
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+ ...
+
+ buildRowsAdapter();
+}
+
+private void buildRowsAdapter() {
+ mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+
+ for (int i = 0; i &lt; NUM_ROWS; ++i) {
+ ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
+ new StringPresenter());
+ listRowAdapter.add("Media Item 1");
+ listRowAdapter.add("Media Item 2");
+ listRowAdapter.add("Media Item 3");
+ HeaderItem header = new HeaderItem(i, "Category " + i, null);
+ mRowsAdapter.add(new ListRow(header, listRowAdapter));
+ }
+
+ mBrowseFragment.setAdapter(mRowsAdapter);
+}
+</pre>
+
+<p>This example shows a static implementation of the adapters. A typical media browsing
+ application uses data from an online database or web service. For an example of a browsing
+ application that uses data retrieved from the web, see the <a href="">!FIX</a> code sample.</p>
+
+<p>The following screenshot shows the output of this code on an Android TV device:</p>
+
+<img src="{@docRoot}preview/tv/images/browsefragment.png" alt="" height="XXX" id="figure1" />
+<p class="img-caption">
+ <strong>Figure 1.</strong> Display layout example based on the
+ {@link android.support.v17.leanback.app.BrowseFragment} and {@code StringPresenter}
+ classes.
+</p>
+
+
+<h2 id="background">Updating the Background</h2>
+
+<p>In order to add visual interest to a media browsing app on TV, you can update the background
+ image as users browse through content. This technique can make interaction with your app feel more
+ cinematic and enjoyable for users.</p>
+
+<p>The Leanback support library provides a {@link
+ android.support.v17.leanback.app.BackgroundManager} class for changing the background of your TV
+ app activity. The following example shows how to create a simple method for updating the
+ background:</p>
+
+<pre>
+protected void updateBackground(Drawable drawable) {
+ BackgroundManager.getInstance(this).setDrawable(drawable);
+}
+</pre>
+
+<p>Many of the existing media browse apps automatically update the background as the user
+ navigates through media listings. In order to do this, you can set up a selection listener to
+ automatically update the background based on the user's current selection. The following example
+ shows you how to set up an {@link android.support.v17.leanback.widget.OnItemSelectedListener}
+ class to catch selection events and update the background:</p>
+
+<pre>
+protected void clearBackground() {
+ BackgroundManager.getInstance(this).setDrawable(mDefaultBackground);
+}
+
+protected OnItemSelectedListener getDefaultItemSelectedListener() {
+ return new OnItemSelectedListener() {
+ &#64;Override
+ public void onItemSelected(Object item, Row row) {
+ if (item instanceof Movie ) {
+ URI uri = ((Movie)item).getBackdropURI();
+ updateBackground(uri);
+ } else {
+ clearBackground();
+ }
+ }
+ };
+}
+</pre>
+
+<p class="note">
+ <strong>Note:</strong> The implementation above is a simple example shown for purposes of
+ illustration. When creating this function in your own app, you should consider running the
+ background update action in a separate thread for better performance. In addition, if you are
+ planning on updating the background in response to user's scrolling through items, consider adding
+ a time to delay a background image update until the user settles on item, to avoid excessive
+ background image updates.
+</p>
diff --git a/docs/html/preview/tv/ui/details.jd b/docs/html/preview/tv/ui/details.jd
new file mode 100644
index 0000000..7da3b5d
--- /dev/null
+++ b/docs/html/preview/tv/ui/details.jd
@@ -0,0 +1,230 @@
+page.title=DetailFragment
+parent.title=User Interfaces for TV
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=BrowseFragment
+previous.link=browse.html
+next.title=Searching in TV Apps
+next.link=in-app-search.html
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#details-presenter">Build a Details Presenter</a></li>
+ <li><a href="#details-fragment">Extend the Details Fragment</a>
+ <li><a href="#activity">Creating a Details Activity</a></li>
+ <li><a href="#item-listener">Listener for Clicked Items</a></li>
+ </li>
+ </ol>
+</div>
+</div>
+
+<p>The media browsing interface classes provided by the
+ <a href="{@docRoot}preview/tv/start/index.html#tv-libraries">Leanback support library</a>
+ include classes for displaying additional information about a media item, such as a description
+ or reviews, and taking action on that item, such as purchasing it or playing its content. This
+ section discusses how to create a presenter class for media item details and extend the
+ {@code DetailsFragment} class to implement a details view for a media item when it
+ is selected by a user.
+</p>
+
+<p class="note">
+ <strong>Note:</strong> The implementation example shown here uses an additional activity to
+ contain the {@code DetailsFragment}. However, it is possible to avoid creating a second activity
+ by replacing the current {@code BrowseFragment} with a {@code DetailsFragment} within the <em>same</em>
+ activity using fragment transactions. For more information on using fragment transactions, see the
+ <a href="{@docRoot}training/basics/fragments/fragment-ui.html#Replace">Building a Dynamic
+ UI with Fragments</a> training.
+</p>
+
+
+<h2 id="details-presenter">Build a Details Presenter</h2>
+
+<p>In the media browsing framework provided for by the leanback support library, you use
+ presenter objects to control the display of data on screen, including media item details. The
+ framework provides the {@code AbstractDetailsDescriptionPresenter} class for this purpose, which
+ is a nearly complete implementation of the presenter for media item details. All you have to do is
+ implement the {@code onBindDescription()} method to bind the view fields to your data objects, as shown in
+ the following code sample:</p>
+
+<pre>
+public class DetailsDescriptionPresenter
+ extends AbstractDetailsDescriptionPresenter {
+
+ &#64;Override
+ protected void onBindDescription(ViewHolder viewHolder, Object itemData) {
+ MyMediaItemDetails details = (MyMediaItemDetails) itemData;
+ // In a production app, the itemData object contains the information
+ // needed to display details for the media item:
+ // viewHolder.getTitle().setText(details.getShortTitle());
+
+ // Here we provide static data for testing purposes:
+ viewHolder.getTitle().setText(itemData.toString());
+ viewHolder.getSubtitle().setText("2014 Drama TV-14");
+ viewHolder.getBody().setText("Lorem ipsum dolor sit amet, consectetur "
+ + "adipisicing elit, sed do eiusmod tempor incididunt ut labore "
+ + " et dolore magna aliqua. Ut enim ad minim veniam, quis "
+ + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
+ + "commodo consequat.");
+ }
+}
+</pre>
+
+
+<h2 id="details-fragment">Extend the Details Fragment</h2>
+
+<p>When you use the {@code DetailsFragment} class for displaying your media item details, you
+ extend that class to provide additional content such as a preview image and actions for the media
+ item. You can also provide additional content, such as a list of related media items.</p>
+
+<p>The following example code demonstrates how to use the presenter class you created in the
+ previous section, add a preview image and actions for the media item being viewed. This example
+ also shows the addition of a related media items row, which appears below the details listing.</p>
+
+<pre>
+public class MediaItemDetailsFragment extends DetailsFragment {
+ private static final String TAG = "MediaItemDetailsFragment";
+ private ArrayObjectAdapter mRowsAdapter;
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.i(TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+
+ buildDetails();
+ }
+
+ private void buildDetails() {
+ ClassPresenterSelector selector = new ClassPresenterSelector();
+ // Attach your media item details presenter to the row presenter:
+ DetailsOverviewRowPresenter rowPresenter =
+ new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
+
+ selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
+ selector.addClassPresenter(ListRow.class,
+ new ListRowPresenter());
+ mRowsAdapter = new ArrayObjectAdapter(selector);
+
+ Resources res = getActivity().getResources();
+ DetailsOverviewRow detailsOverview = new DetailsOverviewRow(
+ "Media Item Details");
+
+ // Add images and action buttons to the details view
+ detailsOverview.setImageDrawable(res.getDrawable(R.drawable.jelly_beans));
+ detailsOverview.addAction(new Action(1, "Buy $9.99"));
+ detailsOverview.addAction(new Action(2, "Rent $2.99"));
+ mRowsAdapter.add(detailsOverview);
+
+ // Add a Related items row
+ ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
+ new StringPresenter());
+ listRowAdapter.add("Media Item 1");
+ listRowAdapter.add("Media Item 2");
+ listRowAdapter.add("Media Item 3");
+ HeaderItem header = new HeaderItem(0, "Related Items", null);
+ mRowsAdapter.add(new ListRow(header, listRowAdapter));
+
+ setAdapter(mRowsAdapter);
+ }
+}
+</pre>
+
+<p>The following screenshot shows the output of this code on a TV device:</p>
+
+<img src="{@docRoot}preview/tv/images/detailsfragment.png" alt="" height="XXX" id="figure1" />
+<p class="img-caption">
+ <strong>Figure 1.</strong> Display layout example based on {@code DetailsFragment}, using a
+ {@code DetailsOverviewRow} and a {@code ListRow} for related items.
+</p>
+
+
+<h3 id="activity">Creating a Details Activity</h3>
+
+<p>Fragments such as the {@code DetailsFragment} must be contained within an activity in order
+ to be used for display. Creating an activity for your details view, separate from the browse
+ activity, enables you to invoke your details view using an Intent. This section explains how to
+ build an activity to contain your implementation of the detail view for your media items.</p>
+
+<p>Start creating the details activity by building a layout that references your implementation
+ of the {@code DetailsFragment}:</p>
+
+<pre>
+&lt;!-- file: res/layout/details.xml --&gt;
+
+&lt;fragment xmlns:android="http://schemas.android.com/apk/res/android"
+ <strong>android:name="com.example.android.mediabrowser.MediaItemDetailsFragment"</strong>
+ android:id="&#64;+id/details_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+/&gt;
+</pre>
+
+<p>Next, create an activity class that uses the layout shown in the previous code example:</p>
+
+<pre>
+public class DetailsActivity extends Activity
+{
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ <strong>setContentView(R.layout.details);</strong>
+ }
+}
+</pre>
+
+<p>Finally, add this new activity to the manifest. Remember to apply the Leanback theme to
+ ensure that the user interface is consistent with the media browse activity:</p>
+
+<pre>
+&lt;application&gt;
+ ...
+
+ &lt;activity android:name=".DetailsActivity"
+ android:exported="true"
+ <strong>android:theme="@style/Theme.Leanback"/&gt;</strong>
+
+&lt;/application&gt;
+</pre>
+
+
+<h3 id="item-listener">Listener for Clicked Items</h3>
+
+<p>After you have implemented the {@code DetailsFragment}, you must modify your main media
+ browsing view to move to your details view when a user clicks on a media item. In order to enable
+ this behavior, add an {@code OnItemClickedListener} object to the BrowseFragment that fires an
+ intent to start the item details activity.</p>
+
+<p>The following example shows how to implement a listener to start the details view when a user
+ clicks a media item in the main media browsing activity:</p>
+
+<pre>
+public class BrowseMediaActivity extends Activity {
+ ...
+
+ &#64;Override
+ protected void onCreate(Bundle savedInstanceState) {
+ ...
+
+ // create the media item rows
+ buildRowsAdapter();
+
+ // add a listener for selected items
+ mBrowseFragment.setOnItemClickedListener(
+ new OnItemClickedListener() {
+ &#64;Override
+ public void onItemClicked(Object item, Row row) {
+ System.out.println("Media Item clicked: " + item.toString());
+ Intent intent = new Intent(BrowseMediaActivity.this,
+ DetailsActivity.class);
+ // pass the item information
+ intent.getExtras().putLong("id", item.getId());
+ startActivity(intent);
+ }
+ });
+ }
+}
+</pre>
diff --git a/docs/html/preview/tv/ui/in-app-search.jd b/docs/html/preview/tv/ui/in-app-search.jd
new file mode 100644
index 0000000..b372254
--- /dev/null
+++ b/docs/html/preview/tv/ui/in-app-search.jd
@@ -0,0 +1,119 @@
+page.title=Searching in TV Apps
+parent.title=User Interfaces for TV
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=DetailsFragment
+previous.link=details.html
+next.title=Recommendations
+next.link=recommendations.html
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#add-search-ui">Add Search User Interface</a></li>
+ </ol>
+
+</div>
+</div>
+
+
+<p>Users frequently have specific content in mind when using a media app. A search interface can
+ help your users get to the content they want faster than browsing. The Leanback library provides a
+ set of classes to enable a standard search interface within your app that is consistent with other
+ search functions on TV and provides features such as voice input.</p>
+
+<h2 id="add-search-ui">Add Search User Interface</h2>
+<p>When you use the BrowseFragment class for your media browsing interface, you can enable the
+ search icon by setting an OnClickListener to the browse fragment object. The following sample code
+ demonstrates this technique.</p>
+
+<pre>
+&#64;Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.browse_activity);
+
+ mBrowseFragment = (BrowseFragment)
+ getFragmentManager().findFragmentById(R.id.browse_fragment);
+
+ ...
+
+ mBrowseFragment.setOnSearchClickedListener(new View.OnClickListener() {
+ &#64;Override
+ public void onClick(View view) {
+ Intent intent = new Intent(BrowseActivity.this, SearchActivity.class);
+ startActivity(intent);
+ }
+ });
+
+ mBrowseFragment.setAdapter(buildAdapter());
+}
+</pre>
+
+<p class="note">
+ <strong>Note:</strong> You can set the color of the search icon using the
+ {@code setSearchAffordanceColor()} method of {@code BrowseFragment}.
+</p>
+
+<p>When a user selects the search icon, the system invokes a search activity via the defined
+ Intent. Your search activity should use a linear layout containing a SearchFragment. This fragment
+ must also implement the SearchFragment.SearchResultProvider interface in order to display the
+ results of a search. The following code sample shows how to extend the SearchFragment class to
+ provide a search interface and results:</p>
+
+<pre>
+public class MySearchFragment extends SearchFragment
+ implements SearchFragment.SearchResultProvider {
+
+ private static final int SEARCH_DELAY_MS = 300;
+ private ArrayObjectAdapter mRowsAdapter;
+ private Handler mHandler = new Handler();
+ private SearchRunnable mDelayedLoad;
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+ setSearchResultProvider(this);
+ setOnItemClickedListener(getDefaultItemClickedListener());
+ mDelayedLoad = new SearchRunnable();
+ }
+
+ &#64;Override
+ public ObjectAdapter getResultsAdapter() {
+ return mRowsAdapter;
+ }
+
+ &#64;Override
+ public boolean onQueryTextChange(String newQuery) {
+ mRowsAdapter.clear();
+ if (!TextUtils.isEmpty(newQuery)) {
+ mDelayedLoad.setSearchQuery(newQuery);
+ mHandler.removeCallbacks(mDelayedLoad);
+ mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
+ }
+ return true;
+ }
+
+ &#64;Override
+ public boolean onQueryTextSubmit(String query) {
+ mRowsAdapter.clear();
+ if (!TextUtils.isEmpty(query)) {
+ mDelayedLoad.setSearchQuery(query);
+ mHandler.removeCallbacks(mDelayedLoad);
+ mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
+ }
+ return true;
+ }
+}
+</pre>
+
+<p>This example code shown above is meant to be used with a separate SearchRunnable class, that
+ runs the search query on a separate thread. This technique keeps potentially slow-running queries
+ from interfering with the main user interface thread.</p>
+
diff --git a/docs/html/preview/tv/ui/index.jd b/docs/html/preview/tv/ui/index.jd
new file mode 100644
index 0000000..9513bc6
--- /dev/null
+++ b/docs/html/preview/tv/ui/index.jd
@@ -0,0 +1,44 @@
+page.title=User Interfaces for TV
+page.tags="input","screens"
+
+trainingnavtop=true
+startpage=true
+
+@jd:body
+
+
+<p>
+ Building an effective and engaging for TV devices requires a firm understanding what works well
+ in the context of a living room. Imagine a large screen that can be seen by many people at the
+ same time, controlled a few buttons by users with limited attention and you start to see the
+ challenges and opportunity of building an app for TV. Building apps for this environment
+ requires a different approach and different tools.</p>
+
+<p>This section discusses how to build a living room experience with your app, including
+ implementation instructions and user interface widgets built for TV. Also check out the
+ <a href="{@docRoot}design/tv/index.html">Design for TV</a> for information and inspiration
+ on creating engaging user interfaces for TV devices.</p>
+
+<h2>Topics</h2>
+
+<dl>
+ <dt><b><a href="layouts.html">Layouts</a></b></dt>
+ <dd>Learn how to build app layouts for TV screens.</dd>
+
+ <dt><b><a href="navigation.html">Navigation</a></b></dt>
+ <dd>Learn how to build navigation for TV devices.</dd>
+
+ <dt><b><a href="browse.html">BrowseFragment</a></b></dt>
+ <dd>Learn how to use this fragment to build a browsing interface for media catalogs.</dd>
+
+ <dt><b><a href="details.html">DetailsFragment</a></b></dt>
+ <dd>Learn how to use this fragment to build a details page for media items.</dd>
+
+ <dt><b><a href="search.html">In-App Search</a></b></dt>
+ <dd>Learn how to use a built-for-TV user interface for searching within your app.</dd>
+
+ <dt><b><a href="recommendations.html">Recommendations</a></b></dt>
+ <dd>Learn how to contribute watch next suggestions and get your content noticed by users.</dd>
+</dl>
+
+
diff --git a/docs/html/preview/tv/ui/layouts.jd b/docs/html/preview/tv/ui/layouts.jd
new file mode 100644
index 0000000..5655152
--- /dev/null
+++ b/docs/html/preview/tv/ui/layouts.jd
@@ -0,0 +1,298 @@
+page.title=Layouts for TV
+parent.title=User Interfaces for TV
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Navigation for TV
+next.link=navigation.html
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#themes">Themes</a>
+ <ol>
+ <li><a href="#leanback-theme">Leanback Theme</a></li>
+ </ol>
+ </li>
+ <li><a href="#structure">Layout Structure</a>
+ <ol>
+ <li><a href="#overscan">Overscan</a></li>
+ </ol>
+ </li>
+ <li><a href="#visibility">Text and Controls Visibility</a></li>
+ <li><a href="#density-resources">Screen Density and Image Resources</a></li>
+ <li><a href="#anti-patterns">Layout Anti-Patterns</a></li>
+ <li><a href="#large-bitmaps">Handling Large Bitmaps</a></li>
+ </ol>
+
+</div>
+</div>
+
+<p>
+ A TV screen is typically viewed from about 10 feet away, and while it is much larger than most
+ other Android device displays, this type of screen does not provide the same level of precise
+ detail and color as a smaller device. These factors require that you create app layouts with
+ TV devices in mind in order to create a useful and enjoyable user experience.</p>
+
+<p>This guide provides guidance and implementation details for building effective layouts for
+ TV apps.</p>
+
+
+<h2 id="themes">Themes</h2>
+
+<p>Android <a href="{@docRoot}guide/topics/ui/themes.html">Themes</a> can provide a basis for
+ layouts in your apps. On TV devices, you should use a theme to suppress the title bar from
+ your app activities that are meant for TV. The title bar is a standard user interface element
+ for Android apps on phones and tablets, but it is not appropriate for TV apps. The following
+ code example from TV app manifest demonstrates how to apply this theme:
+</p>
+
+<pre>
+&lt;application&gt;
+ ...
+
+ &lt;activity
+ android:name="com.example.android.TvActivity"
+ android:label="&#64;string/app_name"
+ <strong>android:theme="&#64;android:style/Theme.NoTitleBar"</strong>&gt;
+ ...
+
+ &lt;/activity&gt;
+&lt;/application&gt;
+
+</pre>
+
+
+<h3 id="leanback-theme">Leanback Theme</h3>
+
+<p>The Android framework provides a standard theme for TV activities, called {@code
+ Leanback.Theme}, which establishes a consistent visual style for TV apps. Use of this theme is
+ recommended for most apps. The following code sample shows how to apply this theme to a given
+ activity within an app:</p>
+
+<pre>
+&lt;activity
+ android:name="com.example.android.TvActivity"
+ android:label="&#64;string/app_name"
+ <strong>android:theme="&#64;android:style/Theme.Leanback"</strong>&gt;
+</pre>
+
+
+<h2 id="structure">Layout Structure</h2>
+
+<p>Layouts for TV devices should follow some basic guidelines to ensure they are useable and
+ effective on large screens. Follow these tips to build landscape layouts optimized for TV screens:
+</p>
+
+<ul>
+ <li>Build layouts with a landscape orientation. TV screens are always displayed in this
+ orientation.</li>
+ <li>Put on-screen navigation 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, using <a
+ href="{@docRoot}guide/components/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 approach allows the system to adjust the
+ position of the views to the size, alignment, aspect ratio, and pixel density of a TV screen.</li>
+ <li>Add sufficient margins between layout controls to avoid a cluttered UI.</li>
+</ul>
+
+
+<h3 id="overscan">Overscan</h3>
+
+<p>Layouts for TV have some unique requirements due to the evolution of TV standards and the
+ desire to always present a full screen picture to viewers. For this reason, TV devices may
+ clip the outside edge of an app layout in order to ensure a that the entire display is filled.
+ This behavior is generally referred to as Overscan.</p>
+
+<p>In order to account for the impact of overscan and make sure that all the user interface
+ elements you place in a layout are actually shown on screen, you should incorporate a 10% margin
+ on all sides of your layout. This translates into a 27dp margin on the left and right edges and
+ a 48dp margin on the top and bottom of your base layouts for activities. The following
+ example layout demonstrates how to set these margins in the root layout for a TV app:
+</p>
+
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/base_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_marginTop="27dp"
+ android:layout_marginLeft="48dp"
+ android:layout_marginRight="48dp"
+ android:layout_marginBottom="27dp" &gt;
+&lt;/LinearLayout&gt;
+</pre>
+
+<p class="caution">
+ <strong>Caution:</strong> Do not apply overscan margins to your layout if you are using the
+ Leanback Support Library {@code BrowseFragment} or related widgets, as those layouts already
+ incorporate overscan-safe margins.
+</p>
+
+
+<h2 id="visibility">Text and Controls Visibility</h2>
+
+<p>
+The text and controls in a TV app layout 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 anti-aliasing to increase readability.</li>
+ <li>Use Android's standard font sizes:
+<pre>
+&lt;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"/&gt;
+</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>
+
+
+<h2 id="density-resources">Screen Density and Image Resources</h2>
+
+<p>The common high-definition TV display resolutions are 720p, 1080i, and 1080p.
+ Your TV layout should target a screen size of 1920 x 1080 pixels, and then allow the Android
+ system to downscale your layout elements to 720p if necessary. In general, downscaling
+ (removing pixels) does not degrade your layout presentation quality. However, upscaling can
+ cause display artifacts that degrade the quality of your layout and have a negative impact on
+ the user experience of your app.</p>
+
+<p>
+ To get the best scaling results for images, provide them as
+ <a href="{@docRoot}tools/help/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 layouts and resources for large screens see
+ <a href="{@docRoot}training/multiscreen/index.html">Designing for multiple screens</a>.
+</p>
+
+
+<h2 id="anti-patterns">Layout Anti-Patterns</h2>
+
+<p>There are a few approaches to building layouts for TV that you should avoid because they do not
+work well and lead to bad user experiences. Here are some user interface approaches you
+should specifically <em>not</em> use when developing a layout for TV.
+</p>
+
+<ul>
+ <li><strong>Re-using phone or tablet layouts</strong> - Do not reuse layouts from a phone or
+ tablet app without modification. Layouts built for other Android device form factors are not
+ well suited for TV devices and should be simplified for operation on a TV.</li>
+ <li><strong>ActionBar</strong> - While this user interface convention is recommended for use
+ on phones and tablets, it is not appropriate for a TV interface. In particular, using an
+ antion bar options menu (or any pull-down menu for that matter) is strongly discouraged, due
+ to the difficulty in navigating such a menu with a remote control.</li>
+ <li><strong>ViewPager</strong> - Sliding between screens can work great on a phone or tablet,
+ but don't try this on a TV!</li>
+
+</ul>
+
+<p>For more information on designing layouts that are appropriate to TV, see the
+ <a href="{@docRoot}design/tv/index.html">TV Design</a> guide.</p>
+
+
+<h2 id="large-bitmaps">Handling Large Bitmaps</h2>
+
+<p>TV devices, like any other Android device, have a limited amount of memory. If you build your
+ app layout with very high-resolution images or use many high-resolutions images in the operation
+ of your app, it can quickly run into memory limits and cause out of memory errors.
+ To avoid these types of problems, 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 an in-memory
+ {@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 large images to a more appropriate size as you download them;
+ otherwise, downloading the image itself may cause an out of memory exception.
+ The following sample code demonstrates how to scale 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 approach helps reduce
+ // memory use. This value 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>
+
diff --git a/docs/html/preview/tv/ui/navigation.jd b/docs/html/preview/tv/ui/navigation.jd
new file mode 100644
index 0000000..3041e58
--- /dev/null
+++ b/docs/html/preview/tv/ui/navigation.jd
@@ -0,0 +1,144 @@
+page.title=Navigation for TV
+parent.title=User Interfaces for TV
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Layouts for TV
+previous.link=layouts.html
+next.title=BrowseFragment
+next.link=browse.html
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#d-pad-navigation">D-pad Navigation</a></li>
+ <li><a href="#focus-selection">Focus and Selection</a></li>
+ </ol>
+
+</div>
+</div>
+
+<p>TV devices provide a limited set of navigation controls for apps. Creating an effective
+ navigation scheme for your TV app depends on understanding these limited controls and the limits
+ of users perception while operating your app. As you build your Android application for TVs,
+ you should pay special attention to how the user actually navigates around your application
+ when using remote control buttons instead of a touch screen.</p>
+
+<p>This guide shows you how to build an effective navigation scheme for your TV app.</p>
+
+
+<h2 id="d-pad-navigation">D-pad Navigation</h2>
+
+<p>On a TV device, users navigate with controls on a remote control device, using either a
+ directional pad (D-pad) or arrow keys. This type of control limits movement to up, down, left,
+ and right. To build a great TV-optimized app, you must provide a navigation scheme where
+ the user can quickly learn how to navigate your app using these limited controls.</p>
+
+<p>Follow these guidelines to build navigation system that works well with a D-pad on a TV device:
+</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>The Android framework handles directional navigation between layout elements automatically, so
+ you typically do not need to do anything extra for your app. However, you should thoroughly test
+ navigation with a D-pad control to discover any navigation problems. If you discover that your
+ screen layout makes navigation difficult, or if you want users to move through the layout in a
+ specific way, you can set up explicit directional navigation for your controls. The following
+ code sample shows how to define the next control to receive focus for a
+ {@link android.widget.TextView} layout object:</p>
+
+<pre>
+&lt;TextView android:id="&#64;+id/Category1"
+ android:nextFocusDown="&#64;+id/Category2"\&gt;
+</pre>
+
+<p>The following table lists all of the available navigation attributes for Android user interface
+widgets:</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 ({@code 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 class="note">
+ <strong>Note:</strong> 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="focus-selection">Focus and Selection</h2>
+
+<p>The success of a navigation scheme on TV devices is strongly dependent on how easy it is for a
+ user to determine what user interface element is in focus on screen. If you do not provide clear
+ indications of what is in focus on screen (and therefore what item they can take action on),
+ users can quickly become frustrated and exit your app. By the same token, it is important
+ to always have an item in focus that a user can take action on immediately after your app starts,
+ and any time your app is not playing content.</p>
+
+<p>Your app layout and implementation should use color, size, animation or a combination of
+ these attributes to help users easily determine what actions they can take next. Use a uniform
+ scheme for indicating focus 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. The
+following code example demonstates how to apply selection indication for a button object:
+</p>
+
+<pre>
+&lt;!-- res/drawable/button.xml --&gt;
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;selector xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;item android:state_pressed="true"
+ android:drawable="@drawable/button_pressed" /&gt; &lt;!-- pressed --&gt;
+ &lt;item android:state_focused="true"
+ android:drawable="@drawable/button_focused" /&gt; &lt;!-- focused --&gt;
+ &lt;item android:state_hovered="true"
+ android:drawable="@drawable/button_focused" /&gt; &lt;!-- hovered --&gt;
+ &lt;item android:drawable="@drawable/button_normal" /&gt; &lt;!-- default --&gt;
+&lt;/selector&gt;
+</pre>
+
+<p>
+This layout XML applies the above state list drawable to a {@link android.widget.Button}:
+</p>
+<pre>
+&lt;Button
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:background="@drawable/button" /&gt;
+</pre>
+
+<p>Make sure to provide sufficient padding within the focusable and selectable controls so that
+ the highlights around them are clearly visible.</p>
+
diff --git a/docs/html/preview/tv/ui/recommendations.jd b/docs/html/preview/tv/ui/recommendations.jd
new file mode 100644
index 0000000..52ac2e5
--- /dev/null
+++ b/docs/html/preview/tv/ui/recommendations.jd
@@ -0,0 +1,234 @@
+page.title=Making Recommendations
+parent.title=User Interfaces for TV
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Searching in TV Apps
+previous.link=in-app-search.html
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#service">Create a Recommendations Service</a></li>
+ <li><a href="#build">Build Recommendations</a></li>
+ <li><a href="#run-service">Run Recommendations Service</a></li>
+ <li><a href="#DesignLandscapeLayouts">Design Landscape Layouts</a></li>
+ </ol>
+
+</div>
+</div>
+
+
+<p>Content recommendations appear as the first row of the TV launch screen after the first use
+ of the device. This row is intended to help users quickly find content they enjoy. Contributing
+ recommendations from your apps content catalog can help bring users back to your app.</p>
+
+
+<img src="{@docRoot}preview/tv/images/home-recommendations.png" alt="" height="XXX" id="figure1" />
+<p class="img-caption">
+ <strong>Figure 1.</strong> The first row after the search widget is the system-wide
+ recommendations.
+</p>
+
+
+<h2 id="service">Create a Recommendations Service</h2>
+
+<p>Content recommendations are created with background processing. In order for your application
+ to contribute to recommendations, you create a service that periodically adds listings from your
+ app's catalog to the system list of recommendations.</p>
+
+<p>The following code example illustrates how to extend the {@link android.app.IntentService} to
+ create a recommendation service for your application.</p>
+
+<pre>
+public class RecommendationsService extends IntentService {
+
+ ...
+
+ public Notification buildRecommendation(Context context, Movie movie)
+ throws IOException {
+
+ if (mNotificationManager == null) {
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ Bundle extras = new Bundle();
+ if (mBackgroundUri != movie.getBackgroundUri()) {
+ extras.putString(EXTRA_BACKGROUND_IMAGE_URL, movie.getBackgroundUri());
+ }
+
+ // build the recommendation as a Notification object
+ Notification notification = new NotificationCompat.BigPictureStyle(
+ new NotificationCompat.Builder(context)
+ .setContentTitle(movie.getTitle())
+ .setContentText(movie.getDescription())
+ .setPriority(movie.getPriority())
+ .setOngoing(true)
+ .setCategory("recommendation")
+ .setLargeIcon(movie.getImage())
+ .setSmallIcon(movie.getSmallIcon())
+ .setContentIntent(buildPendingIntent(movie.getId()))
+ .setExtras(extras))
+ .build();
+
+ // post the recommendation to the NotificationManager
+ mNotificationManager.notify(movie.getId(), notification);
+ mNotificationManager = null;
+ return notification;
+ }
+
+ private PendingIntent buildPendingIntent(long id) {
+ Intent detailsIntent = new Intent(this, DetailsActivity.class);
+ detailsIntent.putExtra("id", id);
+
+ TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
+ stackBuilder.addParentStack(DetailsActivity.class);
+ stackBuilder.addNextIntent(detailsIntent);
+ // Ensure each PendingIntent is unique
+ detailsIntent.setAction(Long.toString(id));
+
+ PendingIntent intent = stackBuilder.getPendingIntent(
+ 0, PendingIntent.FLAG_UPDATE_CURRENT);
+ return intent;
+ }
+}
+</pre>
+
+<p>In order for this class to be recognized and run as a service, you must register this service
+ using your app manifest. The following code snippet illustrates how to add this class as a
+ service:</p>
+
+<pre>
+&lt;manifest ... &gt;
+ &lt;application ... &gt;
+ ...
+
+ &lt;service android:name=&quot;.UpdateRecommendationsService&quot;
+ android:enabled=&quot;true&quot; android:exported=&quot;true&quot;/&gt;
+ &lt;/application&gt;
+&lt;/manifest&gt;
+</pre>
+
+<h2 id="build">Build Recommendations</h2>
+
+<p>Once it starts running, your service must create recommendations and pass them to the Android
+ framework. The framework receives the recommendations as {@link android.app.Notification} objects
+ that use a specific style and are marked with a specific category.</p>
+
+<p>The following code example demonstrates how to get an instance of the {@link
+ android.app.NotificationManager}, build a recommendation and post it to the manager:</p>
+
+<pre>
+public class RecommendationsService extends IntentService {
+
+ ...
+
+ public Notification buildRecommendation(Context context, Movie movie)
+ throws IOException {
+
+ if (mNotificationManager == null) {
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ Bundle extras = new Bundle();
+ if (mBackgroundUri != movie.getBackgroundUri()) {
+ extras.putString(EXTRA_BACKGROUND_IMAGE_URL, movie.getBackgroundUri());
+ }
+
+ // build the recommendation as a Notification object
+ Notification notification = new NotificationCompat.BigPictureStyle(
+ new NotificationCompat.Builder(context)
+ .setContentTitle(movie.getTitle())
+ .setContentText(movie.getDescription())
+ .setPriority(movie.getPriority())
+ .setOngoing(true)
+ .setCategory("recommendation")
+ .setLargeIcon(movie.getImage())
+ .setSmallIcon(movie.getSmallIcon())
+ .setContentIntent(buildPendingIntent(movie.getId()))
+ .setExtras(extras))
+ .build();
+
+ // post the recommendation to the NotificationManager
+ mNotificationManager.notify(movie.getId(), notification);
+ mNotificationManager = null;
+ return notification;
+ }
+
+ private PendingIntent buildPendingIntent(long id) {
+ Intent detailsIntent = new Intent(this, DetailsActivity.class);
+ detailsIntent.putExtra("id", id);
+
+ TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
+ stackBuilder.addParentStack(DetailsActivity.class);
+ stackBuilder.addNextIntent(detailsIntent);
+ // Ensure each PendingIntent is unique
+ detailsIntent.setAction(Long.toString(id));
+
+ PendingIntent intent = stackBuilder.getPendingIntent(
+ 0, PendingIntent.FLAG_UPDATE_CURRENT);
+ return intent;
+ }
+}
+</pre>
+
+
+<h3 id="run-service">Run Recommendations Service</h3>
+
+<p>Your app's recommendation service must run periodically in order to create current
+ recommendations. In order to run your service, you should create a class that runs a timer and
+ invokes it at regular intervals. The following code example extends the {@link
+ android.content.BroadcastReceiver} class to start periodic execution of a recommendation service
+ every 30 minutes:</p>
+
+<pre>
+public class BootupActivity extends BroadcastReceiver {
+ private static final String TAG = "BootupActivity";
+
+ private static final long INITIAL_DELAY = 5000;
+
+ &#64;Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
+ scheduleRecommendationUpdate(context);
+ }
+ }
+
+ private void scheduleRecommendationUpdate(Context context) {
+ AlarmManager alarmManager = (AlarmManager)context.getSystemService(
+ Context.ALARM_SERVICE);
+ Intent recommendationIntent = new Intent(context,
+ UpdateRecommendationsService.class);
+ PendingIntent alarmIntent = PendingIntent.getService(context, 0,
+ recommendationIntent, 0);
+
+ alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ INITIAL_DELAY,
+ AlarmManager.INTERVAL_HALF_HOUR,
+ alarmIntent);
+ }
+}
+</pre>
+
+<p>In order for the {@link android.content.BroadcastReceiver} class to execute after an TV
+ device starts up, you must register this class in your app manifest and attach an intent filter
+ for the completion of the device boot process. This sample code demonstrates how to add this
+ configuration to the manifest:</p>
+
+<pre>
+&lt;manifest ... &gt;
+ &lt;application ... &gt;
+ &lt;receiver android:name=&quot;.BootupActivity&quot; android:enabled=&quot;true&quot;
+ android:exported=&quot;false&quot;&gt;
+ &lt;intent-filter&gt;
+ &lt;action android:name=&quot;android.intent.action.BOOT_COMPLETED&quot;/&gt;
+ &lt;/intent-filter&gt;
+ &lt;/receiver&gt;
+ &lt;/application&gt;
+&lt;/manifest&gt;
+</pre>
diff --git a/docs/html/training/tv/index.jd b/docs/html/training/tv/index.jd
deleted file mode 100644
index a99e378..0000000
--- a/docs/html/training/tv/index.jd
+++ /dev/null
@@ -1,59 +0,0 @@
-page.title=Designing for TV
-page.tags=input,screens
-
-trainingnavtop=true
-startpage=true
-
-@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>
-
-<a class="notice-developers-video wide" href="http://www.youtube.com/watch?v=zsRnRLh-O34">
-<div>
- <h3>Video</h3>
- <p>DevBytes: Design for Large Displays - Part 1</p>
-</div>
-</a>
-
-<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>
diff --git a/docs/html/training/tv/optimizing-layouts-tv.jd b/docs/html/training/tv/optimizing-layouts-tv.jd
deleted file mode 100644
index a6db052..0000000
--- a/docs/html/training/tv/optimizing-layouts-tv.jd
+++ /dev/null
@@ -1,246 +0,0 @@
-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">Design to Handle Large Bitmaps</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/components/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>
-&lt;RelativeLayout
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" &gt;
-
- &lt;fragment
- android:id="@+id/leftsidecontrols"
- android:layout_width="0dip"
- android:layout_marginLeft="5dip"
- android:layout_height="match_parent" /&gt;
-
- &lt;GridView
- android:id="@+id/gridview"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" /&gt;
-
-&lt;/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>
- &lt;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"/&gt;
- </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}tools/help/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 an in-memory
- {@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
deleted file mode 100644
index bb78258..0000000
--- a/docs/html/training/tv/optimizing-navigation-tv.jd
+++ /dev/null
@@ -1,206 +0,0 @@
-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>
-&lt;EditText android:id="@+id/LastNameField" android:nextFocusDown="@+id/FirstNameField"\&gt;
-</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>
-&lt;?xml version="1.0" encoding="utf-8"?&gt;
-&lt;selector xmlns:android="http://schemas.android.com/apk/res/android"&gt;
- &lt;item android:state_pressed="true"
- android:drawable="@drawable/button_pressed" /&gt; &lt;!-- pressed --&gt;
- &lt;item android:state_focused="true"
- android:drawable="@drawable/button_focused" /&gt; &lt;!-- focused --&gt;
- &lt;item android:state_hovered="true"
- android:drawable="@drawable/button_focused" /&gt; &lt;!-- hovered --&gt;
- &lt;item android:drawable="@drawable/button_normal" /&gt; &lt;!-- default --&gt;
-&lt;/selector&gt;
-</pre>
-
-<p>
-This layout XML applies the above state list drawable to a {@link android.widget.Button}:
-</p>
-<pre>
-&lt;Button
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:background="@drawable/button" /&gt;
-</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>
-&lt;LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- &gt;
- &lt;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"/&gt;
- &lt;fragment
- android:id="@+id/continents"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_marginLeft="10dip"
- android:layout_weight="0.2"/&gt;
-
- &lt;fragment
- android:id="@+id/places"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_marginLeft="10dip"
- android:layout_weight="0.6"/&gt;
-
-&lt;/LinearLayout&gt;
-</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
deleted file mode 100644
index a9f090b..0000000
--- a/docs/html/training/tv/unsupported-features-tv.jd
+++ /dev/null
@@ -1,157 +0,0 @@
-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.html">Optimizing Layouts for TV</a> and
-<a href="{@docRoot}training/tv/optimizing-navigation-tv.html">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>
-&lt;uses-feature android:name="android.hardware.touchscreen" android:required="false"/&gt;
-</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/docs/html/tv/index.jd b/docs/html/tv/index.jd
index 3ced60d..e1cae8c 100644
--- a/docs/html/tv/index.jd
+++ b/docs/html/tv/index.jd
@@ -1,7 +1,9 @@
-heropage.title=Android TV
+page.title=Android TV
page.viewport_width=970
fullpage=true
no_footer_links=true
+page.type=about
+
@jd:body
@@ -21,20 +23,6 @@ no_footer_links=true
}
</style>
-<div id="video-container">
- <div id="video-frame">
- <div class="video-close">
- <span id="icon-video-close">&nbsp;</span>
- </div>
- <script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
- <div id="ytapiplayer">
- <a href="http://www.youtube.com/watch?v=0xQ3y902DEQ"><img width=940
- src="https://i1.ytimg.com/vi/0xQ3y902DEQ/maxresdefault.jpg"></a><!--You need Flash player 8+ and JavaScript enabled to view this video. -->
- </div>
- </div>
-</div>
-
-
<div class="landing-body-content">
<div class="landing-hero-container">
@@ -48,48 +36,19 @@ no_footer_links=true
<div class="col-10">
<div class="landing-section-header">
<div class="landing-h1 hero">Android TV</div>
- <div class="landing-subhead hero">
- <p>Big screen, big experience</p>
+ <div class="landing-subhead hero">Your apps on the big screen</div>
+ <div class="landing-hero-description">
+ <p>Engage users from the comfort of their couches.
+ Put your app on TV and bring everyone into
+ the action.</p>
</div>
- <div class="landing-hero-description">
- <p>Engage your users from the comfort of their couch.
- Put your app on TV and bring the whole family into
- the action.</p>
- </div>
<div class="landing-body">
- <a href="{@docRoot}wear/preview/start.html" class="landing-button landing-primary" style="margin-top: 40px;">
+ <a href="{@docRoot}preview/tv/index.html" class="landing-button landing-primary" style="margin-top: 40px;">
Get Started
</a>
- <a id="watchVideo" href="https://youtube.googleapis.com/v/0xQ3y902DEQ">
- <div class="landing-video-link">Watch the video</div>
- </a>
-<script>
-$("#watchVideo").on("click", function(e) {
- $("#video-container").fadeIn(400, function(){$("#video-frame").show()});
-
- var params = { allowScriptAccess: "always"};
- var atts = { id: "ytapiplayer" };
- swfobject.embedSWF("//www.youtube.com/v/0xQ3y902DEQ?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1;autoplay=1",
- "ytapiplayer", "940", "526.4", "8", null, null, params, atts);
-
- e.preventDefault();
-});
-$("#icon-video-close").on("click", function() {
- ytplayer = document.getElementById("ytapiplayer");
- try {
- ytplayer.stopVideo();
- $(ytplayer).hide();
- $("#video-container").fadeOut(400);
- } catch(e) {
- console.log('Video not available');
- $("#video-container").fadeOut(400);
- }
-});
-</script>
</div>
</div>
-
</div>
</div>
</div> <!-- end .wrap -->
diff --git a/docs/html/wear/images/hero.jpg b/docs/html/wear/images/hero.jpg
index 565ffb8..7850a81 100644
--- a/docs/html/wear/images/hero.jpg
+++ b/docs/html/wear/images/hero.jpg
Binary files differ
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index 6aa04a5..026698c 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -2,6 +2,7 @@ page.title=Android Wear
page.viewport_width=970
fullpage=true
no_footer_links=true
+page.type=about
@jd:body
@@ -41,10 +42,11 @@ no_footer_links=true
<div class="landing-subhead hero">Information that moves with you</div>
</div>
<div class="landing-hero-description">
- <p>
- Useful information when you need it most.<br />
- Intelligent answers to spoken questions.<br />
- Tools to help reach fitness goals.<br />
+
+ <p>Small, powerful devices, worn on the body.
+ Useful information when you need it most.
+ Intelligent answers to spoken questions.
+ Tools to help reach fitness goals.
Your key to a multiscreen world.</p>
</div>
@@ -52,29 +54,6 @@ no_footer_links=true
<a href="/training/wearables/index.html" class="landing-button landing-primary" style="margin-top: 40px;">
Get Started
</a>
-<script>
-$("#watchVideo").on("click", function(e) {
- $("#video-container").fadeIn(400, function(){$("#video-frame").show()});
-
- var params = { allowScriptAccess: "always"};
- var atts = { id: "ytapiplayer" };
- swfobject.embedSWF("//www.youtube.com/v/0xQ3y902DEQ?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1;autoplay=1",
- "ytapiplayer", "940", "526.4", "8", null, null, params, atts);
-
- e.preventDefault();
-});
-$("#icon-video-close").on("click", function() {
- ytplayer = document.getElementById("ytapiplayer");
- try {
- ytplayer.stopVideo();
- $(ytplayer).hide();
- $("#video-container").fadeOut(400);
- } catch(e) {
- console.log('Video not available');
- $("#video-container").fadeOut(400);
- }
-});
-</script>
</div>
</div>
@@ -97,6 +76,7 @@ $("#icon-video-close").on("click", function() {
<div class="landing-subhead">
Android Wear extends the Android platform to a new generation of devices, <br>
with a user experience that's designed specifically for wearables.
+
</div>
</div>
@@ -118,6 +98,7 @@ $("#icon-video-close").on("click", function() {
<p class="landing-small">
Get glanceable, actionable information at just the right time with notifications
that are synced from your handheld device.
+
</p>
</div>
<div class="col-3-wide">
@@ -125,6 +106,7 @@ $("#icon-video-close").on("click", function() {
<p class="landing-small">
Design apps that can access a wide range of sensors and other hardware
directly on the wearable.
+
</p>
</div>
</div>
@@ -170,6 +152,7 @@ $("#icon-video-close").on("click", function() {
</p>
<p class="landing-small">
<a href="/training/wearables/apps/index.html/">Create wearable apps</a>
+
</p>
</div>
<div class="col-4">
@@ -181,6 +164,7 @@ $("#icon-video-close").on("click", function() {
</p>
<p class="landing-small">
<a href="/training/wearables/apps/index.html/">Work with the Data Layer</a>
+
</p>
</div>
<div class="col-4">
@@ -198,7 +182,9 @@ $("#icon-video-close").on("click", function() {
</div>
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
+
<!--
+
<div class="landing-section landing-white-background">
<div class="wrap">
<div class="landing-section-header">
@@ -246,12 +232,14 @@ $("#icon-video-close").on("click", function() {
</div>
</div>
</div> <!-- end .wrap
+
</div> <!-- end .landing-section -->
<div class="landing-section landing-red-background">
<div class="wrap">
<div class="landing-section-header">
<div class="landing-h1 landing-align-left">Get Started with Android Wear</div>
+
<div class="landing-subhead landing-subhead-red">
<p>
Set up your development environment and start working with the APIs.
@@ -296,6 +284,7 @@ $("#icon-video-close").on("click", function() {
<p class="landing-small">
Read more about Android Wear development
on our blog. Just search for "Android Wear".
+
</p>
<br>
<p class="landing-small">
@@ -312,6 +301,7 @@ $("#icon-video-close").on("click", function() {
<p>G+ Community</p>
<p class="landing-small">
Follow us on Google+ to stay up-to-date with Android Wear development and to join the discussion!
+
</p>
<p class="landing-small">
<a target="_blank" href="http://g.co/androidweardev">+Android Wear Developers</a>
@@ -340,6 +330,5 @@ $("#icon-video-close").on("click", function() {
License</a>.
</div>
</div>
+ </div> <!-- end landing-body-content -->
-
- </div> <!-- end landing-body-content --> \ No newline at end of file
diff --git a/graphics/java/android/graphics/drawable/Animatable.java b/graphics/java/android/graphics/drawable/Animatable.java
index 9dc62c3..4edfad4 100644
--- a/graphics/java/android/graphics/drawable/Animatable.java
+++ b/graphics/java/android/graphics/drawable/Animatable.java
@@ -32,7 +32,7 @@ public interface Animatable {
/**
* Indicates whether the animation is running.
- *
+ *
* @return True if the animation is running, false otherwise.
*/
boolean isRunning();
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index e8024f7..0fd4423 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -19,6 +19,8 @@ package android.graphics.drawable;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.ColorFilter;
+import android.graphics.PorterDuff.Mode;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
@@ -88,6 +90,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
canvas.restoreToCount(saveCount);
}
+ @Override
public void start() {
if (!mRunning) {
mRunning = true;
@@ -95,11 +98,13 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
}
}
+ @Override
public void stop() {
mRunning = false;
unscheduleSelf(this);
}
+ @Override
public boolean isRunning() {
return mRunning;
}
@@ -108,10 +113,11 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
unscheduleSelf(this);
scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration);
}
-
+
+ @Override
public void run() {
// TODO: This should be computed in draw(Canvas), based on the amount
- // of time since the last frame drawn
+ // of time since the last frame drawn
mCurrentDegrees += mIncrement;
if (mCurrentDegrees > (360.0f - mIncrement)) {
mCurrentDegrees = 0.0f;
@@ -119,7 +125,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
invalidateSelf();
nextFrame();
}
-
+
@Override
public boolean setVisible(boolean visible, boolean restart) {
mState.mDrawable.setVisible(visible, restart);
@@ -133,8 +139,8 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
unscheduleSelf(this);
}
return changed;
- }
-
+ }
+
/**
* Returns the drawable rotated by this RotateDrawable.
*/
@@ -148,7 +154,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
| mState.mChangingConfigurations
| mState.mDrawable.getChangingConfigurations();
}
-
+
@Override
public void setAlpha(int alpha) {
mState.mDrawable.setAlpha(alpha);
@@ -165,10 +171,16 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
}
@Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mState.mDrawable.setTint(tint, tintMode);
+ }
+
+ @Override
public int getOpacity() {
return mState.mDrawable.getOpacity();
}
+ @Override
public void invalidateDrawable(Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
@@ -176,6 +188,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
}
}
+ @Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
@@ -183,6 +196,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
}
}
+ @Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
@@ -194,7 +208,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
public boolean getPadding(Rect padding) {
return mState.mDrawable.getPadding(padding);
}
-
+
@Override
public boolean isStateful() {
return mState.mDrawable.isStateful();
@@ -206,6 +220,16 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
}
@Override
+ protected boolean onLevelChange(int level) {
+ return mState.mDrawable.setLevel(level);
+ }
+
+ @Override
+ protected boolean onStateChange(int[] state) {
+ return mState.mDrawable.setState(state);
+ }
+
+ @Override
public int getIntrinsicWidth() {
return mState.mDrawable.getIntrinsicWidth();
}
@@ -231,11 +255,11 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedRotateDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);
-
+
TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
final boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION;
final float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
-
+
tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION;
final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
@@ -250,7 +274,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
}
a.recycle();
-
+
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT &&
@@ -306,7 +330,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
Drawable mDrawable;
int mChangingConfigurations;
-
+
boolean mPivotXRel;
float mPivotX;
boolean mPivotYRel;
@@ -315,7 +339,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
int mFramesCount;
private boolean mCanConstantState;
- private boolean mCheckedConstantState;
+ private boolean mCheckedConstantState;
public AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner,
Resources res) {
@@ -341,12 +365,12 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac
public Drawable newDrawable() {
return new AnimatedRotateDrawable(this, null);
}
-
+
@Override
public Drawable newDrawable(Resources res) {
return new AnimatedRotateDrawable(this, res);
}
-
+
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 1ee0224..0740761 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -28,7 +28,6 @@ import android.os.SystemClock;
import android.util.AttributeSet;
/**
- *
* An object used to create frame-by-frame animations, defined by a series of Drawable objects,
* which can be used as a View object's background.
* <p>
@@ -114,6 +113,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
* @see #isRunning()
* @see #stop()
*/
+ @Override
public void start() {
if (!isRunning()) {
run();
@@ -127,6 +127,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
* @see #isRunning()
* @see #start()
*/
+ @Override
public void stop() {
if (isRunning()) {
unscheduleSelf(this);
@@ -138,6 +139,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
*
* @return true if the animation is running, false otherwise
*/
+ @Override
public boolean isRunning() {
return mAnimating;
}
@@ -148,6 +150,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
*
* @see #start()
*/
+ @Override
public void run() {
nextFrame(false);
}
@@ -165,41 +168,41 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
public int getNumberOfFrames() {
return mAnimationState.getChildCount();
}
-
+
/**
* @return The Drawable at the specified frame index
*/
public Drawable getFrame(int index) {
return mAnimationState.getChild(index);
}
-
+
/**
- * @return The duration in milliseconds of the frame at the
+ * @return The duration in milliseconds of the frame at the
* specified index
*/
public int getDuration(int i) {
return mAnimationState.mDurations[i];
}
-
+
/**
* @return True of the animation will play once, false otherwise
*/
public boolean isOneShot() {
return mAnimationState.mOneShot;
}
-
+
/**
* Sets whether the animation should play once or repeat.
- *
+ *
* @param oneShot Pass true if the animation should only play once
*/
public void setOneShot(boolean oneShot) {
mAnimationState.mOneShot = oneShot;
}
-
+
/**
* Add a frame to the animation
- *
+ *
* @param frame The frame to add
* @param duration How long in milliseconds the frame should appear
*/
@@ -209,7 +212,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
setFrame(0, true, false);
}
}
-
+
private void nextFrame(boolean unschedule) {
int next = mCurFrame+1;
final int N = mAnimationState.getChildCount();
@@ -239,21 +242,21 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
@Override
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
-
+
TypedArray a = r.obtainAttributes(attrs,
com.android.internal.R.styleable.AnimationDrawable);
super.inflateWithAttributes(r, parser, a,
com.android.internal.R.styleable.AnimationDrawable_visible);
-
+
mAnimationState.setVariablePadding(a.getBoolean(
com.android.internal.R.styleable.AnimationDrawable_variablePadding, false));
-
+
mAnimationState.mOneShot = a.getBoolean(
com.android.internal.R.styleable.AnimationDrawable_oneshot, false);
-
+
a.recycle();
-
+
int type;
final int innerDepth = parser.getDepth()+1;
@@ -267,7 +270,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
if (depth > innerDepth || !parser.getName().equals("item")) {
continue;
}
-
+
a = r.obtainAttributes(attrs, com.android.internal.R.styleable.AnimationDrawableItem);
int duration = a.getInt(
com.android.internal.R.styleable.AnimationDrawableItem_duration, -1);
@@ -278,9 +281,9 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
}
int drawableRes = a.getResourceId(
com.android.internal.R.styleable.AnimationDrawableItem_drawable, 0);
-
+
a.recycle();
-
+
Drawable dr;
if (drawableRes != 0) {
dr = r.getDrawable(drawableRes);
@@ -295,7 +298,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
}
dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
-
+
mAnimationState.addFrame(dr, duration);
if (dr != null) {
dr.setCallback(this);
@@ -342,7 +345,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
}
public void addFrame(Drawable dr, int dur) {
- // Do not combine the following. The array index must be evaluated before
+ // Do not combine the following. The array index must be evaluated before
// the array is accessed because super.addChild(dr) has a side effect on mDurations.
int pos = super.addChild(dr);
mDurations[pos] = dur;
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index ef6c085..be940df 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -31,6 +31,7 @@ import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
+import android.graphics.drawable.ColorDrawable.ColorState;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.Shader;
@@ -618,9 +619,11 @@ public class BitmapDrawable extends Drawable {
@Override
public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) {
- mBitmapState.mTint = tint;
- mBitmapState.mTintMode = tintMode;
- computeTintFilter();
+ final BitmapState state = mBitmapState;
+ state.mTint = tint;
+ state.mTintMode = tintMode;
+
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
invalidateSelf();
}
@@ -638,21 +641,6 @@ public class BitmapDrawable extends Drawable {
return mBitmapState.mTintMode;
}
- private void computeTintFilter() {
- final BitmapState state = mBitmapState;
- if (state.mTint != null && state.mTintMode != null) {
- final int color = state.mTint.getColorForState(getState(), 0);
- if (mTintFilter != null) {
- mTintFilter.setColor(color);
- mTintFilter.setMode(state.mTintMode);
- } else {
- mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
- }
- } else {
- mTintFilter = null;
- }
- }
-
/**
* @hide Candidate for future API inclusion
*/
@@ -679,17 +667,11 @@ public class BitmapDrawable extends Drawable {
@Override
protected boolean onStateChange(int[] stateSet) {
- final ColorStateList tint = mBitmapState.mTint;
- if (tint != null) {
- final int newColor = tint.getColorForState(stateSet, 0);
- final int oldColor = mTintFilter.getColor();
- if (oldColor != newColor) {
- mTintFilter.setColor(newColor);
- invalidateSelf();
- return true;
- }
+ final BitmapState state = mBitmapState;
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ return true;
}
-
return false;
}
@@ -946,7 +928,7 @@ public class BitmapDrawable extends Drawable {
mTargetDensity = state.mTargetDensity;
}
- computeTintFilter();
+ updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
computeBitmapSize();
}
}
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index 3ac9972..174de3a 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -19,10 +19,12 @@ package android.graphics.drawable;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
import android.graphics.*;
+import android.graphics.PorterDuff.Mode;
import android.view.Gravity;
import android.util.AttributeSet;
@@ -52,7 +54,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback {
public static final int HORIZONTAL = 1;
public static final int VERTICAL = 2;
-
+
ClipDrawable() {
this(null, null);
}
@@ -111,6 +113,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback {
// overrides from Drawable.Callback
+ @Override
public void invalidateDrawable(Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
@@ -118,6 +121,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback {
}
}
+ @Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
@@ -125,6 +129,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback {
}
}
+ @Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
@@ -169,6 +174,11 @@ public class ClipDrawable extends Drawable implements Drawable.Callback {
}
@Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mClipState.mDrawable.setTint(tint, tintMode);
+ }
+
+ @Override
public int getOpacity() {
return mClipState.mDrawable.getOpacity();
}
@@ -197,7 +207,7 @@ public class ClipDrawable extends Drawable implements Drawable.Callback {
@Override
public void draw(Canvas canvas) {
-
+
if (mClipState.mDrawable.getLevel() == 0) {
return;
}
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index df5ca33..3716182 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -17,6 +17,8 @@
package android.graphics.drawable;
import android.graphics.*;
+import android.graphics.PorterDuff.Mode;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -39,9 +41,13 @@ import java.io.IOException;
* @attr ref android.R.styleable#ColorDrawable_color
*/
public class ColorDrawable extends Drawable {
+ private final Paint mPaint = new Paint();
+
@ViewDebug.ExportedProperty(deepExport = true, prefix = "state_")
private ColorState mColorState;
- private final Paint mPaint = new Paint();
+ private ColorStateList mTint;
+ private PorterDuffColorFilter mTintFilter;
+
private boolean mMutated;
/**
@@ -84,9 +90,17 @@ public class ColorDrawable extends Drawable {
@Override
public void draw(Canvas canvas) {
- if ((mColorState.mUseColor >>> 24) != 0) {
+ final ColorFilter colorFilter = mPaint.getColorFilter();
+ if ((mColorState.mUseColor >>> 24) != 0 || colorFilter != null || mTintFilter != null) {
+ if (colorFilter == null) {
+ mPaint.setColorFilter(mTintFilter);
+ }
+
mPaint.setColor(mColorState.mUseColor);
canvas.drawRect(getBounds(), mPaint);
+
+ // Restore original color filter.
+ mPaint.setColorFilter(colorFilter);
}
}
@@ -141,16 +155,51 @@ public class ColorDrawable extends Drawable {
}
/**
- * Setting a color filter on a ColorDrawable has no effect.
+ * Sets the color filter applied to this color.
+ * <p>
+ * Only supported on version {@link android.os.Build.VERSION_CODES#L} and
+ * above. Calling this method has no effect on earlier versions.
*
- * @param colorFilter Ignore.
+ * @see android.graphics.drawable.Drawable#setColorFilter(ColorFilter)
*/
@Override
public void setColorFilter(ColorFilter colorFilter) {
+ mPaint.setColorFilter(colorFilter);
+ }
+
+ @Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ final ColorState state = mColorState;
+ if (state.mTint != tint || state.mTintMode != tintMode) {
+ state.mTint = tint;
+ state.mTintMode = tintMode;
+
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ protected boolean onStateChange(int[] stateSet) {
+ final ColorState state = mColorState;
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isStateful() {
+ return mTint != null && mTint.isStateful();
}
@Override
public int getOpacity() {
+ if (mTintFilter != null || mPaint.getColorFilter() != null) {
+ return PixelFormat.TRANSLUCENT;
+ }
+
switch (mColorState.mUseColor >>> 24) {
case 255:
return PixelFormat.OPAQUE;
@@ -165,8 +214,7 @@ public class ColorDrawable extends Drawable {
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs, theme);
- final TypedArray a = obtainAttributes(
- r, theme, attrs, R.styleable.ColorDrawable);
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorDrawable);
inflateStateFromTypedArray(a);
a.recycle();
}
@@ -225,21 +273,25 @@ public class ColorDrawable extends Drawable {
}
final static class ColorState extends ConstantState {
+ int[] mThemeAttrs;
int mBaseColor; // base color, independent of setAlpha()
@ViewDebug.ExportedProperty
int mUseColor; // basecolor modulated by setAlpha()
int mChangingConfigurations;
- int[] mThemeAttrs;
+ ColorStateList mTint;
+ Mode mTintMode;
ColorState() {
// Empty constructor.
}
ColorState(ColorState state) {
+ mThemeAttrs = state.mThemeAttrs;
mBaseColor = state.mBaseColor;
mUseColor = state.mUseColor;
mChangingConfigurations = state.mChangingConfigurations;
- mThemeAttrs = state.mThemeAttrs;
+ mTint = state.mTint;
+ mTintMode = state.mTintMode;
}
@Override
@@ -276,6 +328,6 @@ public class ColorDrawable extends Drawable {
mColorState = state;
}
- // No local properties to initialize.
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
}
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 6a7757b..cb88e3d 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -17,13 +17,6 @@
package android.graphics.drawable;
import android.annotation.NonNull;
-import android.graphics.Insets;
-import android.graphics.Xfermode;
-import android.os.Trace;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -31,15 +24,19 @@ import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
+import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
+import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.Region;
-import android.graphics.PorterDuff.Mode;
+import android.graphics.Xfermode;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.StateSet;
@@ -47,6 +44,9 @@ import android.util.TypedValue;
import android.util.Xml;
import android.view.View;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
@@ -1226,6 +1226,26 @@ public abstract class Drawable {
}
/**
+ * Ensures the tint filter is consistent with the current tint color and
+ * mode.
+ */
+ PorterDuffColorFilter updateTintFilter(PorterDuffColorFilter tintFilter, ColorStateList tint,
+ PorterDuff.Mode tintMode) {
+ if (tint == null || tintMode == null) {
+ return null;
+ }
+
+ final int color = tint.getColorForState(getState(), Color.TRANSPARENT);
+ if (tintFilter == null) {
+ return new PorterDuffColorFilter(color, tintMode);
+ }
+
+ tintFilter.setColor(color);
+ tintFilter.setMode(tintMode);
+ return tintFilter;
+ }
+
+ /**
* Obtains styled attributes from the theme, if available, or unstyled
* resources if the theme is null.
*/
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 2aef39f..8be6eb7 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -16,6 +16,7 @@
package android.graphics.drawable;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.graphics.Canvas;
@@ -23,6 +24,7 @@ import android.graphics.ColorFilter;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.PorterDuff.Mode;
import android.os.SystemClock;
import android.util.LayoutDirection;
import android.util.SparseArray;
@@ -151,7 +153,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
@Override
public void setColorFilter(ColorFilter cf) {
- mDrawableContainerState.mHasColorFilter = true;
+ mDrawableContainerState.mHasColorFilter = (cf != null);
if (mDrawableContainerState.mColorFilter != cf) {
mDrawableContainerState.mColorFilter = cf;
@@ -162,6 +164,20 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
}
+ @Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mDrawableContainerState.mHasTint = (tint != null && tintMode != null);
+
+ if (mDrawableContainerState.mTint != tint || mDrawableContainerState.mTintMode != tintMode) {
+ mDrawableContainerState.mTint = tint;
+ mDrawableContainerState.mTintMode = tintMode;
+
+ if (mCurrDrawable != null) {
+ mCurrDrawable.mutate().setTint(tint, tintMode);
+ }
+ }
+ }
+
/**
* Change the global fade duration when a new drawable is entering
* the scene.
@@ -396,6 +412,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
}
if (mDrawableContainerState.mHasColorFilter) {
d.setColorFilter(mDrawableContainerState.mColorFilter);
+ } else if (mDrawableContainerState.mHasTint) {
+ d.setTint(mDrawableContainerState.mTint, mDrawableContainerState.mTintMode);
}
d.setVisible(isVisible(), true);
d.setDither(mDrawableContainerState.mDither);
@@ -566,6 +584,10 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
ColorFilter mColorFilter;
boolean mHasColorFilter;
+ ColorStateList mTint;
+ Mode mTintMode;
+ boolean mHasTint;
+
DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
@@ -588,6 +610,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
mAutoMirrored = orig.mAutoMirrored;
mColorFilter = orig.mColorFilter;
mHasColorFilter = orig.mHasColorFilter;
+ mTint = orig.mTint;
+ mTintMode = orig.mTintMode;
+ mHasTint = orig.mHasTint;
// Cloning the following values may require creating futures.
mConstantPadding = orig.getConstantPadding();
@@ -741,7 +766,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
final int N = mNumChildren;
final Drawable[] drawables = mDrawables;
for (int i = 0; i < N; i++) {
- final Drawable d = drawables[i];
+ final Drawable d = drawables[i];
if (d != null) {
if (d.canApplyTheme()) {
return true;
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 9e0ab86..220e81c 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -19,10 +19,12 @@ package android.graphics.drawable;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
import android.graphics.*;
+import android.graphics.PorterDuff.Mode;
import android.util.AttributeSet;
import android.util.Log;
@@ -44,8 +46,7 @@ import java.io.IOException;
* @attr ref android.R.styleable#InsetDrawable_insetTop
* @attr ref android.R.styleable#InsetDrawable_insetBottom
*/
-public class InsetDrawable extends Drawable implements Drawable.Callback
-{
+public class InsetDrawable extends Drawable implements Drawable.Callback {
// Most of this is copied from ScaleDrawable.
private InsetState mInsetState;
private final Rect mTmpRect = new Rect();
@@ -62,13 +63,13 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,
int insetRight, int insetBottom) {
this(null, null);
-
+
mInsetState.mDrawable = drawable;
mInsetState.mInsetLeft = insetLeft;
mInsetState.mInsetTop = insetTop;
mInsetState.mInsetRight = insetRight;
mInsetState.mInsetBottom = insetBottom;
-
+
if (drawable != null) {
drawable.setCallback(this);
}
@@ -78,7 +79,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
int type;
-
+
TypedArray a = r.obtainAttributes(attrs,
com.android.internal.R.styleable.InsetDrawable);
@@ -168,7 +169,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
| mInsetState.mChangingConfigurations
| mInsetState.mDrawable.getChangingConfigurations();
}
-
+
@Override
public boolean getPadding(Rect padding) {
boolean pad = mInsetState.mDrawable.getPadding(padding);
@@ -178,7 +179,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
padding.top += mInsetState.mInsetTop;
padding.bottom += mInsetState.mInsetBottom;
- if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight |
+ if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight |
mInsetState.mInsetTop | mInsetState.mInsetBottom) != 0) {
return true;
} else {
@@ -217,6 +218,11 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
mInsetState.mDrawable.setColorFilter(cf);
}
+ @Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mInsetState.mDrawable.setTint(tint, tintMode);
+ }
+
/** {@hide} */
@Override
public void setLayoutDirection(int layoutDirection) {
@@ -227,7 +233,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
public int getOpacity() {
return mInsetState.mDrawable.getOpacity();
}
-
+
@Override
public boolean isStateful() {
return mInsetState.mDrawable.isStateful();
@@ -239,7 +245,12 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
onBoundsChange(getBounds());
return changed;
}
-
+
+ @Override
+ protected boolean onLevelChange(int level) {
+ return mInsetState.mDrawable.setLevel(level);
+ }
+
@Override
protected void onBoundsChange(Rect bounds) {
final Rect r = mTmpRect;
@@ -321,12 +332,12 @@ public class InsetDrawable extends Drawable implements Drawable.Callback
public Drawable newDrawable() {
return new InsetDrawable(this, null);
}
-
+
@Override
public Drawable newDrawable(Resources res) {
return new InsetDrawable(this, res);
}
-
+
@Override
public int getChangingConfigurations() {
return mChangingConfigurations;
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 27f0a9d..5cea7c9 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -16,12 +16,14 @@
package android.graphics.drawable;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
@@ -630,6 +632,15 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
}
}
+ @Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ final ChildDrawable[] array = mLayerState.mChildren;
+ final int N = mLayerState.mNum;
+ for (int i = 0; i < N; i++) {
+ array[i].mDrawable.setTint(tint, tintMode);
+ }
+ }
+
/**
* Sets the opacity of this drawable directly, instead of collecting the
* states from the layers
diff --git a/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java b/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java
index 9e56f67..c484094 100644
--- a/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java
+++ b/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java
@@ -27,8 +27,10 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
+import android.graphics.PorterDuff.Mode;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -67,6 +69,7 @@ class MaterialProgressDrawable extends Drawable implements Animatable {
private final Ring mRing;
private MaterialProgressState mState;
+ private PorterDuffColorFilter mTintFilter;
/** Canvas rotation in degrees. */
private float mRotation;
@@ -106,6 +109,8 @@ class MaterialProgressDrawable extends Drawable implements Animatable {
float insets = minEdge / 2.0f - state.mInnerRadius;
ring.setInsets(insets);
}
+
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
}
@Override
@@ -118,15 +123,21 @@ class MaterialProgressDrawable extends Drawable implements Animatable {
}
@Override
- protected boolean onStateChange(int[] state) {
- boolean changed = super.onStateChange(state);
+ protected boolean onStateChange(int[] stateSet) {
+ boolean changed = super.onStateChange(stateSet);
- final int color = mState.mColor.getColorForState(state, Color.TRANSPARENT);
+ final MaterialProgressState state = mState;
+ final int color = state.mColor.getColorForState(stateSet, Color.TRANSPARENT);
if (color != mRing.getColor()) {
mRing.setColor(color);
changed = true;
}
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ changed = true;
+ }
+
return changed;
}
@@ -223,11 +234,24 @@ class MaterialProgressDrawable extends Drawable implements Animatable {
return mRing.getColorFilter();
}
+ @Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ if (mState.mTint != tint || mState.mTintMode != tintMode) {
+ mState.mTint = tint;
+ mState.mTintMode = tintMode;
+
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
+ invalidateSelf();
+ }
+ }
+
+ @SuppressWarnings("unused")
private void setRotation(float rotation) {
mRotation = rotation;
invalidateSelf();
}
+ @SuppressWarnings("unused")
private float getRotation() {
return mRotation;
}
@@ -331,6 +355,8 @@ class MaterialProgressDrawable extends Drawable implements Animatable {
private int mWidth = -1;
private int mHeight = -1;
private ColorStateList mColor = ColorStateList.valueOf(Color.TRANSPARENT);
+ private ColorStateList mTint = null;
+ private Mode mTintMode = null;
public MaterialProgressState(MaterialProgressState orig) {
if (orig != null) {
@@ -340,6 +366,8 @@ class MaterialProgressDrawable extends Drawable implements Animatable {
mWidth = orig.mWidth;
mHeight = orig.mHeight;
mColor = orig.mColor;
+ mTint = orig.mTint;
+ mTintMode = orig.mTintMode;
}
}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index fea68ee..36ffddd 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -327,25 +327,12 @@ public class NinePatchDrawable extends Drawable {
@Override
public void setTint(ColorStateList tint, PorterDuff.Mode tintMode) {
- mNinePatchState.mTint = tint;
- mNinePatchState.mTintMode = tintMode;
- computeTintFilter();
- invalidateSelf();
- }
-
- private void computeTintFilter() {
final NinePatchState state = mNinePatchState;
- if (state.mTint != null && state.mTintMode != null) {
- final int color = state.mTint.getColorForState(getState(), 0);
- if (mTintFilter != null) {
- mTintFilter.setColor(color);
- mTintFilter.setMode(state.mTintMode);
- } else {
- mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
- }
- } else {
- mTintFilter = null;
- }
+ state.mTint = tint;
+ state.mTintMode = tintMode;
+
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
+ invalidateSelf();
}
@Override
@@ -549,15 +536,10 @@ public class NinePatchDrawable extends Drawable {
@Override
protected boolean onStateChange(int[] stateSet) {
- final ColorStateList tint = mNinePatchState.mTint;
- if (tint != null) {
- final int newColor = tint.getColorForState(stateSet, 0);
- final int oldColor = mTintFilter.getColor();
- if (oldColor != newColor) {
- mTintFilter.setColor(newColor);
- invalidateSelf();
- return true;
- }
+ final NinePatchState state = mNinePatchState;
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ return true;
}
return false;
@@ -689,7 +671,7 @@ public class NinePatchDrawable extends Drawable {
mPadding = new Rect(state.mPadding);
}
- computeTintFilter();
+ updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
setNinePatch(state.mNinePatch);
}
}
diff --git a/graphics/java/android/graphics/drawable/PaintDrawable.java b/graphics/java/android/graphics/drawable/PaintDrawable.java
index c71cda1..a82e7b9 100644
--- a/graphics/java/android/graphics/drawable/PaintDrawable.java
+++ b/graphics/java/android/graphics/drawable/PaintDrawable.java
@@ -35,7 +35,7 @@ public class PaintDrawable extends ShapeDrawable {
public PaintDrawable(int color) {
getPaint().setColor(color);
}
-
+
/**
* Specify radius for the corners of the rectangle. If this is > 0, then the
* drawable is drawn in a round-rectangle, rather than a rectangle.
@@ -51,7 +51,7 @@ public class PaintDrawable extends ShapeDrawable {
}
setCornerRadii(radii);
}
-
+
/**
* Specify radii for each of the 4 corners. For each corner, the array
* contains 2 values, [X_radius, Y_radius]. The corners are ordered
@@ -78,9 +78,9 @@ public class PaintDrawable extends ShapeDrawable {
int radius = a.getDimensionPixelSize(
com.android.internal.R.styleable.DrawableCorners_radius, 0);
setCornerRadius(radius);
-
+
// now check of they have any per-corner radii
-
+
int topLeftRadius = a.getDimensionPixelSize(
com.android.internal.R.styleable.DrawableCorners_topLeftRadius, radius);
int topRightRadius = a.getDimensionPixelSize(
diff --git a/graphics/java/android/graphics/drawable/PictureDrawable.java b/graphics/java/android/graphics/drawable/PictureDrawable.java
index cb2d8f6..6dcda1f 100644
--- a/graphics/java/android/graphics/drawable/PictureDrawable.java
+++ b/graphics/java/android/graphics/drawable/PictureDrawable.java
@@ -25,7 +25,7 @@ import android.graphics.Rect;
/**
* Drawable subclass that wraps a Picture, allowing the picture to be used
- * whereever a Drawable is supported.
+ * wherever a Drawable is supported.
*/
public class PictureDrawable extends Drawable {
@@ -40,7 +40,7 @@ public class PictureDrawable extends Drawable {
public PictureDrawable(Picture picture) {
mPicture = picture;
}
-
+
/**
* Return the picture associated with the drawable. May be null.
*
@@ -49,7 +49,7 @@ public class PictureDrawable extends Drawable {
public Picture getPicture() {
return mPicture;
}
-
+
/**
* Associate a picture with this drawable. The picture may be null.
*
@@ -58,7 +58,7 @@ public class PictureDrawable extends Drawable {
public void setPicture(Picture picture) {
mPicture = picture;
}
-
+
@Override
public void draw(Canvas canvas) {
if (mPicture != null) {
@@ -86,16 +86,16 @@ public class PictureDrawable extends Drawable {
// not sure, so be safe
return PixelFormat.TRANSLUCENT;
}
-
+
@Override
public void setFilterBitmap(boolean filter) {}
-
+
@Override
public void setDither(boolean dither) {}
-
+
@Override
public void setColorFilter(ColorFilter colorFilter) {}
-
+
@Override
public void setAlpha(int alpha) {}
}
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 06aeb98..8f8fa98 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -22,6 +22,8 @@ import org.xmlpull.v1.XmlPullParserException;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
+import android.graphics.PorterDuff.Mode;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
@@ -137,6 +139,11 @@ public class RotateDrawable extends Drawable implements Drawable.Callback {
}
@Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mState.mDrawable.setTint(tint, tintMode);
+ }
+
+ @Override
public int getOpacity() {
return mState.mDrawable.getOpacity();
}
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index f090c11..46c92fe 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -19,10 +19,12 @@ package android.graphics.drawable;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
import android.graphics.*;
+import android.graphics.PorterDuff.Mode;
import android.view.Gravity;
import android.util.AttributeSet;
@@ -188,6 +190,11 @@ public class ScaleDrawable extends Drawable implements Drawable.Callback {
}
@Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ mScaleState.mDrawable.setTint(tint, tintMode);
+ }
+
+ @Override
public int getOpacity() {
return mScaleState.mDrawable.getOpacity();
}
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 024f77c..981efb8 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -32,6 +32,7 @@ import android.graphics.drawable.shapes.Shape;
import android.content.res.Resources.Theme;
import android.util.AttributeSet;
+import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -74,7 +75,7 @@ public class ShapeDrawable extends Drawable {
* ShapeDrawable constructor.
*/
public ShapeDrawable() {
- this((ShapeState) null);
+ this(new ShapeState(null), null, null);
}
/**
@@ -83,20 +84,11 @@ public class ShapeDrawable extends Drawable {
* @param s the Shape that this ShapeDrawable should be
*/
public ShapeDrawable(Shape s) {
- this((ShapeState) null);
+ this(new ShapeState(null), null, null);
mShapeState.mShape = s;
}
- private ShapeDrawable(ShapeState state) {
- mShapeState = new ShapeState(state);
-
- if (state != null && state.mTint != null) {
- final int color = state.mTint.getColorForState(getState(), 0);
- mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
- }
- }
-
/**
* Returns the Shape of this ShapeDrawable.
*/
@@ -408,20 +400,8 @@ public class ShapeDrawable extends Drawable {
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs, theme);
- TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.ShapeDrawable);
-
- int color = mShapeState.mPaint.getColor();
- color = a.getColor(com.android.internal.R.styleable.ShapeDrawable_color, color);
- mShapeState.mPaint.setColor(color);
-
- boolean dither = a.getBoolean(com.android.internal.R.styleable.ShapeDrawable_dither, false);
- mShapeState.mPaint.setDither(dither);
-
- setIntrinsicWidth((int)
- a.getDimension(com.android.internal.R.styleable.ShapeDrawable_width, 0f));
- setIntrinsicHeight((int)
- a.getDimension(com.android.internal.R.styleable.ShapeDrawable_height, 0f));
-
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ShapeDrawable);
+ updateStateFromTypedArray(a);
a.recycle();
int type;
@@ -441,6 +421,38 @@ public class ShapeDrawable extends Drawable {
}
}
+ @Override
+ public void applyTheme(Theme t) {
+ super.applyTheme(t);
+
+ final ShapeState state = mShapeState;
+ if (state == null || state.mThemeAttrs == null) {
+ return;
+ }
+
+ final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ShapeDrawable);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+
+ private void updateStateFromTypedArray(TypedArray a) {
+ final ShapeState state = mShapeState;
+ final Paint paint = state.mPaint;
+
+ int color = paint.getColor();
+ color = a.getColor(R.styleable.ShapeDrawable_color, color);
+ paint.setColor(color);
+
+ boolean dither = paint.isDither();
+ dither = a.getBoolean(R.styleable.ShapeDrawable_dither, dither);
+ paint.setDither(dither);
+
+ setIntrinsicWidth((int) a.getDimension(
+ R.styleable.ShapeDrawable_width, state.mIntrinsicWidth));
+ setIntrinsicHeight((int) a.getDimension(
+ R.styleable.ShapeDrawable_height, state.mIntrinsicHeight));
+ }
+
private void updateShape() {
if (mShapeState.mShape != null) {
final Rect r = getBounds();
@@ -498,6 +510,7 @@ public class ShapeDrawable extends Drawable {
* Defines the intrinsic properties of this ShapeDrawable's Shape.
*/
final static class ShapeState extends ConstantState {
+ int[] mThemeAttrs;
int mChangingConfigurations;
Paint mPaint;
Shape mShape;
@@ -511,6 +524,7 @@ public class ShapeDrawable extends Drawable {
ShapeState(ShapeState orig) {
if (orig != null) {
+ mThemeAttrs = orig.mThemeAttrs;
mPaint = orig.mPaint;
mShape = orig.mShape;
mTint = orig.mTint;
@@ -526,13 +540,23 @@ public class ShapeDrawable extends Drawable {
}
@Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
+ }
+
+ @Override
public Drawable newDrawable() {
- return new ShapeDrawable(this);
+ return new ShapeDrawable(this, null, null);
}
@Override
public Drawable newDrawable(Resources res) {
- return new ShapeDrawable(this);
+ return new ShapeDrawable(this, res, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new ShapeDrawable(this, res, theme);
}
@Override
@@ -541,6 +565,17 @@ public class ShapeDrawable extends Drawable {
}
}
+ private ShapeDrawable(ShapeState state, Resources res, Theme theme) {
+ if (theme != null && state.canApplyTheme()) {
+ mShapeState = new ShapeState(state);
+ applyTheme(theme);
+ } else {
+ mShapeState = state;
+ }
+
+ mTintFilter = updateTintFilter(mTintFilter, mShapeState.mTint, mShapeState.mTintMode);
+ }
+
/**
* Base class defines a factory object that is called each time the drawable
* is resized (has a new width or height). Its resize() method returns a
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index b2fac9b..f359fdd 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -235,10 +235,10 @@ public class StateListDrawable extends DrawableContainer {
public Drawable getStateDrawable(int index) {
return mStateListState.getChild(index);
}
-
+
/**
* Gets the index of the drawable with the provided state set.
- *
+ *
* @param stateSet the state set to look up
* @return the index of the provided state set, or -1 if not found
* @hide pending API council
@@ -248,7 +248,7 @@ public class StateListDrawable extends DrawableContainer {
public int getStateDrawableIndex(int[] stateSet) {
return mStateListState.indexOfStateSet(stateSet);
}
-
+
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java
index 622e90b..4380ca4 100644
--- a/graphics/java/android/graphics/drawable/TransitionDrawable.java
+++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java
@@ -42,20 +42,20 @@ import android.os.SystemClock;
public class TransitionDrawable extends LayerDrawable implements Drawable.Callback {
/**
- * A transition is about to start.
+ * A transition is about to start.
*/
private static final int TRANSITION_STARTING = 0;
-
+
/**
* The transition has started and the animation is in progress
*/
private static final int TRANSITION_RUNNING = 1;
-
+
/**
* No transition will be applied
*/
private static final int TRANSITION_NONE = 2;
-
+
/**
* The current state of the transition. One of {@link #TRANSITION_STARTING},
* {@link #TRANSITION_RUNNING} and {@link #TRANSITION_NONE}
@@ -101,10 +101,10 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba
LayerState createConstantState(LayerState state, Resources res) {
return new TransitionState((TransitionState) state, this, res);
}
-
+
/**
* Begin the second layer on top of the first layer.
- *
+ *
* @param durationMillis The length of the transition in milliseconds
*/
public void startTransition(int durationMillis) {
@@ -116,7 +116,7 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba
mTransitionState = TRANSITION_STARTING;
invalidateSelf();
}
-
+
/**
* Show only the first layer.
*/
@@ -184,7 +184,7 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba
}
break;
}
-
+
final int alpha = mAlpha;
final boolean crossFade = mCrossFade;
final ChildDrawable[] array = mLayerState.mChildren;
@@ -217,7 +217,7 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba
d.draw(canvas);
d.setAlpha(0xFF);
}
-
+
if (!done) {
invalidateSelf();
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index c531c22..c3c1bca 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -14,6 +14,7 @@
package android.graphics.drawable;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -25,8 +26,10 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.PorterDuff.Mode;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
@@ -137,6 +140,8 @@ public class VectorDrawable extends Drawable {
private final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<String, Object>();
+ private PorterDuffColorFilter mTintFilter;
+
public VectorDrawable() {
mVectorState = new VectorDrawableState(null);
}
@@ -147,6 +152,9 @@ public class VectorDrawable extends Drawable {
if (theme != null && canApplyTheme()) {
applyTheme(theme);
}
+
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ mVectorState.mVPathRenderer.setColorFilter(mTintFilter);
}
Object getTargetByName(String name) {
@@ -182,11 +190,46 @@ public class VectorDrawable extends Drawable {
@Override
public void setColorFilter(ColorFilter colorFilter) {
- mVectorState.mVPathRenderer.setColorFilter(colorFilter);
+ final VectorDrawableState state = mVectorState;
+ if (colorFilter != null) {
+ // Color filter overrides tint.
+ mTintFilter = null;
+ } else if (state.mTint != null && state.mTintMode != null) {
+ // Restore the tint filter, if we need one.
+ final int color = state.mTint.getColorForState(getState(), Color.TRANSPARENT);
+ mTintFilter = new PorterDuffColorFilter(color, state.mTintMode);
+ colorFilter = mTintFilter;
+ }
+
+ state.mVPathRenderer.setColorFilter(colorFilter);
invalidateSelf();
}
@Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ final VectorDrawableState state = mVectorState;
+ if (state.mTint != tint || state.mTintMode != tintMode) {
+ state.mTint = tint;
+ state.mTintMode = tintMode;
+
+ mTintFilter = updateTintFilter(mTintFilter, tint, tintMode);
+ mVectorState.mVPathRenderer.setColorFilter(mTintFilter);
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ protected boolean onStateChange(int[] stateSet) {
+ final VectorDrawableState state = mVectorState;
+ if (state.mTint != null && state.mTintMode != null) {
+ mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+ mVectorState.mVPathRenderer.setColorFilter(mTintFilter);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@@ -397,6 +440,8 @@ public class VectorDrawable extends Drawable {
int mChangingConfigurations;
VPathRenderer mVPathRenderer;
Rect mPadding;
+ ColorStateList mTint;
+ Mode mTintMode;
public VectorDrawableState(VectorDrawableState copy) {
if (copy != null) {
@@ -404,6 +449,8 @@ public class VectorDrawable extends Drawable {
// TODO: Make sure the constant state are handled correctly.
mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
mPadding = new Rect(copy.mPadding);
+ mTint = copy.mTint;
+ mTintMode = copy.mTintMode;
}
}
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 60b4b96..1001cae 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -288,19 +288,19 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo
switch (bitmap->config()) {
case SkBitmap::kA8_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(),
+ uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
texture->blend = true;
break;
case SkBitmap::kRGB_565_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
- uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(),
+ uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
texture->blend = false;
break;
case SkBitmap::kARGB_8888_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
- uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(),
+ uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
// Do this after calling getPixels() to make sure Skia's deferred
// decoding happened
@@ -340,27 +340,49 @@ void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap,
SkCanvas canvas(rgbaBitmap);
canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
- uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), width, height,
- GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
+ uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(),
+ width, height, GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
}
-void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride,
+void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
- // TODO: With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
- // if the stride doesn't match the width
const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength();
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
- }
+ if ((stride == width) || useStride) {
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ }
- if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+ if (resize) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
+ }
+
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ }
} else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
- }
+ // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
+ // if the stride doesn't match the width
+
+ GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
+ if (!temp) return;
+
+ uint8_t * pDst = (uint8_t *)temp;
+ uint8_t * pSrc = (uint8_t *)data;
+ for (GLsizei i = 0; i < height; i++) {
+ memcpy(pDst, pSrc, width * bpp);
+ pDst += width * bpp;
+ pSrc += stride * bpp;
+ }
+
+ if (resize) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
+ }
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ free(temp);
}
}
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index e5b5c1a..61db5b0 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -142,7 +142,7 @@ private:
void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate = false);
void uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height);
- void uploadToTexture(bool resize, GLenum format, GLsizei stride,
+ void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
void init();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 6280fde..2f1e11e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -50,12 +50,6 @@ import java.util.ArrayList;
*/
public class AudioManager {
- // If we should use the new sessions APIs.
- private final static boolean USE_SESSIONS = true;
- // If we should use the legacy APIs. If both are true information will be
- // duplicated through both paths. Currently this flag isn't used.
- private final static boolean USE_LEGACY = true;
-
private final Context mContext;
private long mVolumeKeyUpTime;
private final boolean mUseMasterVolume;
@@ -483,17 +477,8 @@ public class AudioManager {
* or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
*/
public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.sendMediaButtonEvent(keyEvent, false);
- } else {
- IAudioService service = getService();
- try {
- service.dispatchMediaKeyEvent(keyEvent);
- } catch (RemoteException e) {
- Log.e(TAG, "dispatchMediaKeyEvent threw exception ", e);
- }
- }
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.sendMediaButtonEvent(keyEvent, false);
}
/**
@@ -644,12 +629,8 @@ public class AudioManager {
if (mUseMasterVolume) {
service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
} else {
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
- } else {
- service.adjustVolume(direction, flags, mContext.getOpPackageName());
- }
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustVolume", e);
@@ -679,13 +660,8 @@ public class AudioManager {
if (mUseMasterVolume) {
service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
} else {
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
- } else {
- service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
- mContext.getOpPackageName());
- }
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustSuggestedStreamVolume", e);
@@ -1425,7 +1401,12 @@ public class AudioManager {
* <var>false</var> to turn mute off
*/
public void setMicrophoneMute(boolean on){
- AudioSystem.muteMicrophone(on);
+ IAudioService service = getService();
+ try {
+ service.setMicrophoneMute(on, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setMicrophoneMute", e);
+ }
}
/**
@@ -2210,16 +2191,15 @@ public class AudioManager {
}
IAudioService service = getService();
try {
- // pi != null
+ // pi != null, this is currently still needed to support across
+ // reboot launching of the last app.
service.registerMediaButtonIntent(pi, eventReceiver,
eventReceiver == null ? mToken : null);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
}
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.addMediaButtonListener(pi, mContext);
- }
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.addMediaButtonListener(pi, mContext);
}
/**
@@ -2293,10 +2273,8 @@ public class AudioManager {
} catch (RemoteException e) {
Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
}
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
- helper.removeMediaButtonListener(pi);
- }
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.removeMediaButtonListener(pi);
}
/**
@@ -2310,20 +2288,7 @@ public class AudioManager {
if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
return;
}
- IAudioService service = getService();
- try {
- int rcseId = service.registerRemoteControlClient(
- rcClient.getRcMediaIntent(), /* mediaIntent */
- rcClient.getIRemoteControlClient(),/* rcClient */
- // used to match media button event receiver and audio focus
- mContext.getPackageName()); /* packageName */
- rcClient.setRcseId(rcseId);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerRemoteControlClient"+e);
- }
- if (USE_SESSIONS) {
- rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext));
- }
+ rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext));
}
/**
@@ -2336,16 +2301,7 @@ public class AudioManager {
if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
return;
}
- IAudioService service = getService();
- try {
- service.unregisterRemoteControlClient(rcClient.getRcMediaIntent(), /* mediaIntent */
- rcClient.getIRemoteControlClient()); /* rcClient */
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e);
- }
- if (USE_SESSIONS) {
- rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mContext));
- }
+ rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(mContext));
}
/**
@@ -2363,25 +2319,8 @@ public class AudioManager {
if (rctlr == null) {
return false;
}
- if (USE_SESSIONS) {
- rctlr.startListeningToSessions();
- return true;
- } else {
- IAudioService service = getService();
- final RemoteController.OnClientUpdateListener l = rctlr.getUpdateListener();
- final ComponentName listenerComponent = new ComponentName(mContext, l.getClass());
- try {
- int[] artworkDimensions = rctlr.getArtworkSize();
- boolean reg = service.registerRemoteController(rctlr.getRcDisplay(),
- artworkDimensions[0]/* w */, artworkDimensions[1]/* h */,
- listenerComponent);
- rctlr.setIsRegistered(reg);
- return reg;
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerRemoteController " + e);
- return false;
- }
- }
+ rctlr.startListeningToSessions();
+ return true;
}
/**
@@ -2393,17 +2332,7 @@ public class AudioManager {
if (rctlr == null) {
return;
}
- if (USE_SESSIONS) {
- rctlr.stopListeningToSessions();
- } else {
- IAudioService service = getService();
- try {
- service.unregisterRemoteControlDisplay(rctlr.getRcDisplay());
- rctlr.setIsRegistered(false);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e);
- }
- }
+ rctlr.stopListeningToSessions();
}
/**
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 2f782cc..72f4a58 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -108,8 +108,7 @@ public class AudioService extends IAudioService.Stub {
/** Debug volumes */
protected static final boolean DEBUG_VOL = false;
- /** Reroute calls to media session apis */
- private static final boolean USE_SESSIONS = true;
+ /** debug calls to media session apis */
private static final boolean DEBUG_SESSIONS = true;
/** Allow volume changes to set ringer mode to silent? */
@@ -1437,6 +1436,16 @@ public class AudioService extends IAudioService.Stub {
}
}
+ /** @see AudioManager#setMicrophoneMute(boolean) */
+ public void setMicrophoneMute(boolean on, String callingPackage) {
+ if (mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+
+ AudioSystem.muteMicrophone(on);
+ }
+
/** @see AudioManager#getRingerMode() */
public int getRingerMode() {
synchronized(mSettingsLock) {
@@ -4474,27 +4483,19 @@ public class AudioService extends IAudioService.Stub {
}
public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
- if (USE_SESSIONS) {
- if (DEBUG_SESSIONS) {
- int pid = getCallingPid();
- Log.w(TAG, "Call to dispatchMediaKeyEvent from " + pid);
- }
- MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
- } else {
- mMediaFocusControl.dispatchMediaKeyEvent(keyEvent);
+ if (DEBUG_SESSIONS) {
+ int pid = getCallingPid();
+ Log.w(TAG, "Call to dispatchMediaKeyEvent from " + pid);
}
+ MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
}
public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
- if (USE_SESSIONS) {
- if (DEBUG_SESSIONS) {
- int pid = getCallingPid();
- Log.w(TAG, "Call to dispatchMediaKeyEventUnderWakelock from " + pid);
- }
- MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, true);
- } else {
- mMediaFocusControl.dispatchMediaKeyEventUnderWakelock(keyEvent);
+ if (DEBUG_SESSIONS) {
+ int pid = getCallingPid();
+ Log.w(TAG, "Call to dispatchMediaKeyEventUnderWakelock from " + pid);
}
+ MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, true);
}
//==========================================================================================
diff --git a/media/java/android/media/ClosedCaptionRenderer.java b/media/java/android/media/ClosedCaptionRenderer.java
new file mode 100644
index 0000000..ec33c5c
--- /dev/null
+++ b/media/java/android/media/ClosedCaptionRenderer.java
@@ -0,0 +1,1464 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.CharacterStyle;
+import android.text.style.StyleSpan;
+import android.text.style.UnderlineSpan;
+import android.text.style.UpdateAppearance;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.CaptioningManager;
+import android.view.accessibility.CaptioningManager.CaptionStyle;
+import android.view.accessibility.CaptioningManager.CaptioningChangeListener;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Vector;
+
+/** @hide */
+public class ClosedCaptionRenderer extends SubtitleController.Renderer {
+ private final Context mContext;
+ private ClosedCaptionWidget mRenderingWidget;
+
+ public ClosedCaptionRenderer(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public boolean supports(MediaFormat format) {
+ if (format.containsKey(MediaFormat.KEY_MIME)) {
+ return format.getString(MediaFormat.KEY_MIME).equals(
+ MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608);
+ }
+ return false;
+ }
+
+ @Override
+ public SubtitleTrack createTrack(MediaFormat format) {
+ if (mRenderingWidget == null) {
+ mRenderingWidget = new ClosedCaptionWidget(mContext);
+ }
+ return new ClosedCaptionTrack(mRenderingWidget, format);
+ }
+}
+
+/** @hide */
+class ClosedCaptionTrack extends SubtitleTrack {
+ private final ClosedCaptionWidget mRenderingWidget;
+ private final CCParser mCCParser;
+
+ ClosedCaptionTrack(ClosedCaptionWidget renderingWidget, MediaFormat format) {
+ super(format);
+
+ mRenderingWidget = renderingWidget;
+ mCCParser = new CCParser(renderingWidget);
+ }
+
+ @Override
+ public void onData(byte[] data, boolean eos, long runID) {
+ mCCParser.parse(data);
+ }
+
+ @Override
+ public RenderingWidget getRenderingWidget() {
+ return mRenderingWidget;
+ }
+
+ @Override
+ public void updateView(Vector<Cue> activeCues) {
+ // Overriding with NO-OP, CC rendering by-passes this
+ }
+}
+
+/**
+ * @hide
+ *
+ * CCParser processes CEA-608 closed caption data.
+ *
+ * It calls back into OnDisplayChangedListener upon
+ * display change with styled text for rendering.
+ *
+ */
+class CCParser {
+ public static final int MAX_ROWS = 15;
+ public static final int MAX_COLS = 32;
+
+ private static final String TAG = "CCParser";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final int INVALID = -1;
+
+ // EIA-CEA-608: Table 70 - Control Codes
+ private static final int RCL = 0x20;
+ private static final int BS = 0x21;
+ private static final int AOF = 0x22;
+ private static final int AON = 0x23;
+ private static final int DER = 0x24;
+ private static final int RU2 = 0x25;
+ private static final int RU3 = 0x26;
+ private static final int RU4 = 0x27;
+ private static final int FON = 0x28;
+ private static final int RDC = 0x29;
+ private static final int TR = 0x2a;
+ private static final int RTD = 0x2b;
+ private static final int EDM = 0x2c;
+ private static final int CR = 0x2d;
+ private static final int ENM = 0x2e;
+ private static final int EOC = 0x2f;
+
+ // Transparent Space
+ private static final char TS = '\u00A0';
+
+ // Captioning Modes
+ private static final int MODE_UNKNOWN = 0;
+ private static final int MODE_PAINT_ON = 1;
+ private static final int MODE_ROLL_UP = 2;
+ private static final int MODE_POP_ON = 3;
+ private static final int MODE_TEXT = 4;
+
+ private final DisplayListener mListener;
+
+ private int mMode = MODE_PAINT_ON;
+ private int mRollUpSize = 4;
+
+ private CCMemory mDisplay = new CCMemory();
+ private CCMemory mNonDisplay = new CCMemory();
+ private CCMemory mTextMem = new CCMemory();
+
+ CCParser(DisplayListener listener) {
+ mListener = listener;
+ }
+
+ void parse(byte[] data) {
+ CCData[] ccData = CCData.fromByteArray(data);
+
+ for (int i = 0; i < ccData.length; i++) {
+ if (DEBUG) {
+ Log.d(TAG, ccData[i].toString());
+ }
+
+ if (handleCtrlCode(ccData[i])
+ || handleTabOffsets(ccData[i])
+ || handlePACCode(ccData[i])
+ || handleMidRowCode(ccData[i])) {
+ continue;
+ }
+
+ handleDisplayableChars(ccData[i]);
+ }
+ }
+
+ interface DisplayListener {
+ public void onDisplayChanged(SpannableStringBuilder[] styledTexts);
+ public CaptionStyle getCaptionStyle();
+ }
+
+ private CCMemory getMemory() {
+ // get the CC memory to operate on for current mode
+ switch (mMode) {
+ case MODE_POP_ON:
+ return mNonDisplay;
+ case MODE_TEXT:
+ // TODO(chz): support only caption mode for now,
+ // in text mode, dump everything to text mem.
+ return mTextMem;
+ case MODE_PAINT_ON:
+ case MODE_ROLL_UP:
+ return mDisplay;
+ default:
+ Log.w(TAG, "unrecoginized mode: " + mMode);
+ }
+ return mDisplay;
+ }
+
+ private boolean handleDisplayableChars(CCData ccData) {
+ if (!ccData.isDisplayableChar()) {
+ return false;
+ }
+
+ // Extended char includes 1 automatic backspace
+ if (ccData.isExtendedChar()) {
+ getMemory().bs();
+ }
+
+ getMemory().writeText(ccData.getDisplayText());
+
+ if (mMode == MODE_PAINT_ON || mMode == MODE_ROLL_UP) {
+ updateDisplay();
+ }
+
+ return true;
+ }
+
+ private boolean handleMidRowCode(CCData ccData) {
+ StyleCode m = ccData.getMidRow();
+ if (m != null) {
+ getMemory().writeMidRowCode(m);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean handlePACCode(CCData ccData) {
+ PAC pac = ccData.getPAC();
+
+ if (pac != null) {
+ if (mMode == MODE_ROLL_UP) {
+ getMemory().moveBaselineTo(pac.getRow(), mRollUpSize);
+ }
+ getMemory().writePAC(pac);
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean handleTabOffsets(CCData ccData) {
+ int tabs = ccData.getTabOffset();
+
+ if (tabs > 0) {
+ getMemory().tab(tabs);
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean handleCtrlCode(CCData ccData) {
+ int ctrlCode = ccData.getCtrlCode();
+ switch(ctrlCode) {
+ case RCL:
+ // select pop-on style
+ mMode = MODE_POP_ON;
+ break;
+ case BS:
+ getMemory().bs();
+ break;
+ case DER:
+ getMemory().der();
+ break;
+ case RU2:
+ case RU3:
+ case RU4:
+ mRollUpSize = (ctrlCode - 0x23);
+ // erase memory if currently in other style
+ if (mMode != MODE_ROLL_UP) {
+ mDisplay.erase();
+ mNonDisplay.erase();
+ }
+ // select roll-up style
+ mMode = MODE_ROLL_UP;
+ break;
+ case FON:
+ Log.i(TAG, "Flash On");
+ break;
+ case RDC:
+ // select paint-on style
+ mMode = MODE_PAINT_ON;
+ break;
+ case TR:
+ mMode = MODE_TEXT;
+ mTextMem.erase();
+ break;
+ case RTD:
+ mMode = MODE_TEXT;
+ break;
+ case EDM:
+ // erase display memory
+ mDisplay.erase();
+ updateDisplay();
+ break;
+ case CR:
+ if (mMode == MODE_ROLL_UP) {
+ getMemory().rollUp(mRollUpSize);
+ } else {
+ getMemory().cr();
+ }
+ if (mMode == MODE_ROLL_UP) {
+ updateDisplay();
+ }
+ break;
+ case ENM:
+ // erase non-display memory
+ mNonDisplay.erase();
+ break;
+ case EOC:
+ // swap display/non-display memory
+ swapMemory();
+ // switch to pop-on style
+ mMode = MODE_POP_ON;
+ updateDisplay();
+ break;
+ case INVALID:
+ default:
+ // not handled
+ return false;
+ }
+
+ // handled
+ return true;
+ }
+
+ private void updateDisplay() {
+ if (mListener != null) {
+ CaptionStyle captionStyle = mListener.getCaptionStyle();
+ mListener.onDisplayChanged(mDisplay.getStyledText(captionStyle));
+ }
+ }
+
+ private void swapMemory() {
+ CCMemory temp = mDisplay;
+ mDisplay = mNonDisplay;
+ mNonDisplay = temp;
+ }
+
+ private static class StyleCode {
+ static final int COLOR_WHITE = 0;
+ static final int COLOR_GREEN = 1;
+ static final int COLOR_BLUE = 2;
+ static final int COLOR_CYAN = 3;
+ static final int COLOR_RED = 4;
+ static final int COLOR_YELLOW = 5;
+ static final int COLOR_MAGENTA = 6;
+ static final int COLOR_INVALID = 7;
+
+ static final int STYLE_ITALICS = 0x00000001;
+ static final int STYLE_UNDERLINE = 0x00000002;
+
+ static final String[] mColorMap = {
+ "WHITE", "GREEN", "BLUE", "CYAN", "RED", "YELLOW", "MAGENTA", "INVALID"
+ };
+
+ final int mStyle;
+ final int mColor;
+
+ static StyleCode fromByte(byte data2) {
+ int style = 0;
+ int color = (data2 >> 1) & 0x7;
+
+ if ((data2 & 0x1) != 0) {
+ style |= STYLE_UNDERLINE;
+ }
+
+ if (color == COLOR_INVALID) {
+ // WHITE ITALICS
+ color = COLOR_WHITE;
+ style |= STYLE_ITALICS;
+ }
+
+ return new StyleCode(style, color);
+ }
+
+ StyleCode(int style, int color) {
+ mStyle = style;
+ mColor = color;
+ }
+
+ boolean isItalics() {
+ return (mStyle & STYLE_ITALICS) != 0;
+ }
+
+ boolean isUnderline() {
+ return (mStyle & STYLE_UNDERLINE) != 0;
+ }
+
+ int getColor() {
+ return mColor;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+ str.append("{");
+ str.append(mColorMap[mColor]);
+ if ((mStyle & STYLE_ITALICS) != 0) {
+ str.append(", ITALICS");
+ }
+ if ((mStyle & STYLE_UNDERLINE) != 0) {
+ str.append(", UNDERLINE");
+ }
+ str.append("}");
+
+ return str.toString();
+ }
+ }
+
+ private static class PAC extends StyleCode {
+ final int mRow;
+ final int mCol;
+
+ static PAC fromBytes(byte data1, byte data2) {
+ int[] rowTable = {11, 1, 3, 12, 14, 5, 7, 9};
+ int row = rowTable[data1 & 0x07] + ((data2 & 0x20) >> 5);
+ int style = 0;
+ if ((data2 & 1) != 0) {
+ style |= STYLE_UNDERLINE;
+ }
+ if ((data2 & 0x10) != 0) {
+ // indent code
+ int indent = (data2 >> 1) & 0x7;
+ return new PAC(row, indent * 4, style, COLOR_WHITE);
+ } else {
+ // style code
+ int color = (data2 >> 1) & 0x7;
+
+ if (color == COLOR_INVALID) {
+ // WHITE ITALICS
+ color = COLOR_WHITE;
+ style |= STYLE_ITALICS;
+ }
+ return new PAC(row, -1, style, color);
+ }
+ }
+
+ PAC(int row, int col, int style, int color) {
+ super(style, color);
+ mRow = row;
+ mCol = col;
+ }
+
+ boolean isIndentPAC() {
+ return (mCol >= 0);
+ }
+
+ int getRow() {
+ return mRow;
+ }
+
+ int getCol() {
+ return mCol;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{%d, %d}, %s",
+ mRow, mCol, super.toString());
+ }
+ }
+
+ /* CCLineBuilder keeps track of displayable chars, as well as
+ * MidRow styles and PACs, for a single line of CC memory.
+ *
+ * It generates styled text via getStyledText() method.
+ */
+ private static class CCLineBuilder {
+ private final StringBuilder mDisplayChars;
+ private final StyleCode[] mMidRowStyles;
+ private final StyleCode[] mPACStyles;
+
+ CCLineBuilder(String str) {
+ mDisplayChars = new StringBuilder(str);
+ mMidRowStyles = new StyleCode[mDisplayChars.length()];
+ mPACStyles = new StyleCode[mDisplayChars.length()];
+ }
+
+ void setCharAt(int index, char ch) {
+ mDisplayChars.setCharAt(index, ch);
+ mMidRowStyles[index] = null;
+ }
+
+ void setMidRowAt(int index, StyleCode m) {
+ mDisplayChars.setCharAt(index, ' ');
+ mMidRowStyles[index] = m;
+ }
+
+ void setPACAt(int index, PAC pac) {
+ mPACStyles[index] = pac;
+ }
+
+ char charAt(int index) {
+ return mDisplayChars.charAt(index);
+ }
+
+ int length() {
+ return mDisplayChars.length();
+ }
+
+ void applyStyleSpan(
+ SpannableStringBuilder styledText,
+ StyleCode s, int start, int end) {
+ if (s.isItalics()) {
+ styledText.setSpan(
+ new StyleSpan(android.graphics.Typeface.ITALIC),
+ start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (s.isUnderline()) {
+ styledText.setSpan(
+ new UnderlineSpan(),
+ start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+ SpannableStringBuilder getStyledText(CaptionStyle captionStyle) {
+ SpannableStringBuilder styledText = new SpannableStringBuilder(mDisplayChars);
+ int start = -1, next = 0;
+ int styleStart = -1;
+ StyleCode curStyle = null;
+ while (next < mDisplayChars.length()) {
+ StyleCode newStyle = null;
+ if (mMidRowStyles[next] != null) {
+ // apply mid-row style change
+ newStyle = mMidRowStyles[next];
+ } else if (mPACStyles[next] != null
+ && (styleStart < 0 || start < 0)) {
+ // apply PAC style change, only if:
+ // 1. no style set, or
+ // 2. style set, but prev char is none-displayable
+ newStyle = mPACStyles[next];
+ }
+ if (newStyle != null) {
+ curStyle = newStyle;
+ if (styleStart >= 0 && start >= 0) {
+ applyStyleSpan(styledText, newStyle, styleStart, next);
+ }
+ styleStart = next;
+ }
+
+ if (mDisplayChars.charAt(next) != TS) {
+ if (start < 0) {
+ start = next;
+ }
+ } else if (start >= 0) {
+ int expandedStart = mDisplayChars.charAt(start) == ' ' ? start : start - 1;
+ int expandedEnd = mDisplayChars.charAt(next - 1) == ' ' ? next : next + 1;
+ styledText.setSpan(
+ new MutableBackgroundColorSpan(captionStyle.backgroundColor),
+ expandedStart, expandedEnd,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (styleStart >= 0) {
+ applyStyleSpan(styledText, curStyle, styleStart, expandedEnd);
+ }
+ start = -1;
+ }
+ next++;
+ }
+
+ return styledText;
+ }
+ }
+
+ /*
+ * CCMemory models a console-style display.
+ */
+ private static class CCMemory {
+ private final String mBlankLine;
+ private final CCLineBuilder[] mLines = new CCLineBuilder[MAX_ROWS + 2];
+ private int mRow;
+ private int mCol;
+
+ CCMemory() {
+ char[] blank = new char[MAX_COLS + 2];
+ Arrays.fill(blank, TS);
+ mBlankLine = new String(blank);
+ }
+
+ void erase() {
+ // erase all lines
+ for (int i = 0; i < mLines.length; i++) {
+ mLines[i] = null;
+ }
+ mRow = MAX_ROWS;
+ mCol = 1;
+ }
+
+ void der() {
+ if (mLines[mRow] != null) {
+ for (int i = 0; i < mCol; i++) {
+ if (mLines[mRow].charAt(i) != TS) {
+ for (int j = mCol; j < mLines[mRow].length(); j++) {
+ mLines[j].setCharAt(j, TS);
+ }
+ return;
+ }
+ }
+ mLines[mRow] = null;
+ }
+ }
+
+ void tab(int tabs) {
+ moveCursorByCol(tabs);
+ }
+
+ void bs() {
+ moveCursorByCol(-1);
+ if (mLines[mRow] != null) {
+ mLines[mRow].setCharAt(mCol, TS);
+ if (mCol == MAX_COLS - 1) {
+ // Spec recommendation:
+ // if cursor was at col 32, move cursor
+ // back to col 31 and erase both col 31&32
+ mLines[mRow].setCharAt(MAX_COLS, TS);
+ }
+ }
+ }
+
+ void cr() {
+ moveCursorTo(mRow + 1, 1);
+ }
+
+ void rollUp(int windowSize) {
+ int i;
+ for (i = 0; i <= mRow - windowSize; i++) {
+ mLines[i] = null;
+ }
+ int startRow = mRow - windowSize + 1;
+ if (startRow < 1) {
+ startRow = 1;
+ }
+ for (i = startRow; i < mRow; i++) {
+ mLines[i] = mLines[i + 1];
+ }
+ for (i = mRow; i < mLines.length; i++) {
+ // clear base row
+ mLines[i] = null;
+ }
+ // default to col 1, in case PAC is not sent
+ mCol = 1;
+ }
+
+ void writeText(String text) {
+ for (int i = 0; i < text.length(); i++) {
+ getLineBuffer(mRow).setCharAt(mCol, text.charAt(i));
+ moveCursorByCol(1);
+ }
+ }
+
+ void writeMidRowCode(StyleCode m) {
+ getLineBuffer(mRow).setMidRowAt(mCol, m);
+ moveCursorByCol(1);
+ }
+
+ void writePAC(PAC pac) {
+ if (pac.isIndentPAC()) {
+ moveCursorTo(pac.getRow(), pac.getCol());
+ } else {
+ moveCursorToRow(pac.getRow());
+ }
+ getLineBuffer(mRow).setPACAt(mCol, pac);
+ }
+
+ SpannableStringBuilder[] getStyledText(CaptionStyle captionStyle) {
+ ArrayList<SpannableStringBuilder> rows =
+ new ArrayList<SpannableStringBuilder>(MAX_ROWS);
+ for (int i = 1; i <= MAX_ROWS; i++) {
+ rows.add(mLines[i] != null ?
+ mLines[i].getStyledText(captionStyle) : null);
+ }
+ return rows.toArray(new SpannableStringBuilder[MAX_ROWS]);
+ }
+
+ private static int clamp(int x, int min, int max) {
+ return x < min ? min : (x > max ? max : x);
+ }
+
+ private void moveCursorTo(int row, int col) {
+ mRow = clamp(row, 1, MAX_ROWS);
+ mCol = clamp(col, 1, MAX_COLS);
+ }
+
+ private void moveCursorToRow(int row) {
+ mRow = clamp(row, 1, MAX_ROWS);
+ }
+
+ private void moveCursorByCol(int col) {
+ mCol = clamp(mCol + col, 1, MAX_COLS);
+ }
+
+ private void moveBaselineTo(int baseRow, int windowSize) {
+ if (mRow == baseRow) {
+ return;
+ }
+ int actualWindowSize = windowSize;
+ if (baseRow < actualWindowSize) {
+ actualWindowSize = baseRow;
+ }
+ if (mRow < actualWindowSize) {
+ actualWindowSize = mRow;
+ }
+
+ int i;
+ if (baseRow < mRow) {
+ // copy from bottom to top row
+ for (i = actualWindowSize - 1; i >= 0; i--) {
+ mLines[baseRow - i] = mLines[mRow - i];
+ }
+ } else {
+ // copy from top to bottom row
+ for (i = 0; i < actualWindowSize; i++) {
+ mLines[baseRow - i] = mLines[mRow - i];
+ }
+ }
+ // clear rest of the rows
+ for (i = 0; i <= baseRow - windowSize; i++) {
+ mLines[i] = null;
+ }
+ for (i = baseRow + 1; i < mLines.length; i++) {
+ mLines[i] = null;
+ }
+ }
+
+ private CCLineBuilder getLineBuffer(int row) {
+ if (mLines[row] == null) {
+ mLines[row] = new CCLineBuilder(mBlankLine);
+ }
+ return mLines[row];
+ }
+ }
+
+ /*
+ * CCData parses the raw CC byte pair into displayable chars,
+ * misc control codes, Mid-Row or Preamble Address Codes.
+ */
+ private static class CCData {
+ private final byte mType;
+ private final byte mData1;
+ private final byte mData2;
+
+ private static final String[] mCtrlCodeMap = {
+ "RCL", "BS" , "AOF", "AON",
+ "DER", "RU2", "RU3", "RU4",
+ "FON", "RDC", "TR" , "RTD",
+ "EDM", "CR" , "ENM", "EOC",
+ };
+
+ private static final String[] mSpecialCharMap = {
+ "\u00AE",
+ "\u00B0",
+ "\u00BD",
+ "\u00BF",
+ "\u2122",
+ "\u00A2",
+ "\u00A3",
+ "\u266A", // Eighth note
+ "\u00E0",
+ "\u00A0", // Transparent space
+ "\u00E8",
+ "\u00E2",
+ "\u00EA",
+ "\u00EE",
+ "\u00F4",
+ "\u00FB",
+ };
+
+ private static final String[] mSpanishCharMap = {
+ // Spanish and misc chars
+ "\u00C1", // A
+ "\u00C9", // E
+ "\u00D3", // I
+ "\u00DA", // O
+ "\u00DC", // U
+ "\u00FC", // u
+ "\u2018", // opening single quote
+ "\u00A1", // inverted exclamation mark
+ "*",
+ "'",
+ "\u2014", // em dash
+ "\u00A9", // Copyright
+ "\u2120", // Servicemark
+ "\u2022", // round bullet
+ "\u201C", // opening double quote
+ "\u201D", // closing double quote
+ // French
+ "\u00C0",
+ "\u00C2",
+ "\u00C7",
+ "\u00C8",
+ "\u00CA",
+ "\u00CB",
+ "\u00EB",
+ "\u00CE",
+ "\u00CF",
+ "\u00EF",
+ "\u00D4",
+ "\u00D9",
+ "\u00F9",
+ "\u00DB",
+ "\u00AB",
+ "\u00BB"
+ };
+
+ private static final String[] mProtugueseCharMap = {
+ // Portuguese
+ "\u00C3",
+ "\u00E3",
+ "\u00CD",
+ "\u00CC",
+ "\u00EC",
+ "\u00D2",
+ "\u00F2",
+ "\u00D5",
+ "\u00F5",
+ "{",
+ "}",
+ "\\",
+ "^",
+ "_",
+ "|",
+ "~",
+ // German and misc chars
+ "\u00C4",
+ "\u00E4",
+ "\u00D6",
+ "\u00F6",
+ "\u00DF",
+ "\u00A5",
+ "\u00A4",
+ "\u2502", // vertical bar
+ "\u00C5",
+ "\u00E5",
+ "\u00D8",
+ "\u00F8",
+ "\u250C", // top-left corner
+ "\u2510", // top-right corner
+ "\u2514", // lower-left corner
+ "\u2518", // lower-right corner
+ };
+
+ static CCData[] fromByteArray(byte[] data) {
+ CCData[] ccData = new CCData[data.length / 3];
+
+ for (int i = 0; i < ccData.length; i++) {
+ ccData[i] = new CCData(
+ data[i * 3],
+ data[i * 3 + 1],
+ data[i * 3 + 2]);
+ }
+
+ return ccData;
+ }
+
+ CCData(byte type, byte data1, byte data2) {
+ mType = type;
+ mData1 = data1;
+ mData2 = data2;
+ }
+
+ int getCtrlCode() {
+ if ((mData1 == 0x14 || mData1 == 0x1c)
+ && mData2 >= 0x20 && mData2 <= 0x2f) {
+ return mData2;
+ }
+ return INVALID;
+ }
+
+ StyleCode getMidRow() {
+ // only support standard Mid-row codes, ignore
+ // optional background/foreground mid-row codes
+ if ((mData1 == 0x11 || mData1 == 0x19)
+ && mData2 >= 0x20 && mData2 <= 0x2f) {
+ return StyleCode.fromByte(mData2);
+ }
+ return null;
+ }
+
+ PAC getPAC() {
+ if ((mData1 & 0x70) == 0x10
+ && (mData2 & 0x40) == 0x40
+ && ((mData1 & 0x07) != 0 || (mData2 & 0x20) == 0)) {
+ return PAC.fromBytes(mData1, mData2);
+ }
+ return null;
+ }
+
+ int getTabOffset() {
+ if ((mData1 == 0x17 || mData1 == 0x1f)
+ && mData2 >= 0x21 && mData2 <= 0x23) {
+ return mData2 & 0x3;
+ }
+ return 0;
+ }
+
+ boolean isDisplayableChar() {
+ return isBasicChar() || isSpecialChar() || isExtendedChar();
+ }
+
+ String getDisplayText() {
+ String str = getBasicChars();
+
+ if (str == null) {
+ str = getSpecialChar();
+
+ if (str == null) {
+ str = getExtendedChar();
+ }
+ }
+
+ return str;
+ }
+
+ private String ctrlCodeToString(int ctrlCode) {
+ return mCtrlCodeMap[ctrlCode - 0x20];
+ }
+
+ private boolean isBasicChar() {
+ return mData1 >= 0x20 && mData1 <= 0x7f;
+ }
+
+ private boolean isSpecialChar() {
+ return ((mData1 == 0x11 || mData1 == 0x19)
+ && mData2 >= 0x30 && mData2 <= 0x3f);
+ }
+
+ private boolean isExtendedChar() {
+ return ((mData1 == 0x12 || mData1 == 0x1A
+ || mData1 == 0x13 || mData1 == 0x1B)
+ && mData2 >= 0x20 && mData2 <= 0x3f);
+ }
+
+ private char getBasicChar(byte data) {
+ char c;
+ // replace the non-ASCII ones
+ switch (data) {
+ case 0x2A: c = '\u00E1'; break;
+ case 0x5C: c = '\u00E9'; break;
+ case 0x5E: c = '\u00ED'; break;
+ case 0x5F: c = '\u00F3'; break;
+ case 0x60: c = '\u00FA'; break;
+ case 0x7B: c = '\u00E7'; break;
+ case 0x7C: c = '\u00F7'; break;
+ case 0x7D: c = '\u00D1'; break;
+ case 0x7E: c = '\u00F1'; break;
+ case 0x7F: c = '\u2588'; break; // Full block
+ default: c = (char) data; break;
+ }
+ return c;
+ }
+
+ private String getBasicChars() {
+ if (mData1 >= 0x20 && mData1 <= 0x7f) {
+ StringBuilder builder = new StringBuilder(2);
+ builder.append(getBasicChar(mData1));
+ if (mData2 >= 0x20 && mData2 <= 0x7f) {
+ builder.append(getBasicChar(mData2));
+ }
+ return builder.toString();
+ }
+
+ return null;
+ }
+
+ private String getSpecialChar() {
+ if ((mData1 == 0x11 || mData1 == 0x19)
+ && mData2 >= 0x30 && mData2 <= 0x3f) {
+ return mSpecialCharMap[mData2 - 0x30];
+ }
+
+ return null;
+ }
+
+ private String getExtendedChar() {
+ if ((mData1 == 0x12 || mData1 == 0x1A)
+ && mData2 >= 0x20 && mData2 <= 0x3f){
+ // 1 Spanish/French char
+ return mSpanishCharMap[mData2 - 0x20];
+ } else if ((mData1 == 0x13 || mData1 == 0x1B)
+ && mData2 >= 0x20 && mData2 <= 0x3f){
+ // 1 Portuguese/German/Danish char
+ return mProtugueseCharMap[mData2 - 0x20];
+ }
+
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ String str;
+
+ if (mData1 < 0x10 && mData2 < 0x10) {
+ // Null Pad, ignore
+ return String.format("[%d]Null: %02x %02x", mType, mData1, mData2);
+ }
+
+ int ctrlCode = getCtrlCode();
+ if (ctrlCode != INVALID) {
+ return String.format("[%d]%s", mType, ctrlCodeToString(ctrlCode));
+ }
+
+ int tabOffset = getTabOffset();
+ if (tabOffset > 0) {
+ return String.format("[%d]Tab%d", mType, tabOffset);
+ }
+
+ PAC pac = getPAC();
+ if (pac != null) {
+ return String.format("[%d]PAC: %s", mType, pac.toString());
+ }
+
+ StyleCode m = getMidRow();
+ if (m != null) {
+ return String.format("[%d]Mid-row: %s", mType, m.toString());
+ }
+
+ if (isDisplayableChar()) {
+ return String.format("[%d]Displayable: %s (%02x %02x)",
+ mType, getDisplayText(), mData1, mData2);
+ }
+
+ return String.format("[%d]Invalid: %02x %02x", mType, mData1, mData2);
+ }
+ }
+}
+
+/**
+ * @hide
+ *
+ * MutableBackgroundColorSpan
+ *
+ * This is a mutable version of BackgroundSpan to facilitate text
+ * rendering with edge styles.
+ *
+ */
+class MutableBackgroundColorSpan extends CharacterStyle
+ implements UpdateAppearance, ParcelableSpan {
+ private int mColor;
+
+ public MutableBackgroundColorSpan(int color) {
+ mColor = color;
+ }
+ public MutableBackgroundColorSpan(Parcel src) {
+ mColor = src.readInt();
+ }
+ public void setBackgroundColor(int color) {
+ mColor = color;
+ }
+ public int getBackgroundColor() {
+ return mColor;
+ }
+ @Override
+ public int getSpanTypeId() {
+ return TextUtils.BACKGROUND_COLOR_SPAN;
+ }
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mColor);
+ }
+ @Override
+ public void updateDrawState(TextPaint ds) {
+ ds.bgColor = mColor;
+ }
+}
+
+/**
+ * Widget capable of rendering CEA-608 closed captions.
+ *
+ * @hide
+ */
+class ClosedCaptionWidget extends ViewGroup implements
+ SubtitleTrack.RenderingWidget,
+ CCParser.DisplayListener {
+ private static final String TAG = "ClosedCaptionWidget";
+
+ private static final Rect mTextBounds = new Rect();
+ private static final String mDummyText = "1234567890123456789012345678901234";
+ private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT;
+
+ /** Captioning manager, used to obtain and track caption properties. */
+ private final CaptioningManager mManager;
+
+ /** Callback for rendering changes. */
+ private OnChangedListener mListener;
+
+ /** Current caption style. */
+ private CaptionStyle mCaptionStyle;
+
+ /* Closed caption layout. */
+ private CCLayout mClosedCaptionLayout;
+
+ /** Whether a caption style change listener is registered. */
+ private boolean mHasChangeListener;
+
+ public ClosedCaptionWidget(Context context) {
+ this(context, null);
+ }
+
+ public ClosedCaptionWidget(Context context, AttributeSet attrs) {
+ this(context, null, 0);
+ }
+
+ public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // Cannot render text over video when layer type is hardware.
+ setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+
+ mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
+ mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(mManager.getUserStyle());
+
+ mClosedCaptionLayout = new CCLayout(context);
+ mClosedCaptionLayout.setCaptionStyle(mCaptionStyle);
+ addView(mClosedCaptionLayout, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+
+ requestLayout();
+ }
+
+ @Override
+ public void setOnChangedListener(OnChangedListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void setSize(int width, int height) {
+ final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
+ final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+
+ measure(widthSpec, heightSpec);
+ layout(0, 0, width, height);
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ if (visible) {
+ setVisibility(View.VISIBLE);
+ } else {
+ setVisibility(View.GONE);
+ }
+
+ manageChangeListener();
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ manageChangeListener();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ manageChangeListener();
+ }
+
+ @Override
+ public void onDisplayChanged(SpannableStringBuilder[] styledTexts) {
+ mClosedCaptionLayout.update(styledTexts);
+
+ if (mListener != null) {
+ mListener.onChanged(this);
+ }
+ }
+
+ @Override
+ public CaptionStyle getCaptionStyle() {
+ return mCaptionStyle;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mClosedCaptionLayout.measure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mClosedCaptionLayout.layout(l, t, r, b);
+ }
+
+ /**
+ * Manages whether this renderer is listening for caption style changes.
+ */
+ private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() {
+ @Override
+ public void onUserStyleChanged(CaptionStyle userStyle) {
+ mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(userStyle);
+ mClosedCaptionLayout.setCaptionStyle(mCaptionStyle);
+ }
+ };
+
+ private void manageChangeListener() {
+ final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE;
+ if (mHasChangeListener != needsListener) {
+ mHasChangeListener = needsListener;
+
+ if (needsListener) {
+ mManager.addCaptioningChangeListener(mCaptioningListener);
+ } else {
+ mManager.removeCaptioningChangeListener(mCaptioningListener);
+ }
+ }
+ }
+
+ private static class CCLineBox extends TextView {
+ private static final float FONT_PADDING_RATIO = 0.75f;
+ private static final float EDGE_OUTLINE_RATIO = 0.1f;
+ private static final float EDGE_SHADOW_RATIO = 0.05f;
+ private float mOutlineWidth;
+ private float mShadowRadius;
+ private float mShadowOffset;
+
+ private int mTextColor = Color.WHITE;
+ private int mBgColor = Color.BLACK;
+ private int mEdgeType = CaptionStyle.EDGE_TYPE_NONE;
+ private int mEdgeColor = Color.TRANSPARENT;
+
+ CCLineBox(Context context) {
+ super(context);
+ setGravity(Gravity.CENTER);
+ setBackgroundColor(Color.TRANSPARENT);
+ setTextColor(Color.WHITE);
+ setTypeface(Typeface.MONOSPACE);
+ setVisibility(View.INVISIBLE);
+
+ final Resources res = getContext().getResources();
+
+ // get the default (will be updated later during measure)
+ mOutlineWidth = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.subtitle_outline_width);
+ mShadowRadius = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.subtitle_shadow_radius);
+ mShadowOffset = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.subtitle_shadow_offset);
+ }
+
+ void setCaptionStyle(CaptionStyle captionStyle) {
+ mTextColor = captionStyle.foregroundColor;
+ mBgColor = captionStyle.backgroundColor;
+ mEdgeType = captionStyle.edgeType;
+ mEdgeColor = captionStyle.edgeColor;
+
+ setTextColor(mTextColor);
+ if (mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
+ setShadowLayer(mShadowRadius, mShadowOffset, mShadowOffset, mEdgeColor);
+ } else {
+ setShadowLayer(0, 0, 0, 0);
+ }
+ invalidate();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ float fontSize = MeasureSpec.getSize(heightMeasureSpec)
+ * FONT_PADDING_RATIO;
+ setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
+
+ mOutlineWidth = EDGE_OUTLINE_RATIO * fontSize + 1.0f;
+ mShadowRadius = EDGE_SHADOW_RATIO * fontSize + 1.0f;;
+ mShadowOffset = mShadowRadius;
+
+ // set font scale in the X direction to match the required width
+ setScaleX(1.0f);
+ getPaint().getTextBounds(mDummyText, 0, mDummyText.length(), mTextBounds);
+ float actualTextWidth = mTextBounds.width();
+ float requiredTextWidth = MeasureSpec.getSize(widthMeasureSpec);
+ setScaleX(requiredTextWidth / actualTextWidth);
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected void onDraw(Canvas c) {
+ if (mEdgeType == CaptionStyle.EDGE_TYPE_UNSPECIFIED
+ || mEdgeType == CaptionStyle.EDGE_TYPE_NONE
+ || mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) {
+ // these edge styles don't require a second pass
+ super.onDraw(c);
+ return;
+ }
+
+ if (mEdgeType == CaptionStyle.EDGE_TYPE_OUTLINE) {
+ drawEdgeOutline(c);
+ } else {
+ // Raised or depressed
+ drawEdgeRaisedOrDepressed(c);
+ }
+ }
+
+ private void drawEdgeOutline(Canvas c) {
+ TextPaint textPaint = getPaint();
+
+ Paint.Style previousStyle = textPaint.getStyle();
+ Paint.Join previousJoin = textPaint.getStrokeJoin();
+ float previousWidth = textPaint.getStrokeWidth();
+
+ setTextColor(mEdgeColor);
+ textPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+ textPaint.setStrokeJoin(Paint.Join.ROUND);
+ textPaint.setStrokeWidth(mOutlineWidth);
+
+ // Draw outline and background only.
+ super.onDraw(c);
+
+ // Restore original settings.
+ setTextColor(mTextColor);
+ textPaint.setStyle(previousStyle);
+ textPaint.setStrokeJoin(previousJoin);
+ textPaint.setStrokeWidth(previousWidth);
+
+ // Remove the background.
+ setBackgroundSpans(Color.TRANSPARENT);
+ // Draw foreground only.
+ super.onDraw(c);
+ // Restore the background.
+ setBackgroundSpans(mBgColor);
+ }
+
+ private void drawEdgeRaisedOrDepressed(Canvas c) {
+ TextPaint textPaint = getPaint();
+
+ Paint.Style previousStyle = textPaint.getStyle();
+ textPaint.setStyle(Paint.Style.FILL);
+
+ final boolean raised = mEdgeType == CaptionStyle.EDGE_TYPE_RAISED;
+ final int colorUp = raised ? Color.WHITE : mEdgeColor;
+ final int colorDown = raised ? mEdgeColor : Color.WHITE;
+ final float offset = mShadowRadius / 2f;
+
+ // Draw background and text with shadow up
+ setShadowLayer(mShadowRadius, -offset, -offset, colorUp);
+ super.onDraw(c);
+
+ // Remove the background.
+ setBackgroundSpans(Color.TRANSPARENT);
+
+ // Draw text with shadow down
+ setShadowLayer(mShadowRadius, +offset, +offset, colorDown);
+ super.onDraw(c);
+
+ // Restore settings
+ textPaint.setStyle(previousStyle);
+
+ // Restore the background.
+ setBackgroundSpans(mBgColor);
+ }
+
+ private void setBackgroundSpans(int color) {
+ CharSequence text = getText();
+ if (text instanceof Spannable) {
+ Spannable spannable = (Spannable) text;
+ MutableBackgroundColorSpan[] bgSpans = spannable.getSpans(
+ 0, spannable.length(), MutableBackgroundColorSpan.class);
+ for (int i = 0; i < bgSpans.length; i++) {
+ bgSpans[i].setBackgroundColor(color);
+ }
+ }
+ }
+ }
+
+ private static class CCLayout extends LinearLayout {
+ private static final int MAX_ROWS = CCParser.MAX_ROWS;
+ private static final float SAFE_AREA_RATIO = 0.9f;
+
+ private final CCLineBox[] mLineBoxes = new CCLineBox[MAX_ROWS];
+
+ CCLayout(Context context) {
+ super(context);
+ setGravity(Gravity.START);
+ setOrientation(LinearLayout.VERTICAL);
+ for (int i = 0; i < MAX_ROWS; i++) {
+ mLineBoxes[i] = new CCLineBox(getContext());
+ addView(mLineBoxes[i], LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ }
+ }
+
+ void setCaptionStyle(CaptionStyle captionStyle) {
+ for (int i = 0; i < MAX_ROWS; i++) {
+ mLineBoxes[i].setCaptionStyle(captionStyle);
+ }
+ }
+
+ void update(SpannableStringBuilder[] textBuffer) {
+ for (int i = 0; i < MAX_ROWS; i++) {
+ if (textBuffer[i] != null) {
+ mLineBoxes[i].setText(textBuffer[i], TextView.BufferType.SPANNABLE);
+ mLineBoxes[i].setVisibility(View.VISIBLE);
+ } else {
+ mLineBoxes[i].setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ int safeWidth = getMeasuredWidth();
+ int safeHeight = getMeasuredHeight();
+
+ // CEA-608 assumes 4:3 video
+ if (safeWidth * 3 >= safeHeight * 4) {
+ safeWidth = safeHeight * 4 / 3;
+ } else {
+ safeHeight = safeWidth * 3 / 4;
+ }
+ safeWidth *= SAFE_AREA_RATIO;
+ safeHeight *= SAFE_AREA_RATIO;
+
+ int lineHeight = safeHeight / MAX_ROWS;
+ int lineHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ lineHeight, MeasureSpec.EXACTLY);
+ int lineWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ safeWidth, MeasureSpec.EXACTLY);
+
+ for (int i = 0; i < MAX_ROWS; i++) {
+ mLineBoxes[i].measure(lineWidthMeasureSpec, lineHeightMeasureSpec);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // safe caption area
+ int viewPortWidth = r - l;
+ int viewPortHeight = b - t;
+ int safeWidth, safeHeight;
+ // CEA-608 assumes 4:3 video
+ if (viewPortWidth * 3 >= viewPortHeight * 4) {
+ safeWidth = viewPortHeight * 4 / 3;
+ safeHeight = viewPortHeight;
+ } else {
+ safeWidth = viewPortWidth;
+ safeHeight = viewPortWidth * 3 / 4;
+ }
+ safeWidth *= SAFE_AREA_RATIO;
+ safeHeight *= SAFE_AREA_RATIO;
+ int left = (viewPortWidth - safeWidth) / 2;
+ int top = (viewPortHeight - safeHeight) / 2;
+
+ for (int i = 0; i < MAX_ROWS; i++) {
+ mLineBoxes[i].layout(
+ left,
+ top + safeHeight * i / MAX_ROWS,
+ left + safeWidth,
+ top + safeHeight * (i + 1) / MAX_ROWS);
+ }
+ }
+ }
+};
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index ba3cfb6..c29e967 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -78,6 +78,8 @@ interface IAudioService {
int getLastAudibleMasterVolume();
+ void setMicrophoneMute(boolean on, String callingPackage);
+
void setRingerMode(int ringerMode);
int getRingerMode();
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 1b92410..e25714a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1649,8 +1649,8 @@ public class MediaPlayer implements SubtitleController.Listener
mFormat = MediaFormat.createSubtitleFormat(
MEDIA_MIMETYPE_TEXT_SUBRIP, language);
} else if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- mFormat = MediaFormat.createSubtitleFormat(
- MEDIA_MIMETYPE_TEXT_VTT, language);
+ String mime = in.readString();
+ mFormat = MediaFormat.createSubtitleFormat(mime, language);
mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt());
mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt());
mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt());
@@ -1683,12 +1683,40 @@ public class MediaPlayer implements SubtitleController.Listener
dest.writeString(getLanguage());
if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
+ dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE));
}
}
+ @Override
+ public String toString() {
+ StringBuilder out = new StringBuilder(128);
+ out.append(getClass().getName());
+ out.append('{');
+ switch (mTrackType) {
+ case MEDIA_TRACK_TYPE_VIDEO:
+ out.append("VIDEO");
+ break;
+ case MEDIA_TRACK_TYPE_AUDIO:
+ out.append("AUDIO");
+ break;
+ case MEDIA_TRACK_TYPE_TIMEDTEXT:
+ out.append("TIMEDTEXT");
+ break;
+ case MEDIA_TRACK_TYPE_SUBTITLE:
+ out.append("SUBTITLE");
+ break;
+ default:
+ out.append("UNKNOWN");
+ break;
+ }
+ out.append(", " + mFormat.toString());
+ out.append("}");
+ return out.toString();
+ }
+
/**
* Used to read a TrackInfo from a Parcel.
*/
@@ -1757,6 +1785,12 @@ public class MediaPlayer implements SubtitleController.Listener
*/
public static final String MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
+ /**
+ * MIME type for CEA-608 closed caption data.
+ * @hide
+ */
+ public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608";
+
/*
* A helper function to check if the mime type is supported by media framework.
*/
@@ -1792,16 +1826,11 @@ public class MediaPlayer implements SubtitleController.Listener
}
SubtitleTrack track = mInbandSubtitleTracks[index];
if (track != null) {
- try {
- long runID = data.getStartTimeUs() + 1;
- // TODO: move conversion into track
- track.onData(new String(data.getData(), "UTF-8"), true /* eos */, runID);
- track.setRunDiscardTimeMs(
- runID,
- (data.getStartTimeUs() + data.getDurationUs()) / 1000);
- } catch (java.io.UnsupportedEncodingException e) {
- Log.w(TAG, "subtitle data for track " + index + " is not UTF-8 encoded: " + e);
- }
+ long runID = data.getStartTimeUs() + 1;
+ track.onData(data.getData(), true /* eos */, runID);
+ track.setRunDiscardTimeMs(
+ runID,
+ (data.getStartTimeUs() + data.getDurationUs()) / 1000);
}
}
};
@@ -1872,7 +1901,7 @@ public class MediaPlayer implements SubtitleController.Listener
}
scanner.close();
mOutOfBandSubtitleTracks.add(track);
- track.onData(contents, true /* eos */, ~0 /* runID: keep forever */);
+ track.onData(contents.getBytes(), true /* eos */, ~0 /* runID: keep forever */);
return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 1da0215..ddd5b72 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -60,7 +60,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class MediaRouter {
private static final String TAG = "MediaRouter";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean USE_SESSIONS = true;
static class Static implements DisplayManager.DisplayListener {
final Context mAppContext;
@@ -2104,11 +2103,7 @@ public class MediaRouter {
public void setPlaybackType(int type) {
if (mPlaybackType != type) {
mPlaybackType = type;
- if (USE_SESSIONS) {
- configureSessionVolume();
- } else {
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type);
- }
+ configureSessionVolume();
}
}
@@ -2121,12 +2116,7 @@ public class MediaRouter {
public void setVolumeHandling(int volumeHandling) {
if (mVolumeHandling != volumeHandling) {
mVolumeHandling = volumeHandling;
- if (USE_SESSIONS) {
- configureSessionVolume();
- } else {
- setPlaybackInfoOnRcc(
- RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling);
- }
+ configureSessionVolume();
}
}
@@ -2139,12 +2129,8 @@ public class MediaRouter {
volume = Math.max(0, Math.min(volume, getVolumeMax()));
if (mVolume != volume) {
mVolume = volume;
- if (USE_SESSIONS) {
- if (mSvp != null) {
- mSvp.notifyVolumeChanged();
- }
- } else {
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume);
+ if (mSvp != null) {
+ mSvp.notifyVolumeChanged();
}
dispatchRouteVolumeChanged(this);
if (mGroup != null) {
@@ -2184,11 +2170,7 @@ public class MediaRouter {
public void setVolumeMax(int volumeMax) {
if (mVolumeMax != volumeMax) {
mVolumeMax = volumeMax;
- if (USE_SESSIONS) {
- configureSessionVolume();
- } else {
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax);
- }
+ configureSessionVolume();
}
}
@@ -2199,40 +2181,12 @@ public class MediaRouter {
public void setPlaybackStream(int stream) {
if (mPlaybackStream != stream) {
mPlaybackStream = stream;
- if (USE_SESSIONS) {
- configureSessionVolume();
- } else {
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream);
- }
+ configureSessionVolume();
}
}
private void updatePlaybackInfoOnRcc() {
- if (USE_SESSIONS) {
- configureSessionVolume();
- } else {
- if ((mRcc != null)
- && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) {
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType);
- // let AudioService know whom to call when remote volume
- // needs to be updated
- try {
- sStatic.mAudioService.registerRemoteVolumeObserverForRcc(
- mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */);
- } catch (RemoteException e) {
- Log.e(TAG, "Error registering remote volume observer", e);
- }
- }
- }
+ configureSessionVolume();
}
private void configureSessionVolume() {
@@ -2272,12 +2226,6 @@ public class MediaRouter {
}
}
- private void setPlaybackInfoOnRcc(int what, int value) {
- if (mRcc != null) {
- mRcc.setPlaybackInformation(what, value);
- }
- }
-
class SessionVolumeProvider extends RemoteVolumeProvider {
public SessionVolumeProvider(int volumeControl, int maxVolume) {
diff --git a/media/java/android/media/PlayerRecord.java b/media/java/android/media/PlayerRecord.java
index f9708c3..664ddcf 100644
--- a/media/java/android/media/PlayerRecord.java
+++ b/media/java/android/media/PlayerRecord.java
@@ -56,7 +56,7 @@ class PlayerRecord implements DeathRecipient {
*/
final private ComponentName mReceiverComponent;
- private int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+ private int mRccId = -1;
/**
* A non-null token implies this record tracks a "live" player whose death is being monitored.
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 0caea5f..73bc61a 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -561,6 +561,8 @@ public class RemoteControlClient
return;
}
synchronized (mCacheLock) {
+ // Still build the old metadata so when creating a new editor
+ // you get the expected values.
// assign the edited data
mMetadata = new Bundle(mEditorMetadata);
// add the information about editable keys
@@ -570,16 +572,6 @@ public class RemoteControlClient
}
mOriginalArtwork = mEditorArtwork;
mEditorArtwork = null;
- if (mMetadataChanged & mArtworkChanged) {
- // send to remote control display if conditions are met
- sendMetadataWithArtwork_syncCacheLock(null, 0, 0);
- } else if (mMetadataChanged) {
- // send to remote control display if conditions are met
- sendMetadata_syncCacheLock(null);
- } else if (mArtworkChanged) {
- // send to remote control display if conditions are met
- sendArtwork_syncCacheLock(null, 0, 0);
- }
// USE_SESSIONS
if (mSession != null && mMetadataBuilder != null) {
@@ -687,14 +679,6 @@ public class RemoteControlClient
// keep track of when the state change occurred
mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime();
- // send to remote control display if conditions are met
- sendPlaybackState_syncCacheLock(null);
- // update AudioService
- sendAudioServiceNewPlaybackState_syncCacheLock();
-
- // handle automatic playback position refreshes
- initiateCheckForDrift_syncCacheLock();
-
// USE_SESSIONS
if (mSession != null) {
int pbState = PlaybackState.getStateFromRccState(state);
@@ -707,29 +691,7 @@ public class RemoteControlClient
}
}
- private void initiateCheckForDrift_syncCacheLock() {
- if (mEventHandler == null) {
- return;
- }
- mEventHandler.removeMessages(MSG_POSITION_DRIFT_CHECK);
- if (!mNeedsPositionSync) {
- return;
- }
- if (mPlaybackPositionMs < 0) {
- // the current playback state has no known playback position, it's no use
- // trying to see if there is any drift at this point
- // (this also bypasses this mechanism for older apps that use the old
- // setPlaybackState(int) API)
- return;
- }
- if (playbackPositionShouldMove(mPlaybackState)) {
- // playback position moving, schedule next position drift check
- mEventHandler.sendMessageDelayed(
- mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
- getCheckPeriodFromSpeed(mPlaybackSpeed));
- }
- }
-
+ // TODO investigate if we still need position drift checking
private void onPositionDriftCheck() {
if (DEBUG) { Log.d(TAG, "onPositionDriftCheck()"); }
synchronized(mCacheLock) {
@@ -781,9 +743,6 @@ public class RemoteControlClient
// store locally
mTransportControlFlags = transportControlFlags;
- // send to remote control display if conditions are met
- sendTransportControlInfo_syncCacheLock(null);
-
// USE_SESSIONS
if (mSession != null) {
mSessionPlaybackState.setActions(PlaybackState
@@ -866,17 +825,7 @@ public class RemoteControlClient
*/
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) {
synchronized(mCacheLock) {
- int oldCapa = mPlaybackPositionCapabilities;
- if (l != null) {
- mPlaybackPositionCapabilities |= MEDIA_POSITION_WRITABLE;
- } else {
- mPlaybackPositionCapabilities &= ~MEDIA_POSITION_WRITABLE;
- }
mPositionUpdateListener = l;
- if (oldCapa != mPlaybackPositionCapabilities) {
- // tell RCDs that this RCC's playback position capabilities have changed
- sendTransportControlInfo_syncCacheLock(null);
- }
}
}
@@ -888,17 +837,7 @@ public class RemoteControlClient
*/
public void setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l) {
synchronized(mCacheLock) {
- int oldCapa = mPlaybackPositionCapabilities;
- if (l != null) {
- mPlaybackPositionCapabilities |= MEDIA_POSITION_READABLE;
- } else {
- mPlaybackPositionCapabilities &= ~MEDIA_POSITION_READABLE;
- }
mPositionProvider = l;
- if (oldCapa != mPlaybackPositionCapabilities) {
- // tell RCDs that this RCC's playback position capabilities have changed
- sendTransportControlInfo_syncCacheLock(null);
- }
if ((mPositionProvider != null) && (mEventHandler != null)
&& playbackPositionShouldMove(mPlaybackState)) {
// playback position is already moving, but now we have a position provider,
@@ -925,124 +864,12 @@ public class RemoteControlClient
*/
public static int MEDIA_POSITION_WRITABLE = 1 << 1;
- private int mPlaybackPositionCapabilities = 0;
-
/** @hide */
public final static int DEFAULT_PLAYBACK_VOLUME_HANDLING = PLAYBACK_VOLUME_VARIABLE;
/** @hide */
// hard-coded to the same number of steps as AudioService.MAX_STREAM_VOLUME[STREAM_MUSIC]
public final static int DEFAULT_PLAYBACK_VOLUME = 15;
- private int mPlaybackType = PLAYBACK_TYPE_LOCAL;
- private int mPlaybackVolumeMax = DEFAULT_PLAYBACK_VOLUME;
- private int mPlaybackVolume = DEFAULT_PLAYBACK_VOLUME;
- private int mPlaybackVolumeHandling = DEFAULT_PLAYBACK_VOLUME_HANDLING;
- private int mPlaybackStream = AudioManager.STREAM_MUSIC;
-
- /**
- * @hide
- * Set information describing information related to the playback of media so the system
- * can implement additional behavior to handle non-local playback usecases.
- * @param what a key to specify the type of information to set. Valid keys are
- * {@link #PLAYBACKINFO_PLAYBACK_TYPE},
- * {@link #PLAYBACKINFO_USES_STREAM},
- * {@link #PLAYBACKINFO_VOLUME},
- * {@link #PLAYBACKINFO_VOLUME_MAX},
- * and {@link #PLAYBACKINFO_VOLUME_HANDLING}.
- * @param value the value for the supplied information to set.
- */
- public void setPlaybackInformation(int what, int value) {
- synchronized(mCacheLock) {
- switch (what) {
- case PLAYBACKINFO_PLAYBACK_TYPE:
- if ((value >= PLAYBACK_TYPE_MIN) && (value <= PLAYBACK_TYPE_MAX)) {
- if (mPlaybackType != value) {
- mPlaybackType = value;
- sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
- }
- } else {
- Log.w(TAG, "using invalid value for PLAYBACKINFO_PLAYBACK_TYPE");
- }
- break;
- case PLAYBACKINFO_VOLUME:
- if ((value > -1) && (value <= mPlaybackVolumeMax)) {
- if (mPlaybackVolume != value) {
- mPlaybackVolume = value;
- sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
- }
- } else {
- Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME");
- }
- break;
- case PLAYBACKINFO_VOLUME_MAX:
- if (value > 0) {
- if (mPlaybackVolumeMax != value) {
- mPlaybackVolumeMax = value;
- sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
- }
- } else {
- Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME_MAX");
- }
- break;
- case PLAYBACKINFO_USES_STREAM:
- if ((value >= 0) && (value < AudioSystem.getNumStreamTypes())) {
- mPlaybackStream = value;
- } else {
- Log.w(TAG, "using invalid value for PLAYBACKINFO_USES_STREAM");
- }
- break;
- case PLAYBACKINFO_VOLUME_HANDLING:
- if ((value >= PLAYBACK_VOLUME_FIXED) && (value <= PLAYBACK_VOLUME_VARIABLE)) {
- if (mPlaybackVolumeHandling != value) {
- mPlaybackVolumeHandling = value;
- sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
- }
- } else {
- Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME_HANDLING");
- }
- break;
- default:
- // not throwing an exception or returning an error if more keys are to be
- // supported in the future
- Log.w(TAG, "setPlaybackInformation() ignoring unknown key " + what);
- break;
- }
- }
- }
-
- /**
- * @hide
- * Return playback information represented as an integer value.
- * @param what a key to specify the type of information to retrieve. Valid keys are
- * {@link #PLAYBACKINFO_PLAYBACK_TYPE},
- * {@link #PLAYBACKINFO_USES_STREAM},
- * {@link #PLAYBACKINFO_VOLUME},
- * {@link #PLAYBACKINFO_VOLUME_MAX},
- * and {@link #PLAYBACKINFO_VOLUME_HANDLING}.
- * @return the current value for the given information type, or
- * {@link #PLAYBACKINFO_INVALID_VALUE} if an error occurred or the request is invalid, or
- * the value is unknown.
- */
- public int getIntPlaybackInformation(int what) {
- synchronized(mCacheLock) {
- switch (what) {
- case PLAYBACKINFO_PLAYBACK_TYPE:
- return mPlaybackType;
- case PLAYBACKINFO_VOLUME:
- return mPlaybackVolume;
- case PLAYBACKINFO_VOLUME_MAX:
- return mPlaybackVolumeMax;
- case PLAYBACKINFO_USES_STREAM:
- return mPlaybackStream;
- case PLAYBACKINFO_VOLUME_HANDLING:
- return mPlaybackVolumeHandling;
- default:
- Log.e(TAG, "getIntPlaybackInformation() unknown key " + what);
- return PLAYBACKINFO_INVALID_VALUE;
- }
- }
- }
-
/**
* Lock for all cached data
*/
@@ -1102,13 +929,6 @@ public class RemoteControlClient
* The current remote control client generation ID across the system, as known by this object
*/
private int mCurrentClientGenId = -1;
- /**
- * The remote control client generation ID, the last time it was told it was the current RC.
- * If (mCurrentClientGenId == mInternalClientGenId) is true, it means that this remote control
- * client is the "focused" one, and that whenever this client's info is updated, it needs to
- * send it to the known IRemoteControlDisplay interfaces.
- */
- private int mInternalClientGenId = -2;
/**
* The media button intent description associated with this remote control client
@@ -1134,186 +954,18 @@ public class RemoteControlClient
private MediaMetadata mMediaMetadata;
/**
- * A class to encapsulate all the information about a remote control display.
- * A RemoteControlClient's metadata and state may be displayed on multiple IRemoteControlDisplay
- */
- private class DisplayInfoForClient {
- /** may never be null */
- private IRemoteControlDisplay mRcDisplay;
- private int mArtworkExpectedWidth;
- private int mArtworkExpectedHeight;
- private boolean mWantsPositionSync = false;
- private boolean mEnabled = true;
-
- DisplayInfoForClient(IRemoteControlDisplay rcd, int w, int h) {
- mRcDisplay = rcd;
- mArtworkExpectedWidth = w;
- mArtworkExpectedHeight = h;
- }
- }
-
- /**
- * The list of remote control displays to which this client will send information.
- * Accessed and modified synchronized on mCacheLock
- */
- private ArrayList<DisplayInfoForClient> mRcDisplays = new ArrayList<DisplayInfoForClient>(1);
-
- /**
* @hide
* Accessor to media button intent description (includes target component)
*/
public PendingIntent getRcMediaIntent() {
return mRcMediaIntent;
}
- /**
- * @hide
- * Accessor to IRemoteControlClient
- */
- public IRemoteControlClient getIRemoteControlClient() {
- return mIRCC;
- }
-
- /**
- * The IRemoteControlClient implementation
- */
- private final IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() {
-
- //TODO change name to informationRequestForAllDisplays()
- public void onInformationRequested(int generationId, int infoFlags) {
- // only post messages, we can't block here
- if (mEventHandler != null) {
- // signal new client
- mEventHandler.removeMessages(MSG_NEW_INTERNAL_CLIENT_GEN);
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_NEW_INTERNAL_CLIENT_GEN,
- /*arg1*/ generationId, /*arg2, ignored*/ 0));
- // send the information
- mEventHandler.removeMessages(MSG_REQUEST_PLAYBACK_STATE);
- mEventHandler.removeMessages(MSG_REQUEST_METADATA);
- mEventHandler.removeMessages(MSG_REQUEST_TRANSPORTCONTROL);
- mEventHandler.removeMessages(MSG_REQUEST_ARTWORK);
- mEventHandler.removeMessages(MSG_REQUEST_METADATA_ARTWORK);
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE, null));
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL, null));
- mEventHandler.sendMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA_ARTWORK,
- 0, 0, null));
- }
- }
-
- public void informationRequestForDisplay(IRemoteControlDisplay rcd, int w, int h) {
- // only post messages, we can't block here
- if (mEventHandler != null) {
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL, rcd));
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE, rcd));
- if ((w > 0) && (h > 0)) {
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_METADATA_ARTWORK, w, h, rcd));
- } else {
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_REQUEST_METADATA, rcd));
- }
- }
- }
-
- public void setCurrentClientGenerationId(int clientGeneration) {
- // only post messages, we can't block here
- if (mEventHandler != null) {
- mEventHandler.removeMessages(MSG_NEW_CURRENT_CLIENT_GEN);
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_NEW_CURRENT_CLIENT_GEN, clientGeneration, 0/*ignored*/));
- }
- }
-
- public void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
- // only post messages, we can't block here
- if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_PLUG_DISPLAY, w, h, rcd));
- }
- }
-
- public void unplugRemoteControlDisplay(IRemoteControlDisplay rcd) {
- // only post messages, we can't block here
- if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_UNPLUG_DISPLAY, rcd));
- }
- }
-
- public void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h) {
- // only post messages, we can't block here
- if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_UPDATE_DISPLAY_ARTWORK_SIZE, w, h, rcd));
- }
- }
-
- public void setWantsSyncForDisplay(IRemoteControlDisplay rcd, boolean wantsSync) {
- // only post messages, we can't block here
- if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_DISPLAY_WANTS_POS_SYNC, wantsSync ? 1 : 0, 0/*arg2 ignored*/, rcd));
- }
- }
-
- public void enableRemoteControlDisplay(IRemoteControlDisplay rcd, boolean enabled) {
- // only post messages, we can't block here
- if ((mEventHandler != null) && (rcd != null)) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_DISPLAY_ENABLE, enabled ? 1 : 0, 0/*arg2 ignored*/, rcd));
- }
- }
-
- public void seekTo(int generationId, long timeMs) {
- // only post messages, we can't block here
- if (mEventHandler != null) {
- mEventHandler.removeMessages(MSG_SEEK_TO);
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_SEEK_TO, generationId /* arg1 */, 0 /* arg2, ignored */,
- new Long(timeMs)));
- }
- }
-
- public void updateMetadata(int generationId, int key, Rating value) {
- // only post messages, we can't block here
- if (mEventHandler != null) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_UPDATE_METADATA, generationId /* arg1 */, key /* arg2*/, value));
- }
- }
- };
/**
* @hide
* Default value for the unique identifier
*/
public final static int RCSE_ID_UNREGISTERED = -1;
- /**
- * Unique identifier of the RemoteControlStackEntry in AudioService with which
- * this RemoteControlClient is associated.
- */
- private int mRcseId = RCSE_ID_UNREGISTERED;
- /**
- * @hide
- * To be only used by AudioManager after it has received the unique id from
- * IAudioService.registerRemoteControlClient()
- * @param id the unique identifier of the RemoteControlStackEntry in AudioService with which
- * this RemoteControlClient is associated.
- */
- public void setRcseId(int id) {
- mRcseId = id;
- }
-
- /**
- * @hide
- */
- public int getRcseId() {
- return mRcseId;
- }
// USE_SESSIONS
private MediaSession.TransportControlsCallback mTransportListener
@@ -1327,31 +979,13 @@ public class RemoteControlClient
@Override
public void onSetRating(Rating rating) {
if ((mTransportControlFlags & FLAG_KEY_MEDIA_RATING) != 0) {
- if (mEventHandler != null) {
- mEventHandler.sendMessage(mEventHandler.obtainMessage(
- MSG_UPDATE_METADATA, mCurrentClientGenId,
- MetadataEditor.RATING_KEY_BY_USER, rating));
- }
+ onUpdateMetadata(mCurrentClientGenId, MetadataEditor.RATING_KEY_BY_USER, rating);
}
}
};
private EventHandler mEventHandler;
- private final static int MSG_REQUEST_PLAYBACK_STATE = 1;
- private final static int MSG_REQUEST_METADATA = 2;
- private final static int MSG_REQUEST_TRANSPORTCONTROL = 3;
- private final static int MSG_REQUEST_ARTWORK = 4;
- private final static int MSG_NEW_INTERNAL_CLIENT_GEN = 5;
- private final static int MSG_NEW_CURRENT_CLIENT_GEN = 6;
- private final static int MSG_PLUG_DISPLAY = 7;
- private final static int MSG_UNPLUG_DISPLAY = 8;
- private final static int MSG_UPDATE_DISPLAY_ARTWORK_SIZE = 9;
- private final static int MSG_SEEK_TO = 10;
private final static int MSG_POSITION_DRIFT_CHECK = 11;
- private final static int MSG_DISPLAY_WANTS_POS_SYNC = 12;
- private final static int MSG_UPDATE_METADATA = 13;
- private final static int MSG_REQUEST_METADATA_ARTWORK = 14;
- private final static int MSG_DISPLAY_ENABLE = 15;
private class EventHandler extends Handler {
public EventHandler(RemoteControlClient rcc, Looper looper) {
@@ -1361,63 +995,9 @@ public class RemoteControlClient
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
- case MSG_REQUEST_PLAYBACK_STATE:
- synchronized (mCacheLock) {
- sendPlaybackState_syncCacheLock((IRemoteControlDisplay)msg.obj);
- }
- break;
- case MSG_REQUEST_METADATA:
- synchronized (mCacheLock) {
- sendMetadata_syncCacheLock((IRemoteControlDisplay)msg.obj);
- }
- break;
- case MSG_REQUEST_TRANSPORTCONTROL:
- synchronized (mCacheLock) {
- sendTransportControlInfo_syncCacheLock((IRemoteControlDisplay)msg.obj);
- }
- break;
- case MSG_REQUEST_ARTWORK:
- synchronized (mCacheLock) {
- sendArtwork_syncCacheLock((IRemoteControlDisplay)msg.obj,
- msg.arg1, msg.arg2);
- }
- break;
- case MSG_REQUEST_METADATA_ARTWORK:
- synchronized (mCacheLock) {
- sendMetadataWithArtwork_syncCacheLock((IRemoteControlDisplay)msg.obj,
- msg.arg1, msg.arg2);
- }
- break;
- case MSG_NEW_INTERNAL_CLIENT_GEN:
- onNewInternalClientGen(msg.arg1);
- break;
- case MSG_NEW_CURRENT_CLIENT_GEN:
- onNewCurrentClientGen(msg.arg1);
- break;
- case MSG_PLUG_DISPLAY:
- onPlugDisplay((IRemoteControlDisplay)msg.obj, msg.arg1, msg.arg2);
- break;
- case MSG_UNPLUG_DISPLAY:
- onUnplugDisplay((IRemoteControlDisplay)msg.obj);
- break;
- case MSG_UPDATE_DISPLAY_ARTWORK_SIZE:
- onUpdateDisplayArtworkSize((IRemoteControlDisplay)msg.obj, msg.arg1, msg.arg2);
- break;
- case MSG_SEEK_TO:
- onSeekTo(msg.arg1, ((Long)msg.obj).longValue());
- break;
case MSG_POSITION_DRIFT_CHECK:
onPositionDriftCheck();
break;
- case MSG_DISPLAY_WANTS_POS_SYNC:
- onDisplayWantsSync((IRemoteControlDisplay)msg.obj, msg.arg1 == 1);
- break;
- case MSG_UPDATE_METADATA:
- onUpdateMetadata(msg.arg1, msg.arg2, msg.obj);
- break;
- case MSG_DISPLAY_ENABLE:
- onDisplayEnable((IRemoteControlDisplay)msg.obj, msg.arg1 == 1);
- break;
default:
Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
}
@@ -1425,346 +1005,8 @@ public class RemoteControlClient
}
//===========================================================
- // Communication with the IRemoteControlDisplay (the displays known to the system)
-
- private void sendPlaybackState_syncCacheLock(IRemoteControlDisplay target) {
- if (mCurrentClientGenId == mInternalClientGenId) {
- if (target != null) {
- try {
- target.setPlaybackState(mInternalClientGenId,
- mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs,
- mPlaybackSpeed);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setPlaybackState() for dead display " + target, e);
- }
- return;
- }
- // target == null implies all displays must be updated
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mEnabled) {
- try {
- di.mRcDisplay.setPlaybackState(mInternalClientGenId,
- mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs,
- mPlaybackSpeed);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setPlaybackState(), dead display " + di.mRcDisplay, e);
- displayIterator.remove();
- }
- }
- }
- }
- }
-
- private void sendMetadata_syncCacheLock(IRemoteControlDisplay target) {
- if (mCurrentClientGenId == mInternalClientGenId) {
- if (target != null) {
- try {
- target.setMetadata(mInternalClientGenId, mMetadata);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setMetadata() for dead display " + target, e);
- }
- return;
- }
- // target == null implies all displays must be updated
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mEnabled) {
- try {
- di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setMetadata(), dead display " + di.mRcDisplay, e);
- displayIterator.remove();
- }
- }
- }
- }
- }
-
- private void sendTransportControlInfo_syncCacheLock(IRemoteControlDisplay target) {
- if (mCurrentClientGenId == mInternalClientGenId) {
- if (target != null) {
- try {
- target.setTransportControlInfo(mInternalClientGenId,
- mTransportControlFlags, mPlaybackPositionCapabilities);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setTransportControlFlags() for dead display " + target,
- e);
- }
- return;
- }
- // target == null implies all displays must be updated
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mEnabled) {
- try {
- di.mRcDisplay.setTransportControlInfo(mInternalClientGenId,
- mTransportControlFlags, mPlaybackPositionCapabilities);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setTransportControlFlags(), dead display " + di.mRcDisplay,
- e);
- displayIterator.remove();
- }
- }
- }
- }
- }
-
- private void sendArtwork_syncCacheLock(IRemoteControlDisplay target, int w, int h) {
- // FIXME modify to cache all requested sizes?
- if (mCurrentClientGenId == mInternalClientGenId) {
- if (target != null) {
- final DisplayInfoForClient di = new DisplayInfoForClient(target, w, h);
- sendArtworkToDisplay(di);
- return;
- }
- // target == null implies all displays must be updated
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- if (!sendArtworkToDisplay(displayIterator.next())) {
- displayIterator.remove();
- }
- }
- }
- }
-
- /**
- * Send artwork to an IRemoteControlDisplay.
- * @param di encapsulates the IRemoteControlDisplay that will receive the artwork, and its
- * dimension requirements.
- * @return false if there was an error communicating with the IRemoteControlDisplay.
- */
- private boolean sendArtworkToDisplay(DisplayInfoForClient di) {
- if ((di.mArtworkExpectedWidth > 0) && (di.mArtworkExpectedHeight > 0)) {
- Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork,
- di.mArtworkExpectedWidth, di.mArtworkExpectedHeight);
- try {
- di.mRcDisplay.setArtwork(mInternalClientGenId, artwork);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in sendArtworkToDisplay(), dead display " + di.mRcDisplay, e);
- return false;
- }
- }
- return true;
- }
-
- private void sendMetadataWithArtwork_syncCacheLock(IRemoteControlDisplay target, int w, int h) {
- // FIXME modify to cache all requested sizes?
- if (mCurrentClientGenId == mInternalClientGenId) {
- if (target != null) {
- try {
- if ((w > 0) && (h > 0)) {
- Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork, w, h);
- target.setAllMetadata(mInternalClientGenId, mMetadata, artwork);
- } else {
- target.setMetadata(mInternalClientGenId, mMetadata);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error in set(All)Metadata() for dead display " + target, e);
- }
- return;
- }
- // target == null implies all displays must be updated
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- try {
- if (di.mEnabled) {
- if ((di.mArtworkExpectedWidth > 0) && (di.mArtworkExpectedHeight > 0)) {
- Bitmap artwork = scaleBitmapIfTooBig(mOriginalArtwork,
- di.mArtworkExpectedWidth, di.mArtworkExpectedHeight);
- di.mRcDisplay.setAllMetadata(mInternalClientGenId, mMetadata, artwork);
- } else {
- di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata);
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error when setting metadata, dead display " + di.mRcDisplay, e);
- displayIterator.remove();
- }
- }
- }
- }
-
- //===========================================================
- // Communication with AudioService
-
- private static IAudioService sService;
-
- private static IAudioService getService()
- {
- if (sService != null) {
- return sService;
- }
- IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
- sService = IAudioService.Stub.asInterface(b);
- return sService;
- }
-
- private void sendAudioServiceNewPlaybackInfo_syncCacheLock(int what, int value) {
- if (mRcseId == RCSE_ID_UNREGISTERED) {
- return;
- }
- //Log.d(TAG, "sending to AudioService key=" + what + ", value=" + value);
- IAudioService service = getService();
- try {
- service.setPlaybackInfoForRcc(mRcseId, what, value);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setPlaybackInfoForRcc", e);
- }
- }
-
- private void sendAudioServiceNewPlaybackState_syncCacheLock() {
- if (mRcseId == RCSE_ID_UNREGISTERED) {
- return;
- }
- IAudioService service = getService();
- try {
- service.setPlaybackStateForRcc(mRcseId,
- mPlaybackState, mPlaybackPositionMs, mPlaybackSpeed);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setPlaybackStateForRcc", e);
- }
- }
-
- //===========================================================
// Message handlers
- private void onNewInternalClientGen(int clientGeneration) {
- synchronized (mCacheLock) {
- // this remote control client is told it is the "focused" one:
- // it implies that now (mCurrentClientGenId == mInternalClientGenId) is true
- mInternalClientGenId = clientGeneration;
- }
- }
-
- private void onNewCurrentClientGen(int clientGeneration) {
- synchronized (mCacheLock) {
- mCurrentClientGenId = clientGeneration;
- }
- }
-
- /** pre-condition rcd != null */
- private void onPlugDisplay(IRemoteControlDisplay rcd, int w, int h) {
- synchronized(mCacheLock) {
- // do we have this display already?
- boolean displayKnown = false;
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext() && !displayKnown) {
- final DisplayInfoForClient di = displayIterator.next();
- displayKnown = di.mRcDisplay.asBinder().equals(rcd.asBinder());
- if (displayKnown) {
- // this display was known but the change in artwork size will cause the
- // artwork to be refreshed
- if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
- di.mArtworkExpectedWidth = w;
- di.mArtworkExpectedHeight = h;
- if (!sendArtworkToDisplay(di)) {
- displayIterator.remove();
- }
- }
- }
- }
- if (!displayKnown) {
- mRcDisplays.add(new DisplayInfoForClient(rcd, w, h));
- }
- }
- }
-
- /** pre-condition rcd != null */
- private void onUnplugDisplay(IRemoteControlDisplay rcd) {
- synchronized(mCacheLock) {
- Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- displayIterator.remove();
- break;
- }
- }
- // list of RCDs has changed, reevaluate whether position check is still needed
- boolean oldNeedsPositionSync = mNeedsPositionSync;
- boolean newNeedsPositionSync = false;
- displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mWantsPositionSync) {
- newNeedsPositionSync = true;
- break;
- }
- }
- mNeedsPositionSync = newNeedsPositionSync;
- if (oldNeedsPositionSync != mNeedsPositionSync) {
- // update needed?
- initiateCheckForDrift_syncCacheLock();
- }
- }
- }
-
- /** pre-condition rcd != null */
- private void onUpdateDisplayArtworkSize(IRemoteControlDisplay rcd, int w, int h) {
- synchronized(mCacheLock) {
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder()) &&
- ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h))) {
- di.mArtworkExpectedWidth = w;
- di.mArtworkExpectedHeight = h;
- if (di.mEnabled) {
- if (!sendArtworkToDisplay(di)) {
- displayIterator.remove();
- }
- }
- break;
- }
- }
- }
- }
-
- /** pre-condition rcd != null */
- private void onDisplayWantsSync(IRemoteControlDisplay rcd, boolean wantsSync) {
- synchronized(mCacheLock) {
- boolean oldNeedsPositionSync = mNeedsPositionSync;
- boolean newNeedsPositionSync = false;
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- // go through the list of RCDs and for each entry, check both whether this is the RCD
- // that gets upated, and whether the list has one entry that wants position sync
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mEnabled) {
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- di.mWantsPositionSync = wantsSync;
- }
- if (di.mWantsPositionSync) {
- newNeedsPositionSync = true;
- }
- }
- }
- mNeedsPositionSync = newNeedsPositionSync;
- if (oldNeedsPositionSync != mNeedsPositionSync) {
- // update needed?
- initiateCheckForDrift_syncCacheLock();
- }
- }
- }
-
- /** pre-condition rcd != null */
- private void onDisplayEnable(IRemoteControlDisplay rcd, boolean enable) {
- synchronized(mCacheLock) {
- final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForClient di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- di.mEnabled = enable;
- }
- }
- }
- }
-
private void onSeekTo(int generationId, long timeMs) {
synchronized (mCacheLock) {
if ((mCurrentClientGenId == generationId) && (mPositionUpdateListener != null)) {
@@ -1785,42 +1027,6 @@ public class RemoteControlClient
// Internal utilities
/**
- * Scale a bitmap to fit the smallest dimension by uniformly scaling the incoming bitmap.
- * If the bitmap fits, then do nothing and return the original.
- *
- * @param bitmap
- * @param maxWidth
- * @param maxHeight
- * @return
- */
-
- private Bitmap scaleBitmapIfTooBig(Bitmap bitmap, int maxWidth, int maxHeight) {
- if (bitmap != null) {
- final int width = bitmap.getWidth();
- final int height = bitmap.getHeight();
- if (width > maxWidth || height > maxHeight) {
- float scale = Math.min((float) maxWidth / width, (float) maxHeight / height);
- int newWidth = Math.round(scale * width);
- int newHeight = Math.round(scale * height);
- Bitmap.Config newConfig = bitmap.getConfig();
- if (newConfig == null) {
- newConfig = Bitmap.Config.ARGB_8888;
- }
- Bitmap outBitmap = Bitmap.createBitmap(newWidth, newHeight, newConfig);
- Canvas canvas = new Canvas(outBitmap);
- Paint paint = new Paint();
- paint.setAntiAlias(true);
- paint.setFilterBitmap(true);
- canvas.drawBitmap(bitmap, null,
- new RectF(0, 0, outBitmap.getWidth(), outBitmap.getHeight()), paint);
- bitmap = outBitmap;
- }
- }
- return bitmap;
- }
-
-
- /**
* Returns whether, for the given playback state, the playback position is expected to
* be changing.
* @param playstate the playback state to evaluate
diff --git a/media/java/android/media/SubtitleTrack.java b/media/java/android/media/SubtitleTrack.java
index 06063de..b0e182d 100644
--- a/media/java/android/media/SubtitleTrack.java
+++ b/media/java/android/media/SubtitleTrack.java
@@ -83,7 +83,7 @@ public abstract class SubtitleTrack implements MediaTimeProvider.OnMediaTimeList
* indicating the last section of the run. Calls from different
* runs must not be intermixed.
*
- * @param data
+ * @param data subtitle data byte buffer
* @param eos true if this is the last section of the run.
* @param runID mostly-unique ID for this run of data. Subtitle cues
* with runID of 0 are discarded immediately after
@@ -92,10 +92,8 @@ public abstract class SubtitleTrack implements MediaTimeProvider.OnMediaTimeList
* with other runID-s are discarded at the end of the
* run, which defaults to the latest timestamp of
* any of its cues (with this runID).
- *
- * TODO use ByteBuffer
*/
- public abstract void onData(String data, boolean eos, long runID);
+ public abstract void onData(byte[] data, boolean eos, long runID);
/**
* Called when adding the subtitle rendering widget to the view hierarchy,
diff --git a/media/java/android/media/TtmlRenderer.java b/media/java/android/media/TtmlRenderer.java
index 0309334..75133c9 100644
--- a/media/java/android/media/TtmlRenderer.java
+++ b/media/java/android/media/TtmlRenderer.java
@@ -563,28 +563,35 @@ class TtmlTrack extends SubtitleTrack implements TtmlNodeListener {
}
@Override
- public void onData(String data, boolean eos, long runID) {
- // implement intermixing restriction for TTML.
- synchronized(mParser) {
- if (mCurrentRunID != null && runID != mCurrentRunID) {
- throw new IllegalStateException(
- "Run #" + mCurrentRunID +
- " in progress. Cannot process run #" + runID);
- }
- mCurrentRunID = runID;
- mParsingData += data;
- if (eos) {
- try {
- mParser.parse(mParsingData, mCurrentRunID);
- } catch (XmlPullParserException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
+ public void onData(byte[] data, boolean eos, long runID) {
+ try {
+ // TODO: handle UTF-8 conversion properly
+ String str = new String(data, "UTF-8");
+
+ // implement intermixing restriction for TTML.
+ synchronized(mParser) {
+ if (mCurrentRunID != null && runID != mCurrentRunID) {
+ throw new IllegalStateException(
+ "Run #" + mCurrentRunID +
+ " in progress. Cannot process run #" + runID);
+ }
+ mCurrentRunID = runID;
+ mParsingData += str;
+ if (eos) {
+ try {
+ mParser.parse(mParsingData, mCurrentRunID);
+ } catch (XmlPullParserException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ finishedRun(runID);
+ mParsingData = "";
+ mCurrentRunID = null;
}
- finishedRun(runID);
- mParsingData = "";
- mCurrentRunID = null;
}
+ } catch (java.io.UnsupportedEncodingException e) {
+ Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e);
}
}
diff --git a/media/java/android/media/WebVttRenderer.java b/media/java/android/media/WebVttRenderer.java
index 7977988..a9374d5 100644
--- a/media/java/android/media/WebVttRenderer.java
+++ b/media/java/android/media/WebVttRenderer.java
@@ -1001,22 +1001,28 @@ class WebVttTrack extends SubtitleTrack implements WebVttCueListener {
}
@Override
- public void onData(String data, boolean eos, long runID) {
- // implement intermixing restriction for WebVTT only for now
- synchronized(mParser) {
- if (mCurrentRunID != null && runID != mCurrentRunID) {
- throw new IllegalStateException(
- "Run #" + mCurrentRunID +
- " in progress. Cannot process run #" + runID);
- }
- mCurrentRunID = runID;
- mParser.parse(data);
- if (eos) {
- finishedRun(runID);
- mParser.eos();
- mRegions.clear();
- mCurrentRunID = null;
+ public void onData(byte[] data, boolean eos, long runID) {
+ try {
+ String str = new String(data, "UTF-8");
+
+ // implement intermixing restriction for WebVTT only for now
+ synchronized(mParser) {
+ if (mCurrentRunID != null && runID != mCurrentRunID) {
+ throw new IllegalStateException(
+ "Run #" + mCurrentRunID +
+ " in progress. Cannot process run #" + runID);
+ }
+ mCurrentRunID = runID;
+ mParser.parse(str);
+ if (eos) {
+ finishedRun(runID);
+ mParser.eos();
+ mRegions.clear();
+ mCurrentRunID = null;
+ }
}
+ } catch (java.io.UnsupportedEncodingException e) {
+ Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e);
}
}
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index ec87c6e..2ed3d73 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -170,7 +170,7 @@ public class DefaultContainerService extends IntentService {
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
- PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packagePath, 0);
+ PackageParser.ApkLite pkg = PackageParser.parseApkLite(packagePath, 0);
if (pkg == null) {
Slog.w(TAG, "Failed to parse package");
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index 2917faa..006b1ee 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -56,7 +56,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/date_owner_info_margin"
android:layout_gravity="center_horizontal"
- android:textColor="#99ffffff"
+ android:textColor="#ccffffff"
android:textSize="@dimen/widget_label_font_size"
android:ellipsize="marquee"
android:singleLine="true" />
diff --git a/packages/Keyguard/res/values-sw600dp/dimens.xml b/packages/Keyguard/res/values-sw600dp/dimens.xml
index e9e9b89..69bf44f 100644
--- a/packages/Keyguard/res/values-sw600dp/dimens.xml
+++ b/packages/Keyguard/res/values-sw600dp/dimens.xml
@@ -63,14 +63,11 @@
<dimen name="keyguard_muliuser_selector_margin">12dp</dimen>
<!-- Overload default clock widget parameters -->
- <dimen name="widget_big_font_size">96dp</dimen>
+ <dimen name="widget_big_font_size">120dp</dimen>
<dimen name="widget_label_font_size">16sp</dimen>
- <dimen name="bottom_text_spacing_digital">-8dp</dimen>
+ <dimen name="bottom_text_spacing_digital">-16dp</dimen>
<!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
Should be 0 on devices with plenty of room (e.g. tablets) -->
<dimen name="eca_overlap">0dip</dimen>
-
- <!-- The vertical margin between the date and the owner info. -->
- <dimen name="date_owner_info_margin">4dp</dimen>
</resources>
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index f971522..4b113ff 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -155,15 +155,15 @@
<dimen name="eca_overlap">-10dip</dimen>
<!-- Default clock parameters -->
- <dimen name="bottom_text_spacing_digital">-6dp</dimen>
+ <dimen name="bottom_text_spacing_digital">-10dp</dimen>
<dimen name="label_font_size">14dp</dimen>
- <dimen name="widget_label_font_size">14sp</dimen>
- <dimen name="widget_big_font_size">68dp</dimen>
+ <dimen name="widget_label_font_size">16sp</dimen>
+ <dimen name="widget_big_font_size">96dp</dimen>
<dimen name="big_font_size">120dp</dimen>
<!-- The y translation to apply at the start in appear animations. -->
<dimen name="appear_y_translation_start">32dp</dimen>
<!-- The vertical margin between the date and the owner info. -->
- <dimen name="date_owner_info_margin">2dp</dimen>
+ <dimen name="date_owner_info_margin">10dp</dimen>
</resources>
diff --git a/packages/Keyguard/res/values/donottranslate.xml b/packages/Keyguard/res/values/donottranslate.xml
index 16f5a3e..78636db 100644
--- a/packages/Keyguard/res/values/donottranslate.xml
+++ b/packages/Keyguard/res/values/donottranslate.xml
@@ -16,7 +16,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Skeleton string format for displaying the date. -->
- <string name="abbrev_wday_month_day_no_year">EEEMMMMd</string>
+ <string name="abbrev_wday_month_day_no_year">EEEEMMMMd</string>
<!-- Skeleton string format for displaying the time in 12-hour format. -->
<string name="clock_12hr_format">hm</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index a8b49e2..7918755 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -130,11 +130,6 @@ public class KeyguardStatusView extends GridLayout {
}
}
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
private void updateOwnerInfo() {
String ownerInfo = getOwnerInfo();
if (!TextUtils.isEmpty(ownerInfo)) {
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 9bf42b2..db5983b 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -46,7 +46,7 @@
android:id="@+id/keyguard_indication_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginBottom="100dp"
+ android:layout_marginBottom="70dp"
android:layout_gravity="bottom|center_horizontal"
android:textStyle="italic"
android:textColor="#ffffff"
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
new file mode 100644
index 0000000..f9b01c8
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+<resources>
+ <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
+ card. -->
+ <integer name="keyguard_max_notification_count">3</integer>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 326f602..5367fbc 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -23,5 +23,8 @@
<item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.25</item>
<fraction name="keyguard_clock_y_fraction_max">37%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">14%</fraction>
+ <fraction name="keyguard_clock_y_fraction_min">20%</fraction>
+
+ <dimen name="keyguard_clock_notifications_margin_min">36dp</dimen>
+ <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 6dea81f..47581a9 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -28,4 +28,8 @@
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<integer name="quick_settings_user_time_settings_tile_span">1</integer>
+
+ <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
+ card. -->
+ <integer name="keyguard_max_notification_count">5</integer>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 313e2e8..a5e3924 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -16,8 +16,8 @@
*/
-->
<resources>
- <!-- The width of the notification panel window: 446 + 16 + 16 (padding in the bg drawable) -->
- <dimen name="notification_panel_width">478dp</dimen>
+ <!-- The width of the notification panel window: 400 + 16 + 16 (padding in the bg drawable) -->
+ <dimen name="notification_panel_width">432dp</dimen>
<!-- Gravity for the notification panel -->
<!-- 0x31 = top|center_horizontal -->
@@ -56,10 +56,10 @@
max value is used when no notifications are displaying, and the min value is when the
highest possible number of notifications are showing. -->
<fraction name="keyguard_clock_y_fraction_max">34%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">25%</fraction>
+ <fraction name="keyguard_clock_y_fraction_min">24%</fraction>
<!-- The margin between the clock and the notifications on Keyguard. See
keyguard_clock_height_fraction_* for the difference between min and max.-->
- <dimen name="keyguard_clock_notifications_margin_min">36dp</dimen>
- <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
+ <dimen name="keyguard_clock_notifications_margin_min">44dp</dimen>
+ <dimen name="keyguard_clock_notifications_margin_max">44dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5ffe3b3..7ed3e29 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -292,12 +292,12 @@
<!-- The fraction of the screen height where the clock on the Keyguard has its center. The
max value is used when no notifications are displaying, and the min value is when the
highest possible number of notifications are showing. -->
- <fraction name="keyguard_clock_y_fraction_max">29.5%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">18%</fraction>
+ <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
+ <fraction name="keyguard_clock_y_fraction_min">19.8%</fraction>
<!-- The margin between the clock and the notifications on Keyguard. See
keyguard_clock_height_fraction_* for the difference between min and max.-->
- <dimen name="keyguard_clock_notifications_margin_min">22dp</dimen>
+ <dimen name="keyguard_clock_notifications_margin_min">24dp</dimen>
<dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
<dimen name="heads_up_window_height">250dp</dimen>
@@ -330,6 +330,4 @@
phone hints. -->
<dimen name="edge_tap_area_width">48dp</dimen>
- <!-- the distance the panel moves up when starting the up motion on Keyguard -->
- <dimen name="keyguard_panel_move_up_distance">100dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 43560a3..c117eba 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -63,7 +63,7 @@
<style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
<!-- Note: must be dp to fit in status bar -->
<item name="android:textSize">16dp</item>
- <item name="android:textStyle">normal</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textColor">@color/status_bar_clock_color</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index e8f3745..c3ba349 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -126,7 +126,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
}
private void updateNotification() {
- Slog.d(TAG, "updateNotification mWarning=" + mWarning
+ if (DEBUG) Slog.d(TAG, "updateNotification mWarning=" + mWarning
+ " mSaver=" + mSaver + " mInvalidCharger=" + mInvalidCharger);
if (mInvalidCharger) {
showInvalidChargerNotification();
@@ -238,13 +238,13 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
@Override
public void dismissLowBatteryWarning() {
- Slog.i(TAG, "dismissing low battery warning: level=" + mBatteryLevel);
+ if (DEBUG) Slog.d(TAG, "dismissing low battery warning: level=" + mBatteryLevel);
dismissLowBatteryNotification();
mFallbackDialogs.dismissLowBatteryWarning();
}
private void dismissLowBatteryNotification() {
- Slog.i(TAG, "dismissing low battery notification");
+ if (mWarning) Slog.i(TAG, "dismissing low battery notification");
mWarning = false;
updateNotification();
}
@@ -307,7 +307,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
}
private void dismissInvalidChargerNotification() {
- Slog.i(TAG, "dismissing invalid charger notification");
+ if (mInvalidCharger) Slog.i(TAG, "dismissing invalid charger notification");
mInvalidCharger = false;
updateNotification();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index db85b14..6a83a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -88,15 +88,13 @@ public class KeyguardClockPositionAlgorithm {
public void run(Result result) {
int y = getClockY() - mKeyguardStatusHeight/2;
- float topAdjustment = getTopExpansionAdjustment();
+ float clockAdjustment = getClockYExpansionAdjustment();
float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
- result.stackScrollerPaddingAdjustment = (int) (topAdjustment*topPaddingAdjMultiplier);
+ result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
int clockNotificationsPadding = getClockNotificationsPadding()
+ result.stackScrollerPaddingAdjustment;
int padding = y + clockNotificationsPadding;
- if (mNotificationCount == 0) {
- y += topAdjustment;
- }
+ y += clockAdjustment;
result.clockY = y;
result.stackScrollerPadding = mKeyguardStatusHeight + padding;
result.clockAlpha = getClockAlpha(result.stackScrollerPadding
@@ -119,8 +117,8 @@ public class KeyguardClockPositionAlgorithm {
return (int) (getClockYFraction() * mHeight);
}
- private float getTopExpansionAdjustment() {
- float rubberbandFactor = getTopExpansionRubberbandFactor();
+ private float getClockYExpansionAdjustment() {
+ float rubberbandFactor = getClockYExpansionRubberbandFactor();
float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight));
float t = value / mMaxPanelHeight;
float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR
@@ -132,7 +130,7 @@ public class KeyguardClockPositionAlgorithm {
}
}
- private float getTopExpansionRubberbandFactor() {
+ private float getClockYExpansionRubberbandFactor() {
float t = getNotificationAmountT();
t = Math.min(t, 1.0f);
t = (float) Math.pow(t, 0.3f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
index ca49408..e312d58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
@@ -246,9 +246,7 @@ public class KeyguardPageSwipeHelper {
private void startHintTranslationAnimations(float target, long duration,
Interpolator interpolator) {
ArrayList<View> targetViews = mCallback.getTranslationViews();
- int length = targetViews.size();
- for (int i = 0; i < length; i++) {
- View targetView = targetViews.get(i);
+ for (View targetView : targetViews) {
targetView.animate()
.setDuration(duration)
.setInterpolator(interpolator)
@@ -261,16 +259,8 @@ public class KeyguardPageSwipeHelper {
}
private void cancelAnimations() {
- ArrayList<View> translatingViews = mCallback.getTranslationViews();
- int length = translatingViews.size();
- for (int i = 0; i < length; i++) {
- View target = translatingViews.get(i);
- target.animate().cancel();
- }
- ArrayList<View> fadingViews = mCallback.getFadingViews();
- length = fadingViews.size();
- for (int i = 0; i < length; i++) {
- View target = fadingViews.get(i);
+ ArrayList<View> targetViews = mCallback.getTranslationViews();
+ for (View target : targetViews) {
target.animate().cancel();
}
View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
@@ -301,11 +291,6 @@ public class KeyguardPageSwipeHelper {
// translation Animation
startTranslationAnimations(vel, target);
- // fade animations
- if (snapBack) {
- startFadeInAnimations();
- }
-
// animate left / right icon
startIconAnimation(vel, snapBack, target);
@@ -361,20 +346,9 @@ public class KeyguardPageSwipeHelper {
mSwipeAnimator = animator;
}
- private void startFadeInAnimations() {
- ArrayList<View> fadingViews = mCallback.getFadingViews();
- int length = fadingViews.size();
- for (int i = 0; i < length; i++) {
- View targetView = fadingViews.get(i);
- targetView.animate().alpha(1.0f);
- }
- }
-
private void startTranslationAnimations(float vel, float target) {
ArrayList<View> targetViews = mCallback.getTranslationViews();
- int length = targetViews.size();
- for (int i = 0; i < length; i++) {
- View targetView = targetViews.get(i);
+ for (View targetView : targetViews) {
ViewPropertyAnimator animator = targetView.animate();
mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
animator.translationX(target);
@@ -401,18 +375,8 @@ public class KeyguardPageSwipeHelper {
translation = leftSwipePossible() ? translation : Math.min(0, translation);
if (translation != mTranslation || isReset) {
ArrayList<View> translatedViews = mCallback.getTranslationViews();
- int length = translatedViews.size();
- for (int i = 0; i < length; i++) {
- View target = translatedViews.get(i);
- target.setTranslationX(translation);
- }
- float targetAlpha = 1.0f - Math.abs(translation / mMinTranslationAmount);
- targetAlpha = Math.max(0.0f, targetAlpha);
- ArrayList<View> fadingViews = mCallback.getFadingViews();
- length = fadingViews.size();
- for (int i = 0; i < length; i++) {
- View view = fadingViews.get(i);
- view.setAlpha(targetAlpha);
+ for (View view : translatedViews) {
+ view.setTranslationX(translation);
}
if (translation == 0.0f) {
boolean animate = !isReset;
@@ -530,8 +494,6 @@ public class KeyguardPageSwipeHelper {
ArrayList<View> getTranslationViews();
- ArrayList<View> getFadingViews();
-
View getLeftIcon();
View getCenterIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 5c686fc..34179cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -108,7 +108,6 @@ public class NotificationPanelView extends PanelView implements
private KeyguardBottomAreaView mKeyguardBottomArea;
private boolean mBlockTouches;
private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
- private ArrayList<View> mSwipeFadingViews = new ArrayList<>();
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -153,7 +152,7 @@ public class NotificationPanelView extends PanelView implements
android.R.interpolator.fast_out_linear_in);
mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
mSwipeTranslationViews.add(mNotificationStackScroller);
- mSwipeFadingViews.add(mKeyguardStatusView);
+ mSwipeTranslationViews.add(mKeyguardStatusView);
mPageSwiper = new KeyguardPageSwipeHelper(this, getContext());
}
@@ -696,11 +695,6 @@ public class NotificationPanelView extends PanelView implements
}
@Override
- protected boolean hasNotifications() {
- return mNotificationStackScroller.getNotGoneChildCount() > 0;
- }
-
- @Override
protected int getMaxPanelHeight() {
// TODO: Figure out transition for collapsing when QS is open, adjust height here.
int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
@@ -964,11 +958,6 @@ public class NotificationPanelView extends PanelView implements
}
@Override
- public ArrayList<View> getFadingViews() {
- return mSwipeFadingViews;
- }
-
- @Override
public View getLeftIcon() {
return getLayoutDirection() == LAYOUT_DIRECTION_RTL
? mKeyguardBottomArea.getCameraImageView()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 08305dc..1f3098d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -30,8 +30,6 @@ import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
@@ -45,8 +43,6 @@ public abstract class PanelView extends FrameLayout {
public static final boolean DEBUG = PanelBar.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
- private static final long KEYGUARD_MOVE_UP_LENGTH = 300;
-
private final void logf(String fmt, Object... args) {
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
@@ -66,9 +62,6 @@ public abstract class PanelView extends FrameLayout {
protected int mTouchSlop;
protected boolean mHintAnimationRunning;
private boolean mOverExpandedBeforeFling;
- private boolean mKeyguardMovingUp;
- private int mKeyguardMoveUpDistance;
- private float mKeyguardFingerHeight;
private ValueAnimator mHeightAnimator;
private ObjectAnimator mPeekAnimator;
@@ -89,8 +82,6 @@ public abstract class PanelView extends FrameLayout {
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mBounceInterpolator;
- private Interpolator mKeyguardMoveUpInterpolator;
- private final Interpolator mLinearInterpolator = new LinearInterpolator();
protected void onExpandingFinished() {
mBar.onExpandingFinished();
@@ -118,7 +109,6 @@ public abstract class PanelView extends FrameLayout {
mLinearOutSlowInInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
mBounceInterpolator = new BounceInterpolator();
- mKeyguardMoveUpInterpolator = new PathInterpolator(0.6f, 0f, 0.4f, 1f);
}
protected void loadDimens() {
@@ -130,8 +120,6 @@ public abstract class PanelView extends FrameLayout {
mTouchSlop = configuration.getScaledTouchSlop();
mHintDistance = res.getDimension(R.dimen.hint_move_distance);
mEdgeTapAreaWidth = res.getDimensionPixelSize(R.dimen.edge_tap_area_width);
- mKeyguardMoveUpDistance =
- res.getDimensionPixelSize(R.dimen.keyguard_panel_move_up_distance);
}
private void trackMovement(MotionEvent event) {
@@ -229,13 +217,8 @@ public abstract class PanelView extends FrameLayout {
mJustPeeked = false;
}
if (!mJustPeeked && (!waitForTouchSlop || mTracking)) {
- if (mStatusBar.getBarState() == StatusBarState.KEYGUARD &&
- !hasNotifications()) {
- setExpandedHeightKeyguard(newHeight);
- } else {
- setExpandedHeightInternal(newHeight);
- mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
- }
+ setExpandedHeightInternal(newHeight);
+ mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
}
trackMovement(event);
@@ -264,56 +247,10 @@ public abstract class PanelView extends FrameLayout {
return !waitForTouchSlop || mTracking;
}
- protected abstract boolean hasNotifications();
-
- private void setExpandedHeightKeyguard(float newHeight) {
- mKeyguardFingerHeight = newHeight;
- if (newHeight < getMaxPanelHeight() && !mKeyguardMovingUp) {
- mKeyguardMovingUp = true;
- ValueAnimator anim = createHeightAnimator(
- getMaxPanelHeight() - mKeyguardMoveUpDistance);
- anim.setDuration(KEYGUARD_MOVE_UP_LENGTH);
- anim.setInterpolator(mKeyguardMoveUpInterpolator);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHeightAnimator = null;
- }
- });
- mHeightAnimator = anim;
- anim.start();
- postOnAnimationDelayed(new Runnable() {
- @Override
- public void run() {
- if (mKeyguardFingerHeight < mExpandedHeight && mHeightAnimator != null
- && mKeyguardMovingUp) {
- mHeightAnimator.cancel();
- float target = getMaxPanelHeight() - 1.75f * mKeyguardMoveUpDistance;
- float diff = mExpandedHeight - target;
- ValueAnimator anim = createHeightAnimator(target);
- float velocity = 2.5f * mKeyguardMoveUpDistance /
- (KEYGUARD_MOVE_UP_LENGTH / 1000f);
- anim.setInterpolator(mLinearInterpolator);
- anim.setDuration(Math.max(0, (long) (diff / velocity * 1000f)));
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mHeightAnimator = null;
- }
- });
- mHeightAnimator = anim;
- anim.start();
- }
- }
- }, KEYGUARD_MOVE_UP_LENGTH / 2);
- }
- }
-
protected abstract boolean hasConflictingGestures();
protected void onTrackingStopped(boolean expand) {
mTracking = false;
- mKeyguardMovingUp = false;
mBar.onTrackingStopped(PanelView.this, expand);
}
@@ -444,9 +381,6 @@ public abstract class PanelView extends FrameLayout {
protected void fling(float vel, boolean expand) {
cancelPeek();
- if (mHeightAnimator != null) {
- mHeightAnimator.cancel();
- }
float target = expand ? getMaxPanelHeight() : 0.0f;
if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
onExpandingFinished();
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 b23992d..1da7dab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2532,6 +2532,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
repositionNavigationBar();
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
updateShowSearchHoldoff();
+ updateRowStates();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 94472a3..4e1b686 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -241,11 +241,12 @@ public class NotificationStackScrollLayout extends ViewGroup
mBottomStackPeekSize = context.getResources()
.getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
mStackScrollAlgorithm = new StackScrollAlgorithm(context);
+ mStackScrollAlgorithm.setDimmed(mAmbientState.isDimmed());
mPaddingBetweenElementsDimmed = context.getResources()
.getDimensionPixelSize(R.dimen.notification_padding_dimmed);
mPaddingBetweenElementsNormal = context.getResources()
.getDimensionPixelSize(R.dimen.notification_padding);
- updatePadding(false);
+ updatePadding(mAmbientState.isDimmed());
int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
mExpandHelper = new ExpandHelper(getContext(), this,
diff --git a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
index b2ecb61..4ad45a8 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
@@ -40,9 +40,6 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
private static String TAG = "PhoneFallbackEventHandler";
private static final boolean DEBUG = false;
- // Use the new sessions APIs
- private static final boolean USE_SESSIONS = true;
-
Context mContext;
View mView;
@@ -294,21 +291,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
}
private void handleMediaKeyEvent(KeyEvent keyEvent) {
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
- } else {
- IAudioService audioService = IAudioService.Stub.asInterface(
- ServiceManager.checkService(Context.AUDIO_SERVICE));
- if (audioService != null) {
- try {
- audioService.dispatchMediaKeyEvent(keyEvent);
- } catch (RemoteException e) {
- Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e);
- }
- } else {
- Slog.w(TAG, "Unable to find IAudioService for media key event.");
- }
- }
+ MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
}
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 5598972..bc2671011 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -144,9 +144,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static final boolean ENABLE_CAR_DOCK_HOME_CAPTURE = true;
static final boolean ENABLE_DESK_DOCK_HOME_CAPTURE = false;
- // Whether to use the new Session APIs
- static final boolean USE_SESSIONS = true;
-
static final int SHORT_PRESS_POWER_NOTHING = 0;
static final int SHORT_PRESS_POWER_GO_TO_SLEEP = 1;
static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2;
@@ -3974,6 +3971,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* controlled by this device, or through remote submix).
*/
boolean isMusicActive() {
+
final AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
if (am == null) {
Log.w(TAG, "isMusicActive: couldn't get AudioManager reference");
@@ -3982,43 +3980,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return am.isLocalOrRemoteMusicActive();
}
- /**
- * Tell the audio service to adjust the volume appropriate to the event.
- * @param keycode
- */
- void handleVolumeKey(int stream, int keycode) {
- IAudioService audioService = getAudioService();
- if (audioService == null) {
- return;
- }
- try {
- // when audio is playing locally, we shouldn't have to hold a wake lock
- // during the call, but we do it as a precaution for the rare possibility
- // that the music stops right before we call this.
- // Otherwise we might also be in a remote playback case.
- // TODO: Actually handle MUTE.
- mBroadcastWakeLock.acquire();
- if (stream == AudioSystem.STREAM_MUSIC) {
- audioService.adjustLocalOrRemoteStreamVolume(stream,
- keycode == KeyEvent.KEYCODE_VOLUME_UP
- ? AudioManager.ADJUST_RAISE
- : AudioManager.ADJUST_LOWER,
- mContext.getOpPackageName());
- } else {
- audioService.adjustStreamVolume(stream,
- keycode == KeyEvent.KEYCODE_VOLUME_UP
- ? AudioManager.ADJUST_RAISE
- : AudioManager.ADJUST_LOWER,
- 0,
- mContext.getOpPackageName());
- }
- } catch (RemoteException e) {
- Log.w(TAG, "IAudioService.adjust*StreamVolume() threw RemoteException " + e);
- } finally {
- mBroadcastWakeLock.release();
- }
- }
-
final Object mScreenshotLock = new Object();
ServiceConnection mScreenshotConnection = null;
@@ -4201,16 +4162,20 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (telephonyManager.isOffhook()
&& (result & ACTION_PASS_TO_USER) == 0) {
// If we are in call but we decided not to pass the key to
- // the application, handle the volume change here.
- handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
+ // the application, just pass it to the session service.
+
+ MediaSessionLegacyHelper.getHelper(mContext)
+ .sendMediaButtonEvent(event, true);
break;
}
}
- if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) {
- // If music is playing but we decided not to pass the key to the
- // application, handle the volume change here.
- handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);
+ if ((result & ACTION_PASS_TO_USER) == 0) {
+ // If we aren't passing to the user and no one else
+ // handled it send it to the session manager to figure
+ // out.
+ MediaSessionLegacyHelper.getHelper(mContext)
+ .sendMediaButtonEvent(event, true);
break;
}
}
@@ -4459,18 +4424,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
void dispatchMediaKeyWithWakeLockToAudioService(KeyEvent event) {
if (ActivityManagerNative.isSystemReady()) {
- if (USE_SESSIONS) {
- MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(event, true);
- } else {
- IAudioService audioService = getAudioService();
- if (audioService != null) {
- try {
- audioService.dispatchMediaKeyEventUnderWakelock(event);
- } catch (RemoteException e) {
- Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e);
- }
- }
- }
+ MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(event, true);
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index e2a8ca2..07c9048 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -308,6 +308,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
+ if (callback == null) {
+ Log.w(TAG, "Callback is null in registerAdapter");
+ return null;
+ }
Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
msg.obj = callback;
mHandler.sendMessage(msg);
@@ -317,6 +321,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
public void unregisterAdapter(IBluetoothManagerCallback callback) {
+ if (callback == null) {
+ Log.w(TAG, "Callback is null in unregisterAdapter");
+ return;
+ }
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
"Need BLUETOOTH permission");
Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 06732ca..825dc84 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2268,16 +2268,15 @@ public class NotificationManagerService extends SystemService {
// lock on mNotificationList
int indexOfNotificationLocked(String key) {
- NotificationRecord r = mNotificationsByKey.get(key);
- if (r == null) {
- return -1;
+ final int N = mNotificationList.size();
+ for (int i = 0; i < N; i++) {
+ if (key.equals(mNotificationList.get(i).getKey())) {
+ return i;
+ }
}
- int index = Collections.binarySearch(mNotificationList, r, mRankingComparator);
- // Guarantee to return -1 when not found.
- return (index >= 0) ? index : -1;
+ return -1;
}
-
private void updateNotificationPulse() {
synchronized (mNotificationList) {
updateLightsLocked();
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 82d3f53..c0f1eec 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -345,6 +345,10 @@ public final class Installer extends SystemService {
}
}
+ public int pruneDexCache() {
+ return execute("prunedexcache");
+ }
+
public int freeCache(long freeStorageSize) {
StringBuilder builder = new StringBuilder("freecache");
builder.append(' ');
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 3ed73f7..dd33771 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -28,7 +28,7 @@ import android.content.pm.IPackageInstallerSession;
import android.content.pm.PackageInstallerParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageLite;
+import android.content.pm.PackageParser.ApkLite;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Bundle;
@@ -297,7 +297,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Verify that all staged packages are internally consistent
for (File file : files) {
- final PackageLite info = PackageParser.parsePackageLite(file.getAbsolutePath(),
+ final ApkLite info = PackageParser.parseApkLite(file.getAbsolutePath(),
PackageParser.PARSE_GET_SIGNATURES);
if (info == null) {
throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
@@ -356,7 +356,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Missing existing base package for " + mPackageName);
}
- final PackageLite info = PackageParser.parsePackageLite(app.sourceDir,
+ final ApkLite info = PackageParser.parseApkLite(app.sourceDir,
PackageParser.PARSE_GET_SIGNATURES);
if (info == null) {
throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 49502b6..1209386 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -93,6 +93,7 @@ import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstallerParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser.ActivityIntentInfo;
+import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
@@ -136,6 +137,7 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -412,9 +414,6 @@ public class PackageManagerService extends IPackageManager.Stub {
final HashMap<String, SharedLibraryEntry> mSharedLibraries
= new HashMap<String, SharedLibraryEntry>();
- // Temporary for building the final shared libraries for an .apk.
- String[] mTmpSharedLibraries = null;
-
// These are the features this devices supports that were read from the
// etc/permissions.xml file.
final HashMap<String, FeatureInfo> mAvailableFeatures =
@@ -1485,7 +1484,21 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if (didDexOptLibraryOrTool) {
- pruneDexFiles(new File(dataDir, "dalvik-cache"));
+ // If we dexopted a library or tool, then something on the system has
+ // changed. Consider this significant, and wipe away all other
+ // existing dexopt files to ensure we don't leave any dangling around.
+ //
+ // Additionally, delete all dex files from the root directory
+ // since there shouldn't be any there anyway.
+ //
+ // TODO: This should be revisited because it isn't as good an indicator
+ // as it used to be. It used to include the boot classpath but at some point
+ // DexFile.isDexOptNeeded started returning false for the boot
+ // class path files in all cases. It is very possible in a
+ // small maintenance release update that the library and tool
+ // jars may be unchanged but APK could be removed resulting in
+ // unused dalvik-cache files.
+ mInstaller.pruneDexCache();
}
// Collect vendor overlay packages.
@@ -1727,45 +1740,6 @@ public class PackageManagerService extends IPackageManager.Stub {
Runtime.getRuntime().gc();
}
- private static void pruneDexFiles(File cacheDir) {
- // If we had to do a dexopt of one of the previous
- // things, then something on the system has changed.
- // Consider this significant, and wipe away all other
- // existing dexopt files to ensure we don't leave any
- // dangling around.
- //
- // Additionally, delete all dex files from the root directory
- // since there shouldn't be any there anyway.
- //
- // Note: This isn't as good an indicator as it used to be. It
- // used to include the boot classpath but at some point
- // DexFile.isDexOptNeeded started returning false for the boot
- // class path files in all cases. It is very possible in a
- // small maintenance release update that the library and tool
- // jars may be unchanged but APK could be removed resulting in
- // unused dalvik-cache files.
- File[] files = cacheDir.listFiles();
- if (files != null) {
- for (File file : files) {
- if (!file.isDirectory()) {
- Slog.i(TAG, "Pruning dalvik file: " + file.getAbsolutePath());
- file.delete();
- } else {
- File[] subDirList = file.listFiles();
- if (subDirList != null) {
- for (File subDirFile : subDirList) {
- final String fn = subDirFile.getName();
- if (fn.startsWith("data@app@") || fn.startsWith("data@app-private@")) {
- Slog.i(TAG, "Pruning dalvik file: " + fn);
- subDirFile.delete();
- }
- }
- }
- }
- }
- }
- }
-
@Override
public boolean isFirstBoot() {
return !mRestoredSettings || mPackageUsage.isFirstBoot();
@@ -2268,8 +2242,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
return null;
}
- // TODO: teach about reading split name
- pkg = new PackageParser.Package(packageName, null);
+ pkg = new PackageParser.Package(packageName);
pkg.applicationInfo.packageName = packageName;
pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
@@ -4097,19 +4070,19 @@ public class PackageManagerService extends IPackageManager.Stub {
private boolean createIdmapForPackagePairLI(PackageParser.Package pkg,
PackageParser.Package opkg) {
if (!opkg.mTrustedOverlay) {
- Slog.w(TAG, "Skipping target and overlay pair " + pkg.mScanPath + " and " +
- opkg.mScanPath + ": overlay not trusted");
+ Slog.w(TAG, "Skipping target and overlay pair " + pkg.codePath + " and " +
+ opkg.codePath + ": overlay not trusted");
return false;
}
HashMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName);
if (overlaySet == null) {
- Slog.e(TAG, "was about to create idmap for " + pkg.mScanPath + " and " +
- opkg.mScanPath + " but target package has no known overlays");
+ Slog.e(TAG, "was about to create idmap for " + pkg.codePath + " and " +
+ opkg.codePath + " but target package has no known overlays");
return false;
}
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- if (mInstaller.idmap(pkg.mScanPath, opkg.mScanPath, sharedGid) != 0) {
- Slog.e(TAG, "Failed to generate idmap for " + pkg.mScanPath + " and " + opkg.mScanPath);
+ if (mInstaller.idmap(pkg.codePath, opkg.codePath, sharedGid) != 0) {
+ Slog.e(TAG, "Failed to generate idmap for " + pkg.codePath + " and " + opkg.codePath);
return false;
}
PackageParser.Package[] overlayArray =
@@ -4204,8 +4177,10 @@ public class PackageManagerService extends IPackageManager.Stub {
Log.i(TAG, srcFile.toString() + " changed; collecting certs");
}
- if (!pp.collectCertificates(pkg, parseFlags)) {
- mLastScanError = pp.getParseError();
+ try {
+ pp.collectCertificates(pkg, parseFlags);
+ } catch (PackageParserException e) {
+ mLastScanError = e.error;
return false;
}
return true;
@@ -4224,11 +4199,13 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser pp = new PackageParser(scanPath);
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
- final PackageParser.Package pkg = pp.parsePackage(scanFile,
- scanPath, mMetrics, parseFlags, (scanMode & SCAN_TRUSTED_OVERLAY) != 0);
- if (pkg == null) {
- mLastScanError = pp.getParseError();
+ final PackageParser.Package pkg;
+ try {
+ pkg = pp.parseMonolithicPackage(scanFile, mMetrics, parseFlags,
+ (scanMode & SCAN_TRUSTED_OVERLAY) != 0);
+ } catch (PackageParserException e) {
+ mLastScanError = e.error;
return null;
}
@@ -4395,12 +4372,13 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
}
} else {
- resPath = pkg.mScanPath;
+ resPath = pkg.codePath;
}
- codePath = pkg.mScanPath;
+ codePath = pkg.codePath;
// Set application objects path explicitly.
- setApplicationInfoPaths(pkg, codePath, resPath);
+ pkg.applicationInfo.sourceDir = codePath;
+ pkg.applicationInfo.publicSourceDir = resPath;
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
| SCAN_UPDATE_SIGNATURE, currentTime, user, abiOverride);
@@ -4425,13 +4403,6 @@ public class PackageManagerService extends IPackageManager.Stub {
return scannedPkg;
}
- private static void setApplicationInfoPaths(PackageParser.Package pkg, String destCodePath,
- String destResPath) {
- pkg.mPath = pkg.mScanPath = destCodePath;
- pkg.applicationInfo.sourceDir = destCodePath;
- pkg.applicationInfo.publicSourceDir = destResPath;
- }
-
private static String fixProcessName(String defProcessName,
String processName, int uid) {
if (processName == null) {
@@ -4657,7 +4628,7 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean performed = false;
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
- String path = pkg.mScanPath;
+ String path = pkg.codePath;
try {
boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path,
pkg.packageName,
@@ -4830,11 +4801,11 @@ public class PackageManagerService extends IPackageManager.Stub {
return res;
}
- private int addSharedLibraryLPw(final SharedLibraryEntry file, int num,
+ private void addSharedLibraryLPw(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
PackageParser.Package changingLib) {
if (file.path != null) {
- mTmpSharedLibraries[num] = file.path;
- return num+1;
+ usesLibraryFiles.add(file.path);
+ return;
}
PackageParser.Package p = mPackages.get(file.apk);
if (changingLib != null && changingLib.packageName.equals(file.apk)) {
@@ -4847,16 +4818,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
if (p != null) {
- String path = p.mPath;
- for (int i=0; i<num; i++) {
- if (mTmpSharedLibraries[i].equals(path)) {
- return num;
- }
- }
- mTmpSharedLibraries[num] = p.mPath;
- return num+1;
+ usesLibraryFiles.add(p.codePath);
}
- return num;
}
private boolean updateSharedLibrariesLPw(PackageParser.Package pkg,
@@ -4870,11 +4833,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
- if (mTmpSharedLibraries == null ||
- mTmpSharedLibraries.length < mSharedLibraries.size()) {
- mTmpSharedLibraries = new String[mSharedLibraries.size()];
- }
- int num = 0;
+ final ArraySet<String> usesLibraryFiles = new ArraySet<>();
int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
for (int i=0; i<N; i++) {
final SharedLibraryEntry file = mSharedLibraries.get(pkg.usesLibraries.get(i));
@@ -4885,7 +4844,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
return false;
}
- num = addSharedLibraryLPw(file, num, changingLib);
+ addSharedLibraryLPw(usesLibraryFiles, file, changingLib);
}
N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
for (int i=0; i<N; i++) {
@@ -4895,13 +4854,12 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " desires unavailable shared library "
+ pkg.usesOptionalLibraries.get(i) + "; ignoring!");
} else {
- num = addSharedLibraryLPw(file, num, changingLib);
+ addSharedLibraryLPw(usesLibraryFiles, file, changingLib);
}
}
- if (num > 0) {
- pkg.usesLibraryFiles = new String[num];
- System.arraycopy(mTmpSharedLibraries, 0,
- pkg.usesLibraryFiles, 0, num);
+ N = usesLibraryFiles.size();
+ if (N > 0) {
+ pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[N]);
} else {
pkg.usesLibraryFiles = null;
}
@@ -4947,7 +4905,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
- File scanFile = new File(pkg.mScanPath);
+ final File scanFile = new File(pkg.codePath);
if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
pkg.applicationInfo.publicSourceDir == null) {
// Bail out. The resource and code paths haven't been set.
@@ -5385,7 +5343,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pkgSetting.uidError = uidError;
}
- String path = scanFile.getPath();
+ final String path = scanFile.getPath();
/* Note: We don't want to unpack the native binaries for
* system applications, unless they have been updated
* (the binaries are already under /system/lib).
@@ -5516,7 +5474,6 @@ public class PackageManagerService extends IPackageManager.Stub {
handle.close();
}
}
- pkg.mScanPath = path;
if ((scanMode&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
// We don't do this here during boot because we can do it all
@@ -5661,7 +5618,7 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
// We don't expect installation to fail beyond this point,
if ((scanMode&SCAN_MONITOR) != 0) {
- mAppDirs.put(pkg.mPath, pkg);
+ mAppDirs.put(pkg.codePath, pkg);
}
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
@@ -6282,8 +6239,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// writer
synchronized (mPackages) {
mPackages.remove(pkg.applicationInfo.packageName);
- if (pkg.mPath != null) {
- mAppDirs.remove(pkg.mPath);
+ if (pkg.codePath != null) {
+ mAppDirs.remove(pkg.codePath);
}
cleanPackageDataStructuresLILPw(pkg, chatty);
}
@@ -9850,7 +9807,7 @@ public class PackageManagerService extends IPackageManager.Stub {
res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
return;
}
- if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {
+ if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.codePath)) {
// Don't allow installation over an existing package with the same name.
Slog.w(TAG, "Attempt to re-install " + pkgName
+ " without first uninstalling.");
@@ -9862,15 +9819,12 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
System.currentTimeMillis(), user, abiOverride);
if (newPackage == null) {
- Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
+ Slog.w(TAG, "Package couldn't be installed in " + pkg.codePath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
} else {
- updateSettingsLI(newPackage,
- installerPackageName,
- null, null,
- res);
+ updateSettingsLI(null, newPackage, installerPackageName, null, null, res);
// delete the partially installed application. the data directory will have to be
// restored if it was already existing
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -9955,15 +9909,13 @@ public class PackageManagerService extends IPackageManager.Stub {
newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME,
System.currentTimeMillis(), user, abiOverride);
if (newPackage == null) {
- Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
+ Slog.w(TAG, "Package couldn't be installed in " + pkg.codePath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
} else {
- updateSettingsLI(newPackage,
- installerPackageName,
- allUsers, perUserInstalled,
- res);
+ updateSettingsLI(deletedPackage, newPackage, installerPackageName, allUsers,
+ perUserInstalled, res);
updatedSettings = true;
}
}
@@ -9984,7 +9936,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// package that we deleted.
if (deletedPkg) {
if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
- File restoreFile = new File(deletedPackage.mPath);
+ File restoreFile = new File(deletedPackage.codePath);
// Parse old package
boolean oldOnSd = isExternal(deletedPackage);
int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY |
@@ -10069,7 +10021,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user, abiOverride);
if (newPackage == null) {
- Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);
+ Slog.w(TAG, "Package couldn't be installed in " + pkg.codePath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
}
@@ -10089,7 +10041,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res);
+ updateSettingsLI(deletedPackage, newPackage, installerPackageName, allUsers,
+ perUserInstalled, res);
updatedSettings = true;
}
}
@@ -10115,12 +10068,21 @@ public class PackageManagerService extends IPackageManager.Stub {
}
// Utility method used to move dex files during install.
- private int moveDexFilesLI(PackageParser.Package newPackage) {
- if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ private int moveDexFilesLI(PackageParser.Package oldPackage, PackageParser.Package newPackage) {
+ // TODO: extend to handle splits
+ if ((newPackage.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
final String instructionSet = getAppInstructionSet(newPackage.applicationInfo);
- int retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath,
- instructionSet);
- if (retCode != 0) {
+
+ boolean moveSuccess = false;
+ if (oldPackage != null
+ && (oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ if (mInstaller.movedex(oldPackage.codePath, newPackage.codePath, instructionSet)
+ == 0) {
+ moveSuccess = true;
+ }
+ }
+
+ if (!moveSuccess) {
/*
* Programs may be lazily run through dexopt, so the
* source may not exist. However, something seems to
@@ -10129,17 +10091,19 @@ public class PackageManagerService extends IPackageManager.Stub {
* remove the target to make sure there isn't a stale
* file from a previous version of the package.
*/
+ if (oldPackage != null) {
+ mInstaller.rmdex(oldPackage.codePath, instructionSet);
+ }
+ mInstaller.rmdex(newPackage.codePath, instructionSet);
newPackage.mDexOptNeeded = true;
- mInstaller.rmdex(newPackage.mScanPath, instructionSet);
- mInstaller.rmdex(newPackage.mPath, instructionSet);
}
}
return PackageManager.INSTALL_SUCCEEDED;
}
- private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
- int[] allUsers, boolean[] perUserInstalled,
- PackageInstalledInfo res) {
+ private void updateSettingsLI(PackageParser.Package oldPackage,
+ PackageParser.Package newPackage, String installerPackageName, int[] allUsers,
+ boolean[] perUserInstalled, PackageInstalledInfo res) {
String pkgName = newPackage.packageName;
synchronized (mPackages) {
//write settings. the installStatus will be incomplete at this stage.
@@ -10149,13 +10113,13 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.writeLPr();
}
- if ((res.returnCode = moveDexFilesLI(newPackage))
+ if ((res.returnCode = moveDexFilesLI(oldPackage, newPackage))
!= PackageManager.INSTALL_SUCCEEDED) {
// Discontinue if moving dex files failed.
return;
}
- if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.mPath);
+ if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);
synchronized (mPackages) {
updatePermissionsLPw(newPackage.packageName, newPackage,
@@ -10222,12 +10186,16 @@ public class PackageManagerService extends IPackageManager.Stub {
| (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
PackageParser pp = new PackageParser(tmpPackageFile.getPath());
pp.setSeparateProcesses(mSeparateProcesses);
- final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
- null, mMetrics, parseFlags);
- if (pkg == null) {
- res.returnCode = pp.getParseError();
+
+ final PackageParser.Package pkg;
+ try {
+ pkg = pp.parseMonolithicPackage(tmpPackageFile, mMetrics,
+ parseFlags);
+ } catch (PackageParserException e) {
+ res.returnCode = e.error;
return;
}
+
String pkgName = res.name = pkg.packageName;
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
@@ -10235,8 +10203,11 @@ public class PackageManagerService extends IPackageManager.Stub {
return;
}
}
- if (!pp.collectCertificates(pkg, parseFlags)) {
- res.returnCode = pp.getParseError();
+
+ try {
+ pp.collectCertificates(pkg, parseFlags);
+ } catch (PackageParserException e) {
+ res.returnCode = e.error;
return;
}
@@ -10341,7 +10312,9 @@ public class PackageManagerService extends IPackageManager.Stub {
return;
}
// Set application objects path explicitly after the rename
- setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
+ pkg.codePath = args.getCodePath();
+ pkg.applicationInfo.sourceDir = args.getCodePath();
+ pkg.applicationInfo.publicSourceDir = args.getResourcePath();
pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
if (replace) {
replacePackageLI(pkg, parseFlags, scanMode, args.user,
@@ -11221,7 +11194,7 @@ public class PackageManagerService extends IPackageManager.Stub {
publicSrcDir = applicationInfo.publicSourceDir;
}
}
- int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, libDirPath,
+ int res = mInstaller.getSizeInfo(packageName, userHandle, p.codePath, libDirPath,
publicSrcDir, asecPath, getAppInstructionSetFromSettings(ps),
pStats);
if (res < 0) {
@@ -12854,7 +12827,7 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " Aborting move and returning error");
returnCode = PackageManager.MOVE_FAILED_INTERNAL_ERROR;
} else {
- final String oldCodePath = pkg.mPath;
+ final String oldCodePath = pkg.codePath;
final String newCodePath = mp.targetArgs.getCodePath();
final String newResPath = mp.targetArgs.getResourcePath();
final String newNativePath = mp.targetArgs
@@ -12884,18 +12857,20 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- pkg.mPath = newCodePath;
+ PackageParser.Package oldPackage = new PackageParser.Package(
+ pkg.packageName);
+ oldPackage.codePath = pkg.codePath;
+ pkg.codePath = newCodePath;
// Move dex files around
- if (moveDexFilesLI(pkg) != PackageManager.INSTALL_SUCCEEDED) {
+ if (moveDexFilesLI(oldPackage, pkg) != PackageManager.INSTALL_SUCCEEDED) {
// Moving of dex files failed. Set
// error code and abort move.
- pkg.mPath = pkg.mScanPath;
+ pkg.codePath = oldPackage.codePath;
returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
}
}
if (returnCode == PackageManager.MOVE_SUCCEEDED) {
- pkg.mScanPath = newCodePath;
pkg.applicationInfo.sourceDir = newCodePath;
pkg.applicationInfo.publicSourceDir = newResPath;
pkg.applicationInfo.nativeLibraryDir = newNativePath;
diff --git a/telecomm/java/android/telecomm/PhoneApplication.java b/telecomm/java/android/telecomm/PhoneApplication.java
new file mode 100644
index 0000000..1da54e0
--- /dev/null
+++ b/telecomm/java/android/telecomm/PhoneApplication.java
@@ -0,0 +1,183 @@
+package android.telecomm;
+
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telecomm.ITelecommService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class for managing the primary phone application that will receive incoming calls, and be allowed
+ * to make emergency outgoing calls.
+ *
+ * @hide
+ */
+public class PhoneApplication {
+ private static final String TAG = PhoneApplication.class.getSimpleName();
+ private static final String TELECOMM_SERVICE_NAME = "telecomm";
+
+ /**
+ * Sets the specified package name as the default phone application. The caller of this method
+ * needs to have permission to write to secure settings.
+ *
+ * @hide
+ * */
+ @SystemApi
+ public static void setDefaultPhoneApplication(String packageName, Context context) {
+ // Get old package name
+ String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.PHONE_DEFAULT_APPLICATION);
+
+ if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
+ // No change
+ return;
+ }
+
+ // Only make the change if the new package belongs to a valid phone application
+ List<ComponentName> componentNames = getInstalledPhoneApplications(context);
+ ComponentName foundComponent = null;
+ for (ComponentName componentName : componentNames) {
+ if (TextUtils.equals(componentName.getPackageName(), packageName)) {
+ foundComponent = componentName;
+ break;
+ }
+ }
+
+ if (foundComponent != null) {
+ // Update the secure setting.
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.PHONE_DEFAULT_APPLICATION, foundComponent.getPackageName());
+ }
+ }
+
+ /**
+ * Returns the installed phone application that will be used to receive incoming calls, and is
+ * allowed to make emergency calls.
+ *
+ * The application will be returned in order of preference:
+ * 1) User selected phone application (if still installed)
+ * 2) Pre-installed system dialer (if not disabled)
+ * 3) Null
+ *
+ * @hide
+ * */
+ @SystemApi
+ public static ComponentName getDefaultPhoneApplication(Context context) {
+ String defaultPackageName = Settings.Secure.getString(context.getContentResolver(),
+ Settings.Secure.PHONE_DEFAULT_APPLICATION);
+
+ final List<ComponentName> componentNames = getInstalledPhoneApplications(context);
+ if (!TextUtils.isEmpty(defaultPackageName)) {
+ for (ComponentName componentName : componentNames) {
+ if (TextUtils.equals(componentName.getPackageName(), defaultPackageName)) {
+ return componentName;
+ }
+ }
+ }
+
+ // No user-set dialer found, fallback to system dialer
+ ComponentName systemDialer = null;
+ try {
+ systemDialer = getTelecommService().getSystemPhoneApplication();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecommService#getSystemPhoneApplication", e);
+ return null;
+ }
+
+ if (systemDialer == null) {
+ // No system dialer configured at build time
+ return null;
+ }
+
+ // Verify that the system dialer has not been disabled.
+ return getComponentName(componentNames, systemDialer.getPackageName());
+ }
+
+ /**
+ * Returns a list of installed and available phone applications.
+ *
+ * In order to appear in the list, a phone application must implement an intent-filter with
+ * the DIAL intent for the following schemes:
+ *
+ * 1) Empty scheme
+ * 2) tel Uri scheme
+ *
+ * @hide
+ **/
+ @SystemApi
+ public static List<ComponentName> getInstalledPhoneApplications(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+
+ // Get the list of apps registered for the DIAL intent with empty scheme
+ Intent intent = new Intent(Intent.ACTION_DIAL);
+ List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
+
+ List<ComponentName> componentNames = new ArrayList<ComponentName> ();
+
+ for (ResolveInfo resolveInfo : resolveInfoList) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ final ComponentName componentName =
+ new ComponentName(activityInfo.packageName, activityInfo.name);
+ componentNames.add(componentName);
+ }
+
+ // TODO: Filter for apps that don't handle DIAL intent with tel scheme
+ return componentNames;
+ }
+
+ /**
+ * Returns the {@link ComponentName} for the installed phone application for a given package
+ * name.
+ *
+ * @param context A valid context.
+ * @param packageName to retrieve the {@link ComponentName} for.
+ *
+ * @return The {@link ComponentName} for the installed phone application corresponding to the
+ * package name, or null if none is found.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static ComponentName getPhoneApplicationForPackageName(Context context,
+ String packageName) {
+ return getComponentName(getInstalledPhoneApplications(context), packageName);
+ }
+
+ /**
+ * Returns the component from a list of application components that corresponds to the package
+ * name.
+ *
+ * @param componentNames A list of component names
+ * @param packageName The package name to look for
+ * @return The {@link ComponentName} that matches the provided packageName, or null if not
+ * found.
+ */
+ private static ComponentName getComponentName(List<ComponentName> componentNames,
+ String packageName) {
+ for (ComponentName componentName : componentNames) {
+ if (TextUtils.equals(packageName, componentName.getPackageName())) {
+ return componentName;
+ }
+ }
+ return null;
+ }
+
+ private static ITelecommService getTelecommService() {
+ return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
+ }
+}
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index 8300c92..4ca878e 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -55,6 +55,21 @@ public final class TelecommConstants {
public static final String ACTION_CALL_SERVICE_SELECTOR = CallServiceSelector.class.getName();
/**
+ * Activity action: Ask the user to change the default phone application. This will show a
+ * dialog that asks the user whether they want to replace the current default phone application
+ * with the one defined in {@link #EXTRA_PACKAGE_NAME}.
+ */
+ public static final String ACTION_CHANGE_DEFAULT_PHONE =
+ "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
+
+ /**
+ * The PackageName string passed in as an extra for {@link #ACTION_CHANGE_DEFAULT_PHONE}.
+ *
+ * @see #ACTION_CHANGE_DEFAULT_PHONE
+ */
+ public static final String EXTRA_PACKAGE_NAME = "package";
+
+ /**
* Extra for {@link #ACTION_INCOMING_CALL} containing the {@link CallServiceDescriptor} that
* describes the call service to use for the incoming call.
*/
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
new file mode 100644
index 0000000..a97e7e4
--- /dev/null
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 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.telecomm;
+
+import android.content.Context;
+
+import com.android.internal.telecomm.ITelecommService;
+
+/**
+ * Provides access to Telecomm-related functionality.
+ */
+public class TelecommManager {
+ private static final String TAG = "TelecommManager";
+
+ private final Context mContext;
+ private final ITelecommService mService;
+
+ /** @hide */
+ public TelecommManager(Context context, ITelecommService service) {
+ Context appContext = context.getApplicationContext();
+ if (appContext != null) {
+ mContext = appContext;
+ } else {
+ mContext = context;
+ }
+
+ mService = service;
+ }
+
+ /** {@hide} */
+ public static TelecommManager from(Context context) {
+ return (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE);
+ }
+}
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 0e94ffb..638b86a 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -16,6 +16,8 @@
package com.android.internal.telecomm;
+import android.content.ComponentName;
+
/**
* Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
* commands that were previously handled by ITelephony.
@@ -38,4 +40,9 @@ interface ITelecommService {
* @param showDialpad if true, make the dialpad visible initially.
*/
void showCallScreen(boolean showDialpad);
+
+ /**
+ * Returns the component name of the phone application installed on the system partition.
+ */
+ ComponentName getSystemPhoneApplication();
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index aaee99f..50bbb1e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1414,6 +1414,9 @@ public class TelephonyManager {
// FIXME the argument to pass is subId ??
public int getSimState(int slotId) {
long[] subId = SubscriptionManager.getSubId(slotId);
+ if (subId == null) {
+ return SIM_STATE_ABSENT;
+ }
// FIXME Do not use a property to determine SIM_STATE, call
// appropriate method on some object.
String prop =
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
index 54ac71e..33ccad5 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
@@ -493,7 +493,8 @@ public class WifiPasspointCredential implements Parcelable {
}
if (this.mType.equals("TLS")) {
result = this.mRealm.equals(other.mRealm) &&
- this.mHomeSpFqdn.equals(other.mHomeSpFqdn);
+ this.mHomeSpFqdn.equals(other.mHomeSpFqdn) &&
+ this.mClientCert.equals(other.mClientCert);
}
if (this.mType.equals("SIM")) {
result = this.mMcc.equals(other.mMcc) &&
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
index 9fccf0a..f84ac88 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
@@ -16,10 +16,18 @@
package android.net.wifi.passpoint;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.ScanResult;
import android.os.Parcelable;
import android.os.Parcel;
+import android.security.Credentials;
import android.util.Log;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+
/** @hide */
public class WifiPasspointPolicy implements Parcelable {
@@ -43,6 +51,20 @@ public class WifiPasspointPolicy implements Parcelable {
private int mRestriction;// Permitted values are "HomeSP", "RoamingPartner", or "Unrestricted"
private boolean mIsHomeSp;
+ private final String INT_PRIVATE_KEY = "private_key";
+ private final String INT_PHASE2 = "phase2";
+ private final String INT_PASSWORD = "password";
+ private final String INT_IDENTITY = "identity";
+ private final String INT_EAP = "eap";
+ private final String INT_CLIENT_CERT = "client_cert";
+ private final String INT_CA_CERT = "ca_cert";
+ private final String INT_ANONYMOUS_IDENTITY = "anonymous_identity";
+ private final String INT_SIM_SLOT = "sim_slot";
+ private final String INT_ENTERPRISEFIELD_NAME ="android.net.wifi.WifiConfiguration$EnterpriseField";
+ private final String ISO8601DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
+ private final String ENTERPRISE_PHASE2_MSCHAPV2 = "auth=MSCHAPV2";
+ private final String ENTERPRISE_PHASE2_MSCHAP = "auth=MSCHAP";
+
/** @hide */
public WifiPasspointPolicy(String name, String ssid,
String bssid, WifiPasspointCredential pc,
@@ -89,7 +111,7 @@ public class WifiPasspointPolicy implements Parcelable {
}
/** @hide */
- public boolean getHomeSp() {
+ public boolean isHomeSp() {
return mIsHomeSp;
}
@@ -121,6 +143,142 @@ public class WifiPasspointPolicy implements Parcelable {
return mRoamingPriority;
}
+ public WifiConfiguration createWifiConfiguration() {
+ WifiConfiguration wfg = new WifiConfiguration();
+ if (mBssid != null) {
+ Log.d(TAG, "create bssid:" + mBssid);
+ wfg.BSSID = mBssid;
+ }
+
+ if (mSsid != null) {
+ Log.d(TAG, "create ssid:" + mSsid);
+ wfg.SSID = mSsid;
+ }
+ //TODO: 1. add pmf configuration
+ // 2. add ocsp configuration
+ // 3. add eap-sim configuration
+ /*Key management*/
+ wfg.status = WifiConfiguration.Status.ENABLED;
+ wfg.allowedKeyManagement.clear();
+ wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+
+ /*Group Ciphers*/
+ wfg.allowedGroupCiphers.clear();
+ wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+ wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
+
+ /*Protocols*/
+ wfg.allowedProtocols.clear();
+ wfg.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+ wfg.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
+
+ Class[] enterpriseFieldArray = WifiConfiguration.class.getClasses();
+ Class<?> enterpriseFieldClass = null;
+
+
+ for(Class<?> myClass : enterpriseFieldArray) {
+ if(myClass.getName().equals(INT_ENTERPRISEFIELD_NAME)) {
+ enterpriseFieldClass = myClass;
+ break;
+ }
+ }
+ Log.d(TAG, "class chosen " + enterpriseFieldClass.getName() );
+
+
+ Field anonymousId = null, caCert = null, clientCert = null,
+ eap = null, identity = null, password = null,
+ phase2 = null, privateKey = null;
+
+ Field[] fields = WifiConfiguration.class.getFields();
+
+
+ for (Field tempField : fields) {
+ if (tempField.getName().trim().equals(INT_ANONYMOUS_IDENTITY)) {
+ anonymousId = tempField;
+ Log.d(TAG, "field " + anonymousId.getName() );
+ } else if (tempField.getName().trim().equals(INT_CA_CERT)) {
+ caCert = tempField;
+ } else if (tempField.getName().trim().equals(INT_CLIENT_CERT)) {
+ clientCert = tempField;
+ Log.d(TAG, "field " + clientCert.getName() );
+ } else if (tempField.getName().trim().equals(INT_EAP)) {
+ eap = tempField;
+ Log.d(TAG, "field " + eap.getName() );
+ } else if (tempField.getName().trim().equals(INT_IDENTITY)) {
+ identity = tempField;
+ Log.d(TAG, "field " + identity.getName() );
+ } else if (tempField.getName().trim().equals(INT_PASSWORD)) {
+ password = tempField;
+ Log.d(TAG, "field " + password.getName() );
+ } else if (tempField.getName().trim().equals(INT_PHASE2)) {
+ phase2 = tempField;
+ Log.d(TAG, "field " + phase2.getName() );
+
+ } else if (tempField.getName().trim().equals(INT_PRIVATE_KEY)) {
+ privateKey = tempField;
+ }
+ }
+
+
+ Method setValue = null;
+
+ for(Method m: enterpriseFieldClass.getMethods()) {
+ if(m.getName().trim().equals("setValue")) {
+ Log.d(TAG, "method " + m.getName() );
+ setValue = m;
+ break;
+ }
+ }
+
+ try {
+ // EAP
+ String eapmethod = mCredential.getType();
+ Log.d(TAG, "eapmethod:" + eapmethod);
+ setValue.invoke(eap.get(wfg), eapmethod);
+
+ // Username, password, EAP Phase 2
+ if ("TTLS".equals(eapmethod)) {
+ setValue.invoke(phase2.get(wfg), ENTERPRISE_PHASE2_MSCHAPV2);
+ setValue.invoke(identity.get(wfg), mCredential.getUserName());
+ setValue.invoke(password.get(wfg), mCredential.getPassword());
+ setValue.invoke(anonymousId.get(wfg), "anonymous@" + mCredential.getRealm());
+ }
+
+ // EAP CA Certificate
+ String cacertificate = null;
+ String rootCA = mCredential.getCaRootCertPath();
+ if (rootCA == null){
+ cacertificate = null;
+ } else {
+ cacertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.CA_CERTIFICATE + rootCA;
+ }
+ Log.d(TAG, "cacertificate:" + cacertificate);
+ setValue.invoke(caCert.get(wfg), cacertificate);
+
+ //User certificate
+ if ("TLS".equals(eapmethod)) {
+ String usercertificate = null;
+ String privatekey = null;
+ String clientCertPath = mCredential.getClientCertPath();
+ if (clientCertPath != null){
+ privatekey = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_PRIVATE_KEY + clientCertPath;
+ usercertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_CERTIFICATE + clientCertPath;
+ }
+ Log.d(TAG, "privatekey:" + privatekey);
+ Log.d(TAG, "usercertificate:" + usercertificate);
+ if (privatekey != null && usercertificate != null) {
+ setValue.invoke(privateKey.get(wfg), privatekey);
+ setValue.invoke(clientCert.get(wfg), usercertificate);
+ }
+ }
+ } catch (Exception e) {
+ Log.d(TAG, "createWifiConfiguration err:" + e);
+ }
+
+ return wfg;
+ }
+
/** {@inheritDoc} @hide */
public int compareTo(WifiPasspointPolicy another) {
Log.d(TAG, "this:" + this);
@@ -128,11 +286,11 @@ public class WifiPasspointPolicy implements Parcelable {
if (another == null) {
return -1;
- } else if (this.mIsHomeSp == true && another.getHomeSp() == false) {
+ } else if (this.mIsHomeSp == true && another.isHomeSp() == false) {
//home sp priority is higher then roaming
Log.d(TAG, "compare HomeSP first, this is HomeSP, another isn't");
return -1;
- } else if ((this.mIsHomeSp == true && another.getHomeSp() == true)) {
+ } else if ((this.mIsHomeSp == true && another.isHomeSp() == true)) {
Log.d(TAG, "both HomeSP");
//if both home sp, compare credential priority
if (this.mCredentialPriority < another.getCredentialPriority()) {
@@ -160,7 +318,7 @@ public class WifiPasspointPolicy implements Parcelable {
} else {
return 1;
}
- } else if ((this.mIsHomeSp == false && another.getHomeSp() == false)) {
+ } else if ((this.mIsHomeSp == false && another.isHomeSp() == false)) {
Log.d(TAG, "both RoamingSp");
//if both roaming sp, compare roaming priority(preferredRoamingPartnerList/<X+>/priority)
if (this.mRoamingPriority < another.getRoamingPriority()) {