summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/17.txt6
-rw-r--r--api/current.txt6
-rw-r--r--core/java/android/app/Presentation.java89
-rw-r--r--core/java/android/hardware/LegacySensorManager.java11
-rw-r--r--core/java/android/hardware/display/DisplayManager.java72
-rw-r--r--core/java/android/os/Debug.java17
-rw-r--r--core/java/android/os/PowerManager.java28
-rw-r--r--core/java/android/provider/Settings.java25
-rw-r--r--core/java/android/server/search/SearchManagerService.java5
-rw-r--r--core/java/android/view/Display.java103
-rw-r--r--core/java/android/view/DisplayInfo.java27
-rw-r--r--core/java/android/view/View.java17
-rw-r--r--core/java/android/view/ViewGroup.java3
-rw-r--r--core/java/android/widget/Editor.java29
-rw-r--r--core/java/android/widget/FrameLayout.java5
-rw-r--r--core/java/android/widget/LinearLayout.java6
-rw-r--r--core/java/android/widget/RelativeLayout.java7
-rw-r--r--core/java/android/widget/ScrollView.java2
-rw-r--r--core/java/android/widget/TableRow.java5
-rw-r--r--core/java/android/widget/TextView.java9
-rw-r--r--core/java/com/android/internal/policy/IFaceLockInterface.aidl1
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java2
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java13
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java145
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/GlowPadView.java65
-rw-r--r--core/res/res/anim/keyguard_security_fade_in.xml4
-rw-r--r--core/res/res/anim/keyguard_security_fade_out.xml6
-rw-r--r--core/res/res/drawable-hdpi/kg_add_widget.pngbin0 -> 1648 bytes
-rw-r--r--core/res/res/drawable-hdpi/kg_bouncer_bg_white.9.pngbin0 -> 546 bytes
-rw-r--r--core/res/res/drawable-hdpi/kg_security_grip.9.pngbin0 -> 140 bytes
-rw-r--r--core/res/res/drawable-hdpi/kg_security_lock.pngbin0 -> 2700 bytes
-rw-r--r--core/res/res/drawable-hdpi/sym_keyboard_return_holo.pngbin0 -> 1104 bytes
-rw-r--r--core/res/res/drawable-mdpi/kg_add_widget.pngbin0 -> 1185 bytes
-rw-r--r--core/res/res/drawable-mdpi/kg_bouncer_bg_white.9.pngbin0 -> 397 bytes
-rw-r--r--core/res/res/drawable-mdpi/kg_security_grip.9.pngbin0 -> 136 bytes
-rw-r--r--core/res/res/drawable-mdpi/kg_security_lock.pngbin0 -> 1921 bytes
-rw-r--r--core/res/res/drawable-mdpi/sym_keyboard_return_holo.pngbin0 -> 823 bytes
-rw-r--r--core/res/res/drawable-sw600dp-hdpi/sym_keyboard_return_holo.pngbin0 -> 1104 bytes
-rw-r--r--core/res/res/drawable-sw600dp-mdpi/sym_keyboard_return_holo.pngbin0 -> 823 bytes
-rw-r--r--core/res/res/drawable-sw600dp-xhdpi/sym_keyboard_return_holo.pngbin0 -> 1501 bytes
-rw-r--r--core/res/res/drawable-xhdpi/default_wallpaper.jpgbin87482 -> 93296 bytes
-rw-r--r--core/res/res/drawable-xhdpi/kg_add_widget.pngbin0 -> 2220 bytes
-rw-r--r--core/res/res/drawable-xhdpi/kg_bouncer_bg_white.9.pngbin0 -> 775 bytes
-rw-r--r--core/res/res/drawable-xhdpi/kg_security_grip.9.pngbin0 -> 149 bytes
-rw-r--r--core/res/res/drawable-xhdpi/kg_security_lock.pngbin0 -> 3499 bytes
-rw-r--r--core/res/res/drawable-xhdpi/sym_keyboard_return_holo.pngbin0 -> 1501 bytes
-rw-r--r--core/res/res/layout-land/keyguard_host_view.xml57
-rw-r--r--core/res/res/layout-land/keyguard_widget_pager.xml (renamed from core/res/res/layout/keyguard_navigation.xml)16
-rw-r--r--core/res/res/layout-port/keyguard_host_view.xml57
-rw-r--r--core/res/res/layout-port/keyguard_widget_pager.xml32
-rw-r--r--core/res/res/layout-sw600dp-port/keyguard_host_view.xml59
-rw-r--r--core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml4
-rw-r--r--core/res/res/layout/keyguard_account_view.xml4
-rw-r--r--core/res/res/layout/keyguard_add_widget.xml40
-rw-r--r--core/res/res/layout/keyguard_emergency_carrier_area.xml43
-rw-r--r--core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml73
-rw-r--r--core/res/res/layout/keyguard_face_unlock_view.xml9
-rw-r--r--core/res/res/layout/keyguard_glow_pad_view.xml6
-rw-r--r--core/res/res/layout/keyguard_message_area.xml32
-rw-r--r--core/res/res/layout/keyguard_message_area_large.xml31
-rw-r--r--core/res/res/layout/keyguard_multi_user_avatar.xml43
-rw-r--r--core/res/res/layout/keyguard_multi_user_selector.xml21
-rw-r--r--core/res/res/layout/keyguard_multi_user_selector_widget.xml3
-rw-r--r--core/res/res/layout/keyguard_password_view.xml39
-rw-r--r--core/res/res/layout/keyguard_pattern_view.xml8
-rw-r--r--core/res/res/layout/keyguard_pin_view.xml210
-rw-r--r--core/res/res/layout/keyguard_selector_view.xml4
-rw-r--r--core/res/res/layout/keyguard_sim_pin_view.xml5
-rw-r--r--core/res/res/layout/keyguard_sim_puk_view.xml4
-rw-r--r--core/res/res/layout/keyguard_status_view.xml2
-rw-r--r--core/res/res/layout/keyguard_transport_control_view.xml16
-rw-r--r--core/res/res/layout/keyguard_widget_region.xml71
-rw-r--r--core/res/res/values-af/strings.xml3
-rw-r--r--core/res/res/values-am/strings.xml4
-rw-r--r--core/res/res/values-ar/strings.xml3
-rw-r--r--core/res/res/values-be/strings.xml3
-rw-r--r--core/res/res/values-bg/strings.xml4
-rw-r--r--core/res/res/values-ca/strings.xml3
-rw-r--r--core/res/res/values-cs/strings.xml3
-rw-r--r--core/res/res/values-da/strings.xml4
-rw-r--r--core/res/res/values-de/strings.xml3
-rw-r--r--core/res/res/values-el/strings.xml3
-rw-r--r--core/res/res/values-en-rGB/strings.xml3
-rw-r--r--core/res/res/values-es-rUS/strings.xml4
-rw-r--r--core/res/res/values-es/strings.xml3
-rw-r--r--core/res/res/values-et/strings.xml3
-rw-r--r--core/res/res/values-fa/strings.xml3
-rw-r--r--core/res/res/values-fi/strings.xml3
-rw-r--r--core/res/res/values-fr/strings.xml3
-rw-r--r--core/res/res/values-hi/strings.xml3
-rw-r--r--core/res/res/values-hr/strings.xml3
-rw-r--r--core/res/res/values-hu/strings.xml3
-rw-r--r--core/res/res/values-in/strings.xml3
-rw-r--r--core/res/res/values-it/strings.xml3
-rw-r--r--core/res/res/values-iw/strings.xml3
-rw-r--r--core/res/res/values-ja/strings.xml3
-rw-r--r--core/res/res/values-ko/strings.xml4
-rw-r--r--core/res/res/values-land/bools.xml1
-rw-r--r--core/res/res/values-land/integers.xml3
-rw-r--r--core/res/res/values-lt/strings.xml3
-rw-r--r--core/res/res/values-lv/strings.xml3
-rw-r--r--core/res/res/values-ms/strings.xml3
-rw-r--r--core/res/res/values-nb/strings.xml4
-rw-r--r--core/res/res/values-nl/strings.xml3
-rw-r--r--core/res/res/values-pl/strings.xml3
-rw-r--r--core/res/res/values-pt-rPT/strings.xml3
-rw-r--r--core/res/res/values-pt/strings.xml3
-rw-r--r--core/res/res/values-rm/strings.xml5
-rw-r--r--core/res/res/values-ro/strings.xml4
-rw-r--r--core/res/res/values-ru/strings.xml6
-rw-r--r--core/res/res/values-sk/strings.xml4
-rw-r--r--core/res/res/values-sl/strings.xml3
-rw-r--r--core/res/res/values-sr/strings.xml7
-rw-r--r--core/res/res/values-sv/strings.xml3
-rw-r--r--core/res/res/values-sw/strings.xml3
-rw-r--r--core/res/res/values-sw600dp-land/integers.xml3
-rw-r--r--core/res/res/values-sw600dp/bools.xml2
-rw-r--r--core/res/res/values-sw600dp/dimens.xml6
-rw-r--r--core/res/res/values-sw600dp/integers.xml (renamed from core/res/res/layout/empty_navigation.xml)8
-rw-r--r--core/res/res/values-sw720dp/dimens.xml12
-rw-r--r--core/res/res/values-th/strings.xml3
-rw-r--r--core/res/res/values-tl/strings.xml3
-rw-r--r--core/res/res/values-tr/strings.xml4
-rw-r--r--core/res/res/values-uk/strings.xml3
-rw-r--r--core/res/res/values-vi/strings.xml3
-rw-r--r--core/res/res/values-zh-rCN/strings.xml4
-rw-r--r--core/res/res/values-zh-rTW/strings.xml3
-rw-r--r--core/res/res/values-zu/strings.xml3
-rw-r--r--core/res/res/values/arrays.xml21
-rwxr-xr-xcore/res/res/values/attrs.xml64
-rw-r--r--core/res/res/values/bools.xml1
-rw-r--r--core/res/res/values/colors.xml5
-rw-r--r--core/res/res/values/dimens.xml25
-rw-r--r--core/res/res/values/integers.xml2
-rwxr-xr-xcore/res/res/values/strings.xml20
-rw-r--r--core/res/res/values/styles.xml26
-rw-r--r--core/res/res/values/symbols.xml34
-rw-r--r--docs/downloads/training/Animations.zipbin0 -> 2238999 bytes
-rw-r--r--docs/html/distribute/googleplay/publish/preparing.jd2
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_10/land_back.pngbin0 -> 481923 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_10/land_fore.pngbin0 -> 33741 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_10/land_shadow.pngbin0 -> 31761 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_10/port_back.pngbin0 -> 508801 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_10/port_fore.pngbin0 -> 31737 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_10/port_shadow.pngbin0 -> 36240 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_10/thumb.pngbin0 -> 17663 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_4/land_back.pngbin0 -> 816742 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_4/land_fore.pngbin0 -> 34120 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_4/land_shadow.pngbin0 -> 31820 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_4/port_back.pngbin0 -> 849499 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_4/port_fore.pngbin0 -> 28044 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_4/port_shadow.pngbin0 -> 37737 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_4/thumb.pngbin0 -> 11736 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_7/land_back.pngbin29977 -> 158936 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.pngbin30153 -> 33274 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.pngbin33797 -> 31186 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_7/port_back.pngbin30267 -> 177565 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.pngbin32982 -> 30824 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.pngbin41554 -> 29482 bytes
-rw-r--r--docs/html/distribute/promote/device-art-resources/nexus_7/thumb.pngbin8163 -> 7754 bytes
-rw-r--r--docs/html/distribute/promote/device-art.jd152
-rwxr-xr-xdocs/html/training/animation/anim_card_flip.mp4bin0 -> 87087 bytes
-rwxr-xr-xdocs/html/training/animation/anim_card_flip.ogvbin0 -> 135105 bytes
-rwxr-xr-xdocs/html/training/animation/anim_card_flip.webmbin0 -> 95870 bytes
-rw-r--r--docs/html/training/animation/anim_crossfade.mp4bin0 -> 67644 bytes
-rw-r--r--docs/html/training/animation/anim_crossfade.ogvbin0 -> 102569 bytes
-rw-r--r--docs/html/training/animation/anim_crossfade.webmbin0 -> 65957 bytes
-rw-r--r--docs/html/training/animation/anim_layout_changes.mp4bin0 -> 42798 bytes
-rw-r--r--docs/html/training/animation/anim_layout_changes.ogvbin0 -> 50321 bytes
-rw-r--r--docs/html/training/animation/anim_layout_changes.webmbin0 -> 39560 bytes
-rwxr-xr-xdocs/html/training/animation/anim_screenslide.mp4bin0 -> 205799 bytes
-rwxr-xr-xdocs/html/training/animation/anim_screenslide.ogvbin0 -> 727629 bytes
-rwxr-xr-xdocs/html/training/animation/anim_screenslide.webmbin0 -> 131539 bytes
-rw-r--r--docs/html/training/animation/anim_zoom.mp4bin0 -> 66004 bytes
-rw-r--r--docs/html/training/animation/anim_zoom.ogvbin0 -> 98651 bytes
-rw-r--r--docs/html/training/animation/anim_zoom.webmbin0 -> 72442 bytes
-rw-r--r--docs/html/training/animation/cardflip.jd365
-rw-r--r--docs/html/training/animation/crossfade.jd208
-rw-r--r--docs/html/training/animation/index.jd86
-rw-r--r--docs/html/training/animation/layout.jd77
-rwxr-xr-xdocs/html/training/animation/screen-slide.jd185
-rw-r--r--docs/html/training/animation/zoom.jd320
-rw-r--r--docs/html/training/training_toc.cs28
-rw-r--r--location/java/android/location/Geofence.java4
-rw-r--r--location/java/android/location/LocationProvider.java4
-rw-r--r--media/java/android/media/AudioService.java74
-rw-r--r--media/java/android/media/MediaRouter.java131
-rw-r--r--packages/FusedLocation/AndroidManifest.xml6
-rw-r--r--packages/SystemUI/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/res/mipmap-hdpi/ic_dreams.pngbin3572 -> 0 bytes
-rw-r--r--packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.pngbin6369 -> 8149 bytes
-rw-r--r--packages/SystemUI/res/mipmap-mdpi/ic_dreams.pngbin2366 -> 0 bytes
-rw-r--r--packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.pngbin3575 -> 4688 bytes
-rw-r--r--packages/SystemUI/res/mipmap-xhdpi/ic_dreams.pngbin4996 -> 0 bytes
-rw-r--r--packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.pngbin9373 -> 11680 bytes
-rw-r--r--packages/SystemUI/res/values-af/strings.xml3
-rw-r--r--packages/SystemUI/res/values-am/strings.xml2
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml2
-rw-r--r--packages/SystemUI/res/values-be/strings.xml3
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml2
-rw-r--r--packages/SystemUI/res/values-da/strings.xml2
-rw-r--r--packages/SystemUI/res/values-de/strings.xml2
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml3
-rw-r--r--packages/SystemUI/res/values-es/strings.xml2
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml3
-rw-r--r--packages/SystemUI/res/values-in/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml3
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml3
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml9
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml3
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml3
-rw-r--r--packages/SystemUI/res/values-th/strings.xml3
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml3
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml3
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml2
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml3
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml3
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/Somnambulator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java7
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java50
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java315
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java92
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/CheckLongPressHelper.java82
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java1
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java10
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java229
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java9
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java181
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java161
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java56
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java390
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java46
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java215
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java147
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java92
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardNavigationManager.java57
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java97
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java280
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java15
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java20
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java10
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java6
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java72
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java107
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java15
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java20
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java6
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java23
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java214
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java9
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java25
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java6
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java12
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java36
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java34
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java210
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java114
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java297
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java563
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java125
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java490
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java116
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/PagedView.java912
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java2
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java1014
-rw-r--r--services/java/com/android/server/AlarmManagerService.java8
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java9
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java5
-rw-r--r--services/java/com/android/server/display/DisplayDeviceInfo.java20
-rw-r--r--services/java/com/android/server/display/HeadlessDisplayAdapter.java2
-rw-r--r--services/java/com/android/server/display/LocalDisplayAdapter.java3
-rw-r--r--services/java/com/android/server/display/LogicalDisplay.java2
-rw-r--r--services/java/com/android/server/display/OverlayDisplayAdapter.java2
-rw-r--r--services/java/com/android/server/display/WifiDisplayAdapter.java10
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java4
-rw-r--r--services/java/com/android/server/pm/Settings.java46
-rw-r--r--services/java/com/android/server/pm/UserManagerService.java40
-rw-r--r--services/java/com/android/server/power/DisplayPowerState.java4
-rw-r--r--services/java/com/android/server/wm/WindowAnimator.java2
-rwxr-xr-xservices/java/com/android/server/wm/WindowManagerService.java56
-rw-r--r--services/java/com/android/server/wm/WindowState.java2
-rw-r--r--services/java/com/android/server/wm/WindowToken.java3
-rw-r--r--wifi/java/android/net/wifi/WifiSsid.java6
290 files changed, 8917 insertions, 2023 deletions
diff --git a/api/17.txt b/api/17.txt
index e1e89b55d511..e26d8f03b806 100644
--- a/api/17.txt
+++ b/api/17.txt
@@ -10093,8 +10093,10 @@ package android.hardware.display {
public final class DisplayManager {
method public android.view.Display getDisplay(int);
method public android.view.Display[] getDisplays();
+ method public android.view.Display[] getDisplays(java.lang.String);
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
+ field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
}
public static abstract interface DisplayManager.DisplayListener {
@@ -11768,6 +11770,7 @@ package android.media {
method public abstract void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteGrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup, int);
+ method public void onRoutePresentationDisplayChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteRemoved(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteSelected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteUngrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup);
@@ -11802,6 +11805,7 @@ package android.media {
method public java.lang.CharSequence getName(android.content.Context);
method public int getPlaybackStream();
method public int getPlaybackType();
+ method public android.view.Display getPresentationDisplay();
method public java.lang.CharSequence getStatus();
method public int getSupportedTypes();
method public java.lang.Object getTag();
@@ -25365,7 +25369,6 @@ package android.view {
field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
field public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4
- field public static int TEXT_ALIGNMENT_DEFAULT;
field public static final int TEXT_ALIGNMENT_GRAVITY = 1; // 0x1
field public static final int TEXT_ALIGNMENT_INHERIT = 0; // 0x0
field public static final int TEXT_ALIGNMENT_TEXT_END = 3; // 0x3
@@ -25373,7 +25376,6 @@ package android.view {
field public static final int TEXT_ALIGNMENT_VIEW_END = 6; // 0x6
field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
- field public static int TEXT_DIRECTION_DEFAULT;
field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
diff --git a/api/current.txt b/api/current.txt
index e1e89b55d511..e26d8f03b806 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10093,8 +10093,10 @@ package android.hardware.display {
public final class DisplayManager {
method public android.view.Display getDisplay(int);
method public android.view.Display[] getDisplays();
+ method public android.view.Display[] getDisplays(java.lang.String);
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
+ field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
}
public static abstract interface DisplayManager.DisplayListener {
@@ -11768,6 +11770,7 @@ package android.media {
method public abstract void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteGrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup, int);
+ method public void onRoutePresentationDisplayChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteRemoved(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteSelected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo);
method public abstract void onRouteUngrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup);
@@ -11802,6 +11805,7 @@ package android.media {
method public java.lang.CharSequence getName(android.content.Context);
method public int getPlaybackStream();
method public int getPlaybackType();
+ method public android.view.Display getPresentationDisplay();
method public java.lang.CharSequence getStatus();
method public int getSupportedTypes();
method public java.lang.Object getTag();
@@ -25365,7 +25369,6 @@ package android.view {
field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
field public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4
- field public static int TEXT_ALIGNMENT_DEFAULT;
field public static final int TEXT_ALIGNMENT_GRAVITY = 1; // 0x1
field public static final int TEXT_ALIGNMENT_INHERIT = 0; // 0x0
field public static final int TEXT_ALIGNMENT_TEXT_END = 3; // 0x3
@@ -25373,7 +25376,6 @@ package android.view {
field public static final int TEXT_ALIGNMENT_VIEW_END = 6; // 0x6
field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
- field public static int TEXT_DIRECTION_DEFAULT;
field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java
index 20b27c5ccefb..3e8af603c8fc 100644
--- a/core/java/android/app/Presentation.java
+++ b/core/java/android/app/Presentation.java
@@ -50,6 +50,93 @@ import android.util.TypedValue;
* whenever the activity itself is paused or resumed.
* </p>
*
+ * <h3>Choosing a presentation display</h3>
+ * <p>
+ * Before showing a {@link Presentation} it's important to choose the {@link Display}
+ * on which it will appear. Choosing a presentation display is sometimes difficult
+ * because there may be multiple displays attached. Rather than trying to guess
+ * which display is best, an application should let the system choose a suitable
+ * presentation display.
+ * </p><p>
+ * There are two main ways to choose a {@link Display}.
+ * </p>
+ *
+ * <h4>Using the media router to choose a presentation display</h4>
+ * <p>
+ * The easiest way to choose a presentation display is to use the
+ * {@link android.media.MediaRouter MediaRouter} API. The media router service keeps
+ * track of which audio and video routes are available on the system.
+ * The media router sends notifications whenever routes are selected or unselected
+ * or when the preferred presentation display of a route changes.
+ * So an application can simply watch for these notifications and show or dismiss
+ * a presentation on the preferred presentation display automatically.
+ * </p><p>
+ * The preferred presentation display is the display that the media router recommends
+ * that the application should use if it wants to show content on the secondary display.
+ * Sometimes there may not be a preferred presentation display in which
+ * case the application should show its content locally without using a presentation.
+ * </p><p>
+ * Here's how to use the media router to create and show a presentation on the preferred
+ * presentation display using {@link android.media.MediaRouter.RouteInfo#getPresentationDisplay()}.
+ * </p>
+ * {@samplecode
+ * MediaRouter mediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
+ * if (route != null) $&#123;
+ * Display presentationDisplay = route.getPresentationDisplay();
+ * if (presentationDisplay != null) $&#123;
+ * Presentation presentation = new MyPresentation(context, presentationDisplay);
+ * presentation.show();
+ * $&#125;
+ * $&#125;
+ * }
+ * <p>
+ * The following sample code from <code>ApiDemos</code> demonstrates how to use the media
+ * router to automatically switch between showing content in the main activity and showing
+ * the content in a presentation when a presentation display is available.
+ * </p>
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/PresentationWithMediaRouterActivity.java
+ * activity}
+ *
+ * <h4>Using the display manager to choose a presentation display</h4>
+ * <p>
+ * Another way to choose a presentation display is to use the {@link DisplayManager} API
+ * directly. The display manager service provides functions to enumerate and describe all
+ * displays that are attached to the system including displays that may be used
+ * for presentations.
+ * </p><p>
+ * The display manager keeps track of all displays in the system. However, not all
+ * displays are appropriate for showing presentations. For example, if an activity
+ * attempted to show a presentation on the main display it might obscure its own content
+ * (it's like opening a dialog on top of your activity).
+ * </p><p>
+ * Here's how to identify suitable displays for showing presentations using
+ * {@link DisplayManager#getDisplays(String)} and the
+ * {@link DisplayManager#DISPLAY_CATEGORY_PRESENTATION} category.
+ * </p>
+ * {@samplecode
+ * DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ * Display[] presentationDisplays = displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
+ * if (presentationDisplays.length > 0) $&#123;
+ * // If there is more than one suitable presentation display, then we could consider
+ * // giving the user a choice. For this example, we simply choose the first display
+ * // which is the one the system recommends as the preferred presentation display.
+ * Display display = presentationDisplays[0];
+ * Presentation presentation = new MyPresentation(context, presentationDisplay);
+ * presentation.show();
+ * $&#125;
+ * }
+ * <p>
+ * The following sample code from <code>ApiDemos</code> demonstrates how to use the display
+ * manager to enumerate displays and show content on multiple presentation displays
+ * simultaneously.
+ * </p>
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/PresentationActivity.java
+ * activity}
+ *
+ * @see android.media.MediaRouter#ROUTE_TYPE_LIVE_VIDEO for information on about live
+ * video routes and how to obtain the preferred presentation display for the
+ * current media route.
* @see DisplayManager for information on how to enumerate displays and receive
* notifications when displays are added or removed.
*/
@@ -121,7 +208,7 @@ public class Presentation extends Dialog {
@Override
protected void onStart() {
super.onStart();
- mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
// Since we were not watching for display changes until just now, there is a
// chance that the display metrics have changed. If so, we will need to
diff --git a/core/java/android/hardware/LegacySensorManager.java b/core/java/android/hardware/LegacySensorManager.java
index 62c194f0005d..f95909385fb9 100644
--- a/core/java/android/hardware/LegacySensorManager.java
+++ b/core/java/android/hardware/LegacySensorManager.java
@@ -371,7 +371,7 @@ final class LegacySensorManager {
private static final float PREDICTION_RATIO = 1.0f/3.0f;
private static final float PREDICTION_TIME = (SENSORS_RATE_MS*COUNT/1000.0f)*PREDICTION_RATIO;
private float mV[] = new float[COUNT*2];
- private float mT[] = new float[COUNT*2];
+ private long mT[] = new long[COUNT*2];
private int mIndex;
public LmsFilter() {
@@ -381,7 +381,6 @@ final class LegacySensorManager {
public float filter(long time, float in) {
float v = in;
final float ns = 1.0f / 1000000000.0f;
- final float t = time*ns;
float v1 = mV[mIndex];
if ((v-v1) > 180) {
v -= 360;
@@ -396,9 +395,9 @@ final class LegacySensorManager {
if (mIndex >= COUNT*2)
mIndex = COUNT;
mV[mIndex] = v;
- mT[mIndex] = t;
+ mT[mIndex] = time;
mV[mIndex-COUNT] = v;
- mT[mIndex-COUNT] = t;
+ mT[mIndex-COUNT] = time;
float A, B, C, D, E;
float a, b;
@@ -408,8 +407,8 @@ final class LegacySensorManager {
for (i=0 ; i<COUNT-1 ; i++) {
final int j = mIndex - 1 - i;
final float Z = mV[j];
- final float T = 0.5f*(mT[j] + mT[j+1]) - t;
- float dT = mT[j] - mT[j+1];
+ final float T = (mT[j]/2 + mT[j+1]/2 - time)*ns;
+ float dT = (mT[j] - mT[j+1])*ns;
dT *= dT;
A += Z*dT;
B += T*(T*dT);
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 28e320baca88..0a7a2e71b770 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -21,6 +21,8 @@ import android.os.Handler;
import android.util.SparseArray;
import android.view.Display;
+import java.util.ArrayList;
+
/**
* Manages the properties of attached displays.
* <p>
@@ -40,6 +42,8 @@ public final class DisplayManager {
private final Object mLock = new Object();
private final SparseArray<Display> mDisplays = new SparseArray<Display>();
+ private final ArrayList<Display> mTempDisplays = new ArrayList<Display>();
+
/**
* Broadcast receiver that indicates when the Wifi display status changes.
* <p>
@@ -60,6 +64,20 @@ public final class DisplayManager {
public static final String EXTRA_WIFI_DISPLAY_STATUS =
"android.hardware.display.extra.WIFI_DISPLAY_STATUS";
+ /**
+ * Display category: Presentation displays.
+ * <p>
+ * This category can be used to identify secondary displays that are suitable for
+ * use as presentation displays.
+ * </p>
+ *
+ * @see android.app.Presentation for information about presenting content
+ * on secondary displays.
+ * @see #getDisplays(String)
+ */
+ public static final String DISPLAY_CATEGORY_PRESENTATION =
+ "android.hardware.display.category.PRESENTATION";
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
@@ -87,24 +105,52 @@ public final class DisplayManager {
* @return An array containing all displays.
*/
public Display[] getDisplays() {
- int[] displayIds = mGlobal.getDisplayIds();
- int expectedCount = displayIds.length;
- Display[] displays = new Display[expectedCount];
+ return getDisplays(null);
+ }
+
+ /**
+ * Gets all currently valid logical displays of the specified category.
+ * <p>
+ * When there are multiple displays in a category the returned displays are sorted
+ * of preference. For example, if the requested category is
+ * {@link #DISPLAY_CATEGORY_PRESENTATION} and there are multiple presentation displays
+ * then the displays are sorted so that the first display in the returned array
+ * is the most preferred presentation display. The application may simply
+ * use the first display or allow the user to choose.
+ * </p>
+ *
+ * @param category The requested display category or null to return all displays.
+ * @return An array containing all displays sorted by order of preference.
+ *
+ * @see #DISPLAY_CATEGORY_PRESENTATION
+ */
+ public Display[] getDisplays(String category) {
+ final int[] displayIds = mGlobal.getDisplayIds();
synchronized (mLock) {
- int actualCount = 0;
- for (int i = 0; i < expectedCount; i++) {
- Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
- if (display != null) {
- displays[actualCount++] = display;
+ try {
+ if (category == null) {
+ addMatchingDisplaysLocked(mTempDisplays, displayIds, -1);
+ } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
+ addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
+ addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_HDMI);
+ addMatchingDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
}
+ return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
+ } finally {
+ mTempDisplays.clear();
}
- if (actualCount != expectedCount) {
- Display[] oldDisplays = displays;
- displays = new Display[actualCount];
- System.arraycopy(oldDisplays, 0, displays, 0, actualCount);
+ }
+ }
+
+ private void addMatchingDisplaysLocked(
+ ArrayList<Display> displays, int[] displayIds, int matchType) {
+ for (int i = 0; i < displayIds.length; i++) {
+ Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
+ if (display != null
+ && (matchType < 0 || display.getType() == matchType)) {
+ displays.add(display);
}
}
- return displays;
}
private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index c08bfeb17d54..e50c94813a74 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1363,6 +1363,23 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
}
/**
+ * Like {@link #getCallers(int)}, but each location is append to the string
+ * as a new line with <var>linePrefix</var> in front of it.
+ * @param depth the number of levels to return, starting with the immediate caller.
+ * @param linePrefix prefix to put in front of each location.
+ * @return a string describing the call stack.
+ * {@hide}
+ */
+ public static String getCallers(final int depth, String linePrefix) {
+ final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < depth; i++) {
+ sb.append(linePrefix).append(getCaller(callStack, i)).append("\n");
+ }
+ return sb.toString();
+ }
+
+ /**
* @return a String describing the immediate caller of the calling function.
* {@hide}
*/
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 7e11c22fd24f..4a011135aeac 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -46,29 +46,25 @@ import android.util.Log;
* The following wake lock levels are defined, with varying effects on system power.
* <i>These levels are mutually exclusive - you may only specify one of them.</i>
*
- * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
- * <thead>
+ * <table>
* <tr><th>Flag Value</th>
* <th>CPU</th> <th>Screen</th> <th>Keyboard</th></tr>
- * </thead>
*
- * <tbody>
- * <tr><th>{@link #PARTIAL_WAKE_LOCK}</th>
+ * <tr><td>{@link #PARTIAL_WAKE_LOCK}</td>
* <td>On*</td> <td>Off</td> <td>Off</td>
* </tr>
*
- * <tr><th>{@link #SCREEN_DIM_WAKE_LOCK}</th>
+ * <tr><td>{@link #SCREEN_DIM_WAKE_LOCK}</td>
* <td>On</td> <td>Dim</td> <td>Off</td>
* </tr>
*
- * <tr><th>{@link #SCREEN_BRIGHT_WAKE_LOCK}</th>
+ * <tr><td>{@link #SCREEN_BRIGHT_WAKE_LOCK}</td>
* <td>On</td> <td>Bright</td> <td>Off</td>
* </tr>
*
- * <tr><th>{@link #FULL_WAKE_LOCK}</th>
+ * <tr><td>{@link #FULL_WAKE_LOCK}</td>
* <td>On</td> <td>Bright</td> <td>Bright</td>
* </tr>
- * </tbody>
* </table>
* </p><p>
* *<i>If you hold a partial wake lock, the CPU will continue to run, regardless of any
@@ -77,15 +73,12 @@ import android.util.Log;
* using the power button.</i>
* </p><p>
* In addition, you can add two more flags, which affect behavior of the screen only.
- * <i>These flags have no effect when combined with a {@link #PARTIAL_WAKE_LOCK}.</i>
+ * <i>These flags have no effect when combined with a {@link #PARTIAL_WAKE_LOCK}.</i></p>
*
- * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
- * <thead>
+ * <table>
* <tr><th>Flag Value</th> <th>Description</th></tr>
- * </thead>
*
- * <tbody>
- * <tr><th>{@link #ACQUIRE_CAUSES_WAKEUP}</th>
+ * <tr><td>{@link #ACQUIRE_CAUSES_WAKEUP}</td>
* <td>Normal wake locks don't actually turn on the illumination. Instead, they cause
* the illumination to remain on once it turns on (e.g. from user activity). This flag
* will force the screen and/or keyboard to turn on immediately, when the WakeLock is
@@ -93,14 +86,13 @@ import android.util.Log;
* see immediately.</td>
* </tr>
*
- * <tr><th>{@link #ON_AFTER_RELEASE}</th>
+ * <tr><td>{@link #ON_AFTER_RELEASE}</td>
* <td>If this flag is set, the user activity timer will be reset when the WakeLock is
* released, causing the illumination to remain on a bit longer. This can be used to
* reduce flicker if you are cycling between wake lock conditions.</td>
* </tr>
- * </tbody>
* </table>
- * </p><p>
+ * <p>
* Any application using a WakeLock must request the {@code android.permission.WAKE_LOCK}
* permission in an {@code &lt;uses-permission&gt;} element of the application's manifest.
* </p>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8897039fbe66..f482335721ff 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3216,27 +3216,27 @@ public final class Settings {
/**
- * This preference contains the string that shows for owner info on LockScren.
+ * This preference contains the string that shows for owner info on LockScreen.
* @hide
*/
public static final String LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info";
/**
- * Id of the time appwidget on the lockscreen, or -1 if none
+ * Ids of the user-selected appwidgets on the lockscreen (comma-delimited).
* @hide
*/
- public static final String LOCK_SCREEN_STATUS_APPWIDGET_ID =
- "lock_screen_status_appwidget_id";
+ public static final String LOCK_SCREEN_APPWIDGET_IDS =
+ "lock_screen_appwidget_ids";
/**
- * Id of the user-selected appwidget on the lockscreen, or -1 if none
+ * Index of the lockscreen appwidget to restore, -1 if none.
* @hide
*/
- public static final String LOCK_SCREEN_USER_SELECTED_APPWIDGET_ID =
- "lock_screen_user_selected_appwidget_id";
+ public static final String LOCK_SCREEN_STICKY_APPWIDGET =
+ "lock_screen_sticky_appwidget";
/**
- * This preference enables showing the owner info on LockScren.
+ * This preference enables showing the owner info on LockScreen.
* @hide
*/
public static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
@@ -5300,6 +5300,14 @@ public final class Settings {
"always_finish_activities";
/**
+ * Use Dock audio output for media:
+ * 0 = disabled
+ * 1 = enabled
+ * @hide
+ */
+ public static final String DOCK_AUDIO_MEDIA_ENABLED = "dock_audio_media_enabled";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
@@ -5330,6 +5338,7 @@ public final class Settings {
WIFI_NUM_OPEN_NETWORKS_KEPT,
EMERGENCY_TONE,
CALL_AUTO_RETRY,
+ DOCK_AUDIO_MEDIA_ENABLED
};
// Populated lazily, guarded by class object:
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index affeb9005018..4a2137461801 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -74,7 +74,7 @@ public class SearchManagerService extends ISearchManager.Stub {
mContext = context;
mContext.registerReceiver(new BootCompletedReceiver(),
new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
- mContext.registerReceiver(new UserReceiver(),
+ mContext.registerReceiver(new UserReceiver(),
new IntentFilter(Intent.ACTION_USER_REMOVED));
new MyPackageMonitor().register(context, null, UserHandle.ALL, true);
}
@@ -161,7 +161,8 @@ public class SearchManagerService extends ISearchManager.Stub {
}
// Inform all listeners that the list of searchables has been updated.
Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(intent, new UserHandle(changingUserId));
}
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 1cd3e05b2690..758abb5999c4 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -54,7 +54,9 @@ public final class Display {
private final DisplayManagerGlobal mGlobal;
private final int mDisplayId;
private final int mLayerStack;
- private final String mName;
+ private final int mFlags;
+ private final int mType;
+ private final String mAddress;
private final CompatibilityInfoHolder mCompatibilityInfo;
private DisplayInfo mDisplayInfo; // never null
@@ -141,6 +143,36 @@ public final class Display {
public static final int FLAG_SECURE = 1 << 1;
/**
+ * Display type: Unknown display type.
+ * @hide
+ */
+ public static final int TYPE_UNKNOWN = 0;
+
+ /**
+ * Display type: Built-in display.
+ * @hide
+ */
+ public static final int TYPE_BUILT_IN = 1;
+
+ /**
+ * Display type: HDMI display.
+ * @hide
+ */
+ public static final int TYPE_HDMI = 2;
+
+ /**
+ * Display type: WiFi display.
+ * @hide
+ */
+ public static final int TYPE_WIFI = 3;
+
+ /**
+ * Display type: Overlay display.
+ * @hide
+ */
+ public static final int TYPE_OVERLAY = 4;
+
+ /**
* Internal method to create a display.
* Applications should use {@link android.view.WindowManager#getDefaultDisplay()}
* or {@link android.hardware.display.DisplayManager#getDisplay}
@@ -154,10 +186,14 @@ public final class Display {
mGlobal = global;
mDisplayId = displayId;
mDisplayInfo = displayInfo;
- mLayerStack = displayInfo.layerStack; // can never change as long as the display is valid
- mName = displayInfo.name; // cannot change as long as the display is valid
mCompatibilityInfo = compatibilityInfo;
mIsValid = true;
+
+ // Cache properties that cannot change as long as the display is valid.
+ mLayerStack = displayInfo.layerStack;
+ mFlags = displayInfo.flags;
+ mType = displayInfo.type;
+ mAddress = displayInfo.address;
}
/**
@@ -228,10 +264,34 @@ public final class Display {
* @see #FLAG_SECURE
*/
public int getFlags() {
- synchronized (this) {
- updateDisplayInfoLocked();
- return mDisplayInfo.flags;
- }
+ return mFlags;
+ }
+
+ /**
+ * Gets the display type.
+ *
+ * @return The display type.
+ *
+ * @see #TYPE_UNKNOWN
+ * @see #TYPE_BUILT_IN
+ * @see #TYPE_HDMI
+ * @see #TYPE_WIFI
+ * @see #TYPE_OVERLAY
+ * @hide
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Gets the display address, or null if none.
+ * Interpretation varies by display type.
+ *
+ * @return The display address.
+ * @hide
+ */
+ public String getAddress() {
+ return mAddress;
}
/**
@@ -246,10 +306,17 @@ public final class Display {
/**
* Gets the name of the display.
+ * <p>
+ * Note that some displays may be renamed by the user.
+ * </p>
+ *
* @return The display's name.
*/
public String getName() {
- return mName;
+ synchronized (this) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.name;
+ }
}
/**
@@ -527,5 +594,25 @@ public final class Display {
+ ", " + mTempMetrics + ", isValid=" + mIsValid;
}
}
+
+ /**
+ * @hide
+ */
+ public static String typeToString(int type) {
+ switch (type) {
+ case TYPE_UNKNOWN:
+ return "UNKNOWN";
+ case TYPE_BUILT_IN:
+ return "BUILT_IN";
+ case TYPE_HDMI:
+ return "HDMI";
+ case TYPE_WIFI:
+ return "WIFI";
+ case TYPE_OVERLAY:
+ return "OVERLAY";
+ default:
+ return Integer.toString(type);
+ }
+ }
}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index ead5ff4649bc..f3841d5960d9 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -39,6 +39,17 @@ public final class DisplayInfo implements Parcelable {
public int flags;
/**
+ * Display type.
+ */
+ public int type;
+
+ /**
+ * Display address, or null if none.
+ * Interpretation varies by display type.
+ */
+ public String address;
+
+ /**
* The human-readable name of the display.
*/
public String name;
@@ -143,10 +154,12 @@ public final class DisplayInfo implements Parcelable {
public float physicalYDpi;
public static final Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
+ @Override
public DisplayInfo createFromParcel(Parcel source) {
return new DisplayInfo(source);
}
+ @Override
public DisplayInfo[] newArray(int size) {
return new DisplayInfo[size];
}
@@ -171,6 +184,9 @@ public final class DisplayInfo implements Parcelable {
public boolean equals(DisplayInfo other) {
return other != null
&& layerStack == other.layerStack
+ && flags == other.flags
+ && type == other.type
+ && Objects.equal(address, other.address)
&& Objects.equal(name, other.name)
&& appWidth == other.appWidth
&& appHeight == other.appHeight
@@ -195,6 +211,8 @@ public final class DisplayInfo implements Parcelable {
public void copyFrom(DisplayInfo other) {
layerStack = other.layerStack;
flags = other.flags;
+ type = other.type;
+ address = other.address;
name = other.name;
appWidth = other.appWidth;
appHeight = other.appHeight;
@@ -214,6 +232,8 @@ public final class DisplayInfo implements Parcelable {
public void readFromParcel(Parcel source) {
layerStack = source.readInt();
flags = source.readInt();
+ type = source.readInt();
+ address = source.readString();
name = source.readString();
appWidth = source.readInt();
appHeight = source.readInt();
@@ -234,6 +254,8 @@ public final class DisplayInfo implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(layerStack);
dest.writeInt(this.flags);
+ dest.writeInt(type);
+ dest.writeString(address);
dest.writeString(name);
dest.writeInt(appWidth);
dest.writeInt(appHeight);
@@ -294,7 +316,10 @@ public final class DisplayInfo implements Parcelable {
+ ", rotation " + rotation
+ ", density " + logicalDensityDpi
+ ", " + physicalXDpi + " x " + physicalYDpi + " dpi"
- + ", layerStack " + layerStack + flagsToString(flags) + "}";
+ + ", layerStack " + layerStack
+ + ", type " + Display.typeToString(type)
+ + ", address " + address
+ + flagsToString(flags) + "}";
}
private static String flagsToString(int flags) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f5e259ec322e..9d0d4f0221d7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1864,7 +1864,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Default horizontal layout direction.
- * @hide
*/
private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
@@ -1914,7 +1913,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Default text direction is inherited
*/
- public static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
+ private static final int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
/**
* Bit shift to get the horizontal layout direction. (bits after LAYOUT_DIRECTION_RESOLVED)
@@ -2024,7 +2023,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Default text alignment is inherited
*/
- public static int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
+ private static final int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
/**
* Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
@@ -3224,7 +3223,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mContext = context;
mResources = context != null ? context.getResources() : null;
mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
- // Set layout and text direction defaults
+ // Set some flags defaults
mPrivateFlags2 =
(LAYOUT_DIRECTION_DEFAULT << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT) |
(TEXT_DIRECTION_DEFAULT << PFLAG2_TEXT_DIRECTION_MASK_SHIFT) |
@@ -14198,11 +14197,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @hide
*/
protected void resolveDrawables() {
- if (mBackground != null) {
- mBackground.setLayoutDirection(getLayoutDirection());
+ if (canResolveLayoutDirection()) {
+ if (mBackground != null) {
+ mBackground.setLayoutDirection(getLayoutDirection());
+ }
+ mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
+ onResolveDrawables(getLayoutDirection());
}
- mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
- onResolveDrawables(getLayoutDirection());
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c252c77d5152..00723f3b91e3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3384,7 +3384,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if (child.isLayoutDirectionInherited()) {
child.resetRtlProperties();
- child.resolveRtlPropertiesIfNeeded();
}
onViewAdded(child);
@@ -4817,8 +4816,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
- final int layoutDirection = getLayoutDirection();
- lp.resolveLayoutDirection(layoutDirection);
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 19b825c4fa0f..495e46b051cb 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -144,6 +144,8 @@ public class Editor {
CharSequence mError;
boolean mErrorWasChanged;
ErrorPopup mErrorPopup;
+ private int mLastLayoutDirection = -1;
+
/**
* This flag is set if the TextView tries to display an error before it
* is attached to the window (so its position is still unknown).
@@ -288,23 +290,30 @@ public class Editor {
public void setError(CharSequence error, Drawable icon) {
mError = TextUtils.stringOrSpannedString(error);
mErrorWasChanged = true;
- final Drawables dr = mTextView.mDrawables;
- if (dr != null) {
- switch (mTextView.getLayoutDirection()) {
+ final int layoutDirection = mTextView.getLayoutDirection();
+ if (mLastLayoutDirection != layoutDirection) {
+ final Drawables dr = mTextView.mDrawables;
+ switch (layoutDirection) {
default:
case View.LAYOUT_DIRECTION_LTR:
- mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
- dr.mDrawableBottom);
+ if (dr != null) {
+ mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
+ dr.mDrawableBottom);
+ } else {
+ mTextView.setCompoundDrawables(null, null, icon, null);
+ }
break;
case View.LAYOUT_DIRECTION_RTL:
- mTextView.setCompoundDrawables(icon, dr.mDrawableTop, dr.mDrawableRight,
- dr.mDrawableBottom);
+ if (dr != null) {
+ mTextView.setCompoundDrawables(icon, dr.mDrawableTop, dr.mDrawableRight,
+ dr.mDrawableBottom);
+ } else {
+ mTextView.setCompoundDrawables(icon, null, null, null);
+ }
break;
}
- } else {
- mTextView.setCompoundDrawables(null, null, icon, null);
+ mLastLayoutDirection = layoutDirection;
}
-
if (mError == null) {
if (mErrorPopup != null) {
if (mErrorPopup.isShowing()) {
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index e15877640758..738f63bb9f4d 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -304,16 +304,11 @@ public class FrameLayout extends ViewGroup {
int maxWidth = 0;
int childState = 0;
- final int layoutDirection = getLayoutDirection();
-
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
- // measureChildWithMargins() has triggered layout params resolution, so no need
- // to do it now
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 83c15bbac452..b6f08623a38f 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -648,8 +648,6 @@ public class LinearLayout extends ViewGroup {
int largestChildHeight = Integer.MIN_VALUE;
- final int layoutDirection = getLayoutDirection();
-
// See how tall everyone is. Also remember max width.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -669,7 +667,6 @@ public class LinearLayout extends ViewGroup {
}
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
- lp.resolveLayoutDirection(layoutDirection);
totalWeight += lp.weight;
@@ -992,8 +989,6 @@ public class LinearLayout extends ViewGroup {
int largestChildWidth = Integer.MIN_VALUE;
- final int layoutDirection = getLayoutDirection();
-
// See how wide everyone is. Also remember max height.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -1014,7 +1009,6 @@ public class LinearLayout extends ViewGroup {
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
- lp.resolveLayoutDirection(layoutDirection);
totalWeight += lp.weight;
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index ace26f33584f..e52e84df3fa2 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -414,15 +414,12 @@ public class RelativeLayout extends ViewGroup {
final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
- final int layoutDirection = getLayoutDirection();
-
View[] views = mSortedHorizontalChildren;
int count = views.length;
for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) {
LayoutParams params = (LayoutParams) child.getLayoutParams();
- params.resolveLayoutDirection(layoutDirection);
applyHorizontalSizeRules(params, myWidth);
measureChildHorizontal(child, params, myWidth, myHeight);
@@ -486,6 +483,8 @@ public class RelativeLayout extends ViewGroup {
}
}
+ final int layoutDirection = getLayoutDirection();
+
if (isWrapContentWidth) {
// Width already has left padding in it since it was calculated by looking at
// the right of each child view
@@ -730,7 +729,6 @@ public class RelativeLayout extends ViewGroup {
final int layoutDirection = getLayoutDirection();
int[] rules = params.getRules(layoutDirection);
- params.resolveLayoutDirection(layoutDirection);
if (params.mLeft < 0 && params.mRight >= 0) {
// Right is fixed, but left varies
@@ -984,7 +982,6 @@ public class RelativeLayout extends ViewGroup {
if (child.getVisibility() != GONE) {
RelativeLayout.LayoutParams st =
(RelativeLayout.LayoutParams) child.getLayoutParams();
- st.resolveLayoutDirection(getLayoutDirection());
child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
}
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 1def89fe3cba..bc41931c366a 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -329,13 +329,11 @@ public class ScrollView extends FrameLayout {
return;
}
- final int layoutDirection = getLayoutDirection();
if (getChildCount() > 0) {
final View child = getChildAt(0);
int height = getMeasuredHeight();
if (child.getMeasuredHeight() < height) {
final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
- lp.resolveLayoutDirection(layoutDirection);
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index 68ffd733f6ad..35927e05df73 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -192,9 +192,7 @@ public class TableRow extends LinearLayout {
int widthMeasureSpec, int totalWidth,
int heightMeasureSpec, int totalHeight) {
if (mConstrainedColumnWidths != null) {
- final int layoutDirection = getLayoutDirection();
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- lp.resolveLayoutDirection(layoutDirection);
int measureMode = MeasureSpec.EXACTLY;
int columnWidth = 0;
@@ -228,6 +226,7 @@ public class TableRow extends LinearLayout {
final int childWidth = child.getMeasuredWidth();
lp.mOffset[LayoutParams.LOCATION_NEXT] = columnWidth - childWidth;
+ final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.LEFT:
@@ -293,13 +292,11 @@ public class TableRow extends LinearLayout {
}
final int[] columnWidths = mColumnWidths;
- final int layoutDirection = getLayoutDirection();
for (int i = 0; i < numColumns; i++) {
final View child = getVirtualChildAt(i);
if (child != null && child.getVisibility() != GONE) {
final LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
- layoutParams.resolveLayoutDirection(layoutDirection);
if (layoutParams.span == 1) {
int spec;
switch (layoutParams.width) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8e5ff40fac65..a46481c3c88e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -306,7 +306,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private Layout.Alignment mLayoutAlignment;
private int mResolvedTextAlignment;
- private boolean mResolvedDrawables;
+ private int mLastLayoutDirection = -1;
/**
* On some devices the fading edges add a performance penalty if used
@@ -8260,16 +8260,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public void onResolveDrawables(int layoutDirection) {
// No need to resolve twice
- if (mResolvedDrawables) {
+ if (mLastLayoutDirection == layoutDirection) {
return;
}
+ mLastLayoutDirection = layoutDirection;
// No drawable to resolve
if (mDrawables == null) {
return;
}
// No relative drawable to resolve
if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) {
- mResolvedDrawables = true;
return;
}
@@ -8307,7 +8307,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
break;
}
updateDrawablesLayoutDirection(dr, layoutDirection);
- mResolvedDrawables = true;
}
private void updateDrawablesLayoutDirection(Drawables dr, int layoutDirection) {
@@ -8329,7 +8328,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @hide
*/
protected void resetResolvedDrawables() {
- mResolvedDrawables = false;
+ mLastLayoutDirection = -1;
}
/**
diff --git a/core/java/com/android/internal/policy/IFaceLockInterface.aidl b/core/java/com/android/internal/policy/IFaceLockInterface.aidl
index 017801bbf886..a017722439d4 100644
--- a/core/java/com/android/internal/policy/IFaceLockInterface.aidl
+++ b/core/java/com/android/internal/policy/IFaceLockInterface.aidl
@@ -23,6 +23,7 @@ interface IFaceLockInterface {
void startUi(IBinder containingWindowToken, int x, int y, int width, int height,
boolean useLiveliness);
void stopUi();
+ void makeInvisible();
void registerCallback(IFaceLockCallback cb);
void unregisterCallback(IFaceLockCallback cb);
}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 43c63b6d07d0..8bc1081a3f97 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -343,11 +343,9 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
final int height = maxHeight - verticalPadding;
final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
- final int layoutDirection = getLayoutDirection();
if (mClose != null) {
availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
- lp.resolveLayoutDirection(layoutDirection);
availableWidth -= lp.leftMargin + lp.rightMargin;
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index d8b3d2ffef1e..6fb459c1b5ed 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -946,9 +946,6 @@ public class ActionBarView extends AbsActionBarView {
final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
(ActionBar.LayoutParams) lp : null;
- final int layoutDirection = getLayoutDirection();
- lp.resolveLayoutDirection(layoutDirection);
-
int horizontalMargin = 0;
int verticalMargin = 0;
if (ablp != null) {
@@ -1099,9 +1096,8 @@ public class ActionBarView extends AbsActionBarView {
customView = mCustomNavView;
}
if (customView != null) {
- ViewGroup.LayoutParams lp = customView.getLayoutParams();
final int layoutDirection = getLayoutDirection();
- lp.resolveLayoutDirection(layoutDirection);
+ ViewGroup.LayoutParams lp = customView.getLayoutParams();
final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
(ActionBar.LayoutParams) lp : null;
final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY;
@@ -1339,15 +1335,11 @@ public class ActionBarView extends AbsActionBarView {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
- // measureChildWithMargins() has triggered layout params resolution, so no need
- // to do it now
final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
mUpWidth = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin;
int width = mUpView.getVisibility() == GONE ? 0 : mUpWidth;
int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin;
measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0);
- // measureChildWithMargins() has triggered layout params resolution, so no need
- // to do it now
final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin;
height = Math.max(height,
@@ -1387,12 +1379,10 @@ public class ActionBarView extends AbsActionBarView {
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int vCenter = (b - t) / 2;
final boolean isLayoutRtl = isLayoutRtl();
- final int layoutDirection = getLayoutDirection();
final int width = getWidth();
int upOffset = 0;
if (mUpView.getVisibility() != GONE) {
final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
- upLp.resolveLayoutDirection(layoutDirection);
final int upHeight = mUpView.getMeasuredHeight();
final int upWidth = mUpView.getMeasuredWidth();
upOffset = upLp.leftMargin + upWidth + upLp.rightMargin;
@@ -1413,7 +1403,6 @@ public class ActionBarView extends AbsActionBarView {
}
final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
- iconLp.resolveLayoutDirection(layoutDirection);
final int iconHeight = mIconView.getMeasuredHeight();
final int iconWidth = mIconView.getMeasuredWidth();
final int hCenter = (r - l) / 2;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 3f40f202bb58..f8c3b4deb4ef 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -16,10 +16,6 @@
package com.android.internal.widget;
-import com.android.internal.R;
-import com.android.internal.telephony.ITelephony;
-import com.google.android.collect.Lists;
-
import android.app.ActivityManagerNative;
import android.app.admin.DevicePolicyManager;
import android.content.ContentResolver;
@@ -29,7 +25,6 @@ import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -43,6 +38,10 @@ import android.util.Log;
import android.view.View;
import android.widget.Button;
+import com.android.internal.R;
+import com.android.internal.telephony.ITelephony;
+import com.google.android.collect.Lists;
+
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@@ -125,6 +124,11 @@ public class LockPatternUtils {
*/
public static final int FLAG_BIOMETRIC_WEAK_LIVELINESS = 0x1;
+ /**
+ * Pseudo-appwidget id we use to represent the default clock status widget
+ */
+ public static final int ID_DEFAULT_STATUS_WIDGET = -2;
+
protected final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
protected final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
protected final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
@@ -1045,28 +1049,131 @@ public class LockPatternUtils {
}
}
- public int[] getUserDefinedWidgets() {
- int appWidgetId = -1;
+ public int[] getAppWidgets() {
String appWidgetIdString = Settings.Secure.getStringForUser(
- mContentResolver, Settings.Secure.LOCK_SCREEN_USER_SELECTED_APPWIDGET_ID,
+ mContentResolver, Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS,
UserHandle.USER_CURRENT);
- if (appWidgetIdString != null) {
- appWidgetId = (int) Integer.decode(appWidgetIdString);
+ String delims = ",";
+ if (appWidgetIdString != null && appWidgetIdString.length() > 0) {
+ String[] appWidgetStringIds = appWidgetIdString.split(delims);
+ int[] appWidgetIds = new int[appWidgetStringIds.length];
+ for (int i = 0; i < appWidgetStringIds.length; i++) {
+ String appWidget = appWidgetStringIds[i];
+ try {
+ appWidgetIds[i] = Integer.decode(appWidget);
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Error when parsing widget id " + appWidget);
+ return null;
+ }
+ }
+ return appWidgetIds;
+ }
+ if (appWidgetIdString == null) {
+ return new int[] { LockPatternUtils.ID_DEFAULT_STATUS_WIDGET };
+ } else {
+ return new int[0];
+ }
+ }
+
+ private static String combineStrings(int[] list, String separator) {
+ int listLength = list.length;
+
+ switch (listLength) {
+ case 0: {
+ return "";
+ }
+ case 1: {
+ return Integer.toString(list[0]);
+ }
+ }
+
+ int strLength = 0;
+ int separatorLength = separator.length();
+
+ String[] stringList = new String[list.length];
+ for (int i = 0; i < listLength; i++) {
+ stringList[i] = Integer.toString(list[i]);
+ strLength += stringList[i].length();
+ if (i < listLength - 1) {
+ strLength += separatorLength;
+ }
+ }
+
+ StringBuilder sb = new StringBuilder(strLength);
+
+ for (int i = 0; i < listLength; i++) {
+ sb.append(list[i]);
+ if (i < listLength - 1) {
+ sb.append(separator);
+ }
}
- return new int[] { appWidgetId };
+ return sb.toString();
}
- public int getStatusWidget() {
- int appWidgetId = -1;
- String appWidgetIdString = Settings.Secure.getStringForUser(
- mContentResolver, Settings.Secure.LOCK_SCREEN_STATUS_APPWIDGET_ID,
- UserHandle.USER_CURRENT);
- if (appWidgetIdString != null) {
- appWidgetId = (int) Integer.decode(appWidgetIdString);
+ private void writeAppWidgets(int[] appWidgetIds) {
+ Settings.Secure.putStringForUser(mContentResolver,
+ Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS,
+ combineStrings(appWidgetIds, ","),
+ UserHandle.USER_CURRENT);
+ }
+
+ // TODO: log an error if this returns false
+ public boolean addAppWidget(int widgetId, int index) {
+ int[] widgets = getAppWidgets();
+ if (widgets == null) {
+ return false;
+ }
+ if (index < 0 || index > widgets.length) {
+ return false;
+ }
+ int[] newWidgets = new int[widgets.length + 1];
+ for (int i = 0, j = 0; i < newWidgets.length; i++) {
+ if (index == i) {
+ newWidgets[i] = widgetId;
+ i++;
+ }
+ if (i < newWidgets.length) {
+ newWidgets[i] = widgets[j];
+ j++;
+ }
}
+ writeAppWidgets(newWidgets);
+ return true;
+ }
- return appWidgetId;
+ public boolean removeAppWidget(int widgetId) {
+ int[] widgets = getAppWidgets();
+
+ int[] newWidgets = new int[widgets.length - 1];
+ for (int i = 0, j = 0; i < widgets.length; i++) {
+ if (widgets[i] == widgetId) {
+ // continue...
+ } else if (j >= newWidgets.length) {
+ // we couldn't find the widget
+ return false;
+ } else {
+ newWidgets[j] = widgets[i];
+ j++;
+ }
+ }
+ writeAppWidgets(newWidgets);
+ return true;
+ }
+
+ public int getStickyAppWidgetIndex() {
+ return Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.LOCK_SCREEN_STICKY_APPWIDGET,
+ -1,
+ UserHandle.USER_CURRENT);
+ }
+
+ public void setStickyAppWidgetIndex(int value) {
+ Settings.Secure.putIntForUser(mContentResolver,
+ Settings.Secure.LOCK_SCREEN_STICKY_APPWIDGET,
+ value,
+ UserHandle.USER_CURRENT);
}
private long getLong(String secureSettingKey, long defaultValue) {
diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
index 0f49776330a2..209bd3cb1424 100644
--- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
@@ -116,6 +116,8 @@ public class GlowPadView extends View {
private float mOuterRadius = 0.0f;
private float mSnapMargin = 0.0f;
+ private float mFirstItemOffset = 0.0f;
+ private boolean mMagneticTargets = false;
private boolean mDragging;
private int mNewTargetResources;
@@ -212,6 +214,9 @@ public class GlowPadView extends View {
mInnerRadius = a.getDimension(R.styleable.GlowPadView_innerRadius, mInnerRadius);
mOuterRadius = a.getDimension(R.styleable.GlowPadView_outerRadius, mOuterRadius);
mSnapMargin = a.getDimension(R.styleable.GlowPadView_snapMargin, mSnapMargin);
+ mFirstItemOffset = (float) Math.toRadians(
+ a.getFloat(R.styleable.GlowPadView_firstItemOffset,
+ (float) Math.toDegrees(mFirstItemOffset)));
mVibrationDuration = a.getInt(R.styleable.GlowPadView_vibrationDuration,
mVibrationDuration);
mFeedbackCount = a.getInt(R.styleable.GlowPadView_feedbackCount,
@@ -223,6 +228,7 @@ public class GlowPadView extends View {
getResourceId(a, R.styleable.GlowPadView_outerRingDrawable));
mAlwaysTrackFinger = a.getBoolean(R.styleable.GlowPadView_alwaysTrackFinger, false);
+ mMagneticTargets = a.getBoolean(R.styleable.GlowPadView_magneticTargets, mMagneticTargets);
int pointId = getResourceId(a, R.styleable.GlowPadView_pointDrawable);
Drawable pointDrawable = pointId != 0 ? res.getDrawable(pointId) : null;
@@ -256,11 +262,8 @@ public class GlowPadView extends View {
setDirectionDescriptionsResourceId(resourceId);
}
- a.recycle();
+ mGravity = a.getInt(R.styleable.GlowPadView_gravity, Gravity.TOP);
- // Use gravity attribute from LinearLayout
- a = context.obtainStyledAttributes(attrs, android.R.styleable.LinearLayout);
- mGravity = a.getInt(android.R.styleable.LinearLayout_gravity, Gravity.TOP);
a.recycle();
setVibrateEnabled(mVibrationDuration > 0);
@@ -823,6 +826,7 @@ public class GlowPadView extends View {
int ntargets = targets.size();
float x = 0.0f;
float y = 0.0f;
+ float activeAngle = 0.0f;
int actionIndex = event.findPointerIndex(mPointerId);
if (actionIndex == -1) {
@@ -855,15 +859,18 @@ public class GlowPadView extends View {
for (int i = 0; i < ntargets; i++) {
TargetDrawable target = targets.get(i);
- double targetMinRad = (i - 0.5) * 2 * Math.PI / ntargets;
- double targetMaxRad = (i + 0.5) * 2 * Math.PI / ntargets;
+ double targetMinRad = mFirstItemOffset + (i - 0.5) * 2 * Math.PI / ntargets;
+ double targetMaxRad = mFirstItemOffset + (i + 0.5) * 2 * Math.PI / ntargets;
if (target.isEnabled()) {
boolean angleMatches =
(angleRad > targetMinRad && angleRad <= targetMaxRad) ||
(angleRad + 2 * Math.PI > targetMinRad &&
- angleRad + 2 * Math.PI <= targetMaxRad);
+ angleRad + 2 * Math.PI <= targetMaxRad) ||
+ (angleRad - 2 * Math.PI > targetMinRad &&
+ angleRad - 2 * Math.PI <= targetMaxRad);
if (angleMatches && (dist2(tx, ty) > snapDistance2)) {
activeTarget = i;
+ activeAngle = (float) -angleRad;
}
}
}
@@ -891,6 +898,9 @@ public class GlowPadView extends View {
if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
target.setState(TargetDrawable.STATE_INACTIVE);
}
+ if (mMagneticTargets) {
+ updateTargetPosition(mActiveTarget, mWaveCenterX, mWaveCenterY);
+ }
}
// Focus the new target
if (activeTarget != -1) {
@@ -898,6 +908,9 @@ public class GlowPadView extends View {
if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
target.setState(TargetDrawable.STATE_FOCUSED);
}
+ if (mMagneticTargets) {
+ updateTargetPosition(activeTarget, mWaveCenterX, mWaveCenterY, activeAngle);
+ }
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
String targetContentDescription = getTargetDescription(activeTarget);
announceForAccessibility(targetContentDescription);
@@ -1042,14 +1055,15 @@ public class GlowPadView extends View {
if (DEBUG) dump();
}
- private void updateTargetPositions(float centerX, float centerY) {
- // Reposition the target drawables if the view changed.
- ArrayList<TargetDrawable> targets = mTargetDrawables;
- final int size = targets.size();
- final float alpha = (float) (-2.0f * Math.PI / size);
- for (int i = 0; i < size; i++) {
+ private void updateTargetPosition(int i, float centerX, float centerY) {
+ final float angle = getAngle(getSliceAngle(), i);
+ updateTargetPosition(i, centerX, centerY, angle);
+ }
+
+ private void updateTargetPosition(int i, float centerX, float centerY, float angle) {
+ if (i >= 0) {
+ ArrayList<TargetDrawable> targets = mTargetDrawables;
final TargetDrawable targetIcon = targets.get(i);
- final float angle = alpha * i;
targetIcon.setPositionX(centerX);
targetIcon.setPositionY(centerY);
targetIcon.setX(mOuterRadius * (float) Math.cos(angle));
@@ -1057,6 +1071,29 @@ public class GlowPadView extends View {
}
}
+ private void updateTargetPositions(float centerX, float centerY) {
+ updateTargetPositions(centerX, centerY, false);
+ }
+
+ private void updateTargetPositions(float centerX, float centerY, boolean skipActive) {
+ final int size = mTargetDrawables.size();
+ final float alpha = getSliceAngle();
+ // Reposition the target drawables if the view changed.
+ for (int i = 0; i < size; i++) {
+ if (!skipActive || i != mActiveTarget) {
+ updateTargetPosition(i, centerX, centerY, getAngle(alpha, i));
+ }
+ }
+ }
+
+ private float getAngle(float alpha, int i) {
+ return mFirstItemOffset + alpha * i;
+ }
+
+ private float getSliceAngle() {
+ return (float) (-2.0f * Math.PI / mTargetDrawables.size());
+ }
+
private void updatePointCloudPosition(float centerX, float centerY) {
mPointCloud.setCenter(centerX, centerY);
}
diff --git a/core/res/res/anim/keyguard_security_fade_in.xml b/core/res/res/anim/keyguard_security_fade_in.xml
index 7d5516ad4703..6293432c7ccc 100644
--- a/core/res/res/anim/keyguard_security_fade_in.xml
+++ b/core/res/res/anim/keyguard_security_fade_in.xml
@@ -15,8 +15,8 @@
-->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@interpolator/decelerate_quad"
+ android:interpolator="@android:interpolator/decelerate_quad"
android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@integer/kg_security_fade_duration" />
+ android:duration="@*android:integer/kg_security_fade_duration" />
diff --git a/core/res/res/anim/keyguard_security_fade_out.xml b/core/res/res/anim/keyguard_security_fade_out.xml
index 08c8b2ba7e16..4ab0229e2092 100644
--- a/core/res/res/anim/keyguard_security_fade_out.xml
+++ b/core/res/res/anim/keyguard_security_fade_out.xml
@@ -13,9 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<alpha xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@interpolator/accelerate_quad"
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/accelerate_quad"
android:fromAlpha="1.0"
android:toAlpha="0.0"
- android:duration="@integer/kg_security_fade_duration"
+ android:duration="@*android:integer/kg_security_fade_duration"
/>
diff --git a/core/res/res/drawable-hdpi/kg_add_widget.png b/core/res/res/drawable-hdpi/kg_add_widget.png
new file mode 100644
index 000000000000..7456705b402f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/kg_add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_bouncer_bg_white.9.png b/core/res/res/drawable-hdpi/kg_bouncer_bg_white.9.png
new file mode 100644
index 000000000000..cfd5db357dc4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/kg_bouncer_bg_white.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_security_grip.9.png b/core/res/res/drawable-hdpi/kg_security_grip.9.png
new file mode 100644
index 000000000000..1e40aef33699
--- /dev/null
+++ b/core/res/res/drawable-hdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/kg_security_lock.png b/core/res/res/drawable-hdpi/kg_security_lock.png
new file mode 100644
index 000000000000..c3c94c464bf8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/kg_security_lock.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-hdpi/sym_keyboard_return_holo.png
new file mode 100644
index 000000000000..f1bcf487c5f1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_add_widget.png b/core/res/res/drawable-mdpi/kg_add_widget.png
new file mode 100644
index 000000000000..1cab0d9328fb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/kg_add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_bouncer_bg_white.9.png b/core/res/res/drawable-mdpi/kg_bouncer_bg_white.9.png
new file mode 100644
index 000000000000..e3cb6db09736
--- /dev/null
+++ b/core/res/res/drawable-mdpi/kg_bouncer_bg_white.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_security_grip.9.png b/core/res/res/drawable-mdpi/kg_security_grip.9.png
new file mode 100644
index 000000000000..334a39b5c9a2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/kg_security_lock.png b/core/res/res/drawable-mdpi/kg_security_lock.png
new file mode 100644
index 000000000000..a39f38002e5d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/kg_security_lock.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-mdpi/sym_keyboard_return_holo.png
new file mode 100644
index 000000000000..d5a7708ca082
--- /dev/null
+++ b/core/res/res/drawable-mdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-hdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-sw600dp-hdpi/sym_keyboard_return_holo.png
new file mode 100644
index 000000000000..f1bcf487c5f1
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-hdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-mdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-sw600dp-mdpi/sym_keyboard_return_holo.png
new file mode 100644
index 000000000000..d5a7708ca082
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-mdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-xhdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-sw600dp-xhdpi/sym_keyboard_return_holo.png
new file mode 100644
index 000000000000..55174e076765
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-xhdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/default_wallpaper.jpg b/core/res/res/drawable-xhdpi/default_wallpaper.jpg
index 5b8d1d59833e..da9fa91fae70 100644
--- a/core/res/res/drawable-xhdpi/default_wallpaper.jpg
+++ b/core/res/res/drawable-xhdpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_add_widget.png b/core/res/res/drawable-xhdpi/kg_add_widget.png
new file mode 100644
index 000000000000..d71905f8ff78
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/kg_add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_bouncer_bg_white.9.png b/core/res/res/drawable-xhdpi/kg_bouncer_bg_white.9.png
new file mode 100644
index 000000000000..b9e30e20a952
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/kg_bouncer_bg_white.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_security_grip.9.png b/core/res/res/drawable-xhdpi/kg_security_grip.9.png
new file mode 100644
index 000000000000..c33b9d3471bc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/kg_security_grip.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/kg_security_lock.png b/core/res/res/drawable-xhdpi/kg_security_lock.png
new file mode 100644
index 000000000000..81d577e328d6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/kg_security_lock.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/sym_keyboard_return_holo.png b/core/res/res/drawable-xhdpi/sym_keyboard_return_holo.png
new file mode 100644
index 000000000000..55174e076765
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/sym_keyboard_return_holo.png
Binary files differ
diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml
index 521853f4174f..bb455bdecbba 100644
--- a/core/res/res/layout-land/keyguard_host_view.xml
+++ b/core/res/res/layout-land/keyguard_host_view.xml
@@ -21,30 +21,53 @@
and the security view. -->
<com.android.internal.policy.impl.keyguard.KeyguardHostView
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/android"
android:id="@+id/keyguard_host_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center_vertical"
android:orientation="horizontal">
- <include layout="@layout/keyguard_widget_region"
- android:layout_width="0dp"
+ <com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout
+ android:id="@+id/multi_pane_challenge"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_weight="@integer/kg_widget_region_weight" />
+ android:clipChildren="false">
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
- android:id="@+id/view_flipper"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="@integer/kg_security_flipper_weight"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingLeft="@dimen/keyguard_security_view_margin"
- android:paddingTop="@dimen/keyguard_security_view_margin"
- android:paddingRight="@dimen/keyguard_security_view_margin"
- android:paddingBottom="@dimen/keyguard_security_view_margin"
- android:gravity="center">
+ <include layout="@layout/keyguard_widget_pager"
+ android:id="@+id/app_widget_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_centerWithinArea="0.55"
+ androidprv:layout_childType="widget"
+ androidprv:layout_maxWidth="480dp"
+ androidprv:layout_maxHeight="480dp" />
+ <include layout="@layout/keyguard_multi_user_selector"/>
+
+ <View android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_childType="scrim"
+ android:background="#99000000" />
- </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+ <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer
+ android:id="@+id/keyguard_security_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ androidprv:layout_childType="challenge"
+ androidprv:layout_centerWithinArea="0.55">
+ <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
+ android:id="@+id/view_flipper"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingLeft="@dimen/keyguard_security_view_margin"
+ android:paddingTop="@dimen/keyguard_security_view_margin"
+ android:paddingRight="@dimen/keyguard_security_view_margin"
+ android:paddingBottom="@dimen/keyguard_security_view_margin"
+ android:gravity="center">
+ </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+ </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>
+ </com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout>
</com.android.internal.policy.impl.keyguard.KeyguardHostView>
+
diff --git a/core/res/res/layout/keyguard_navigation.xml b/core/res/res/layout-land/keyguard_widget_pager.xml
index 8230c523c103..975288fa4cc2 100644
--- a/core/res/res/layout/keyguard_navigation.xml
+++ b/core/res/res/layout-land/keyguard_widget_pager.xml
@@ -16,6 +16,16 @@
** limitations under the License.
*/
-->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <include layout="@layout/empty_navigation" />
-</merge>
+
+<!-- This is the selector widget that allows the user to select an action. -->
+<com.android.internal.policy.impl.keyguard.KeyguardWidgetCarousel
+ xmlns:androidprv="http://schemas.android.com/apk/res/android"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingLeft="25dp"
+ android:paddingRight="25dp"
+ android:paddingTop="25dp"
+ android:paddingBottom="25dp"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ androidprv:pageSpacing="10dp">
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetCarousel> \ No newline at end of file
diff --git a/core/res/res/layout-port/keyguard_host_view.xml b/core/res/res/layout-port/keyguard_host_view.xml
index 981fe6d0da55..ed55e61ce5b2 100644
--- a/core/res/res/layout-port/keyguard_host_view.xml
+++ b/core/res/res/layout-port/keyguard_host_view.xml
@@ -21,28 +21,55 @@
and the security view. -->
<com.android.internal.policy.impl.keyguard.KeyguardHostView
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/android"
android:id="@+id/keyguard_host_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
- <include layout="@layout/keyguard_widget_region"
+ <com.android.internal.policy.impl.keyguard.SlidingChallengeLayout
+ android:id="@+id/sliding_layout"
android:layout_width="match_parent"
- android:layout_height="153dp" />
+ android:layout_height="match_parent"
+ androidprv:dragIcon="@drawable/kg_security_lock">
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
- android:id="@+id/view_flipper"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_weight="1"
- android:paddingLeft="@dimen/keyguard_security_view_margin"
- android:paddingTop="@dimen/keyguard_security_view_margin"
- android:paddingRight="@dimen/keyguard_security_view_margin"
- android:paddingBottom="@dimen/keyguard_security_view_margin"
- android:gravity="center">
- </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <include layout="@layout/keyguard_widget_pager"
+ android:id="@+id/app_widget_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <View android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_childType="scrim"
+ android:background="#99000000" />
+
+ <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer
+ android:id="@+id/keyguard_security_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ androidprv:layout_childType="challenge"
+ android:layout_marginLeft="@dimen/kg_edge_swipe_region_size"
+ android:layout_marginRight="@dimen/kg_edge_swipe_region_size"
+ android:background="@drawable/kg_bouncer_bg_white"
+ android:gravity="bottom|center_horizontal">
+ <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
+ android:id="@+id/view_flipper"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingLeft="@dimen/keyguard_security_view_margin"
+ android:paddingTop="@dimen/keyguard_security_view_margin"
+ android:paddingRight="@dimen/keyguard_security_view_margin"
+ android:gravity="center">
+ </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+ </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>
+ </com.android.internal.policy.impl.keyguard.SlidingChallengeLayout>
</com.android.internal.policy.impl.keyguard.KeyguardHostView>
diff --git a/core/res/res/layout-port/keyguard_widget_pager.xml b/core/res/res/layout-port/keyguard_widget_pager.xml
new file mode 100644
index 000000000000..3b29db8516e6
--- /dev/null
+++ b/core/res/res/layout-port/keyguard_widget_pager.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the selector widget that allows the user to select an action. -->
+<com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
+ xmlns:androidprv="http://schemas.android.com/apk/res/android"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/app_widget_container"
+ android:paddingLeft="25dp"
+ android:paddingRight="25dp"
+ android:paddingTop="25dp"
+ android:paddingBottom="64dp"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ androidprv:pageSpacing="10dp">
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
diff --git a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
index 23c1e9cb28a9..3953c95139e6 100644
--- a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
+++ b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
@@ -21,33 +21,54 @@
and the security view. -->
<com.android.internal.policy.impl.keyguard.KeyguardHostView
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/android"
android:id="@+id/keyguard_host_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical">
+ android:orientation="horizontal">
- <include layout="@layout/keyguard_widget_region"
+ <com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout
+ android:id="@+id/multi_pane_challenge"
android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="@integer/kg_widget_region_weight" />
-
- <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
- android:id="@+id/view_flipper"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="@integer/kg_security_flipper_weight"
+ android:layout_height="match_parent"
android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingLeft="@dimen/keyguard_security_view_margin"
- android:paddingTop="@dimen/keyguard_security_view_margin"
- android:paddingRight="@dimen/keyguard_security_view_margin"
- android:paddingBottom="@dimen/keyguard_security_view_margin"
- android:layout_gravity="center">
+ android:orientation="vertical">
+ <include layout="@layout/keyguard_widget_pager"
+ android:id="@+id/app_widget_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_centerWithinArea="0.55"
+ androidprv:layout_childType="widget"
+ androidprv:layout_maxWidth="480dp"
+ androidprv:layout_maxHeight="480dp" />
+ <include layout="@layout/keyguard_multi_user_selector"/>
- </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+ <View android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ androidprv:layout_childType="scrim"
+ android:background="#99000000" />
-</com.android.internal.policy.impl.keyguard.KeyguardHostView>
+ <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer
+ android:id="@+id/keyguard_security_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ androidprv:layout_childType="challenge"
+ android:layout_gravity="center_horizontal|bottom">
+ <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
+ android:id="@+id/view_flipper"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:paddingLeft="@dimen/keyguard_security_view_margin"
+ android:paddingTop="@dimen/keyguard_security_view_margin"
+ android:paddingRight="@dimen/keyguard_security_view_margin"
+ android:paddingBottom="@dimen/keyguard_security_view_margin"
+ android:gravity="center">
+ </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
+ </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>
+ </com.android.internal.policy.impl.keyguard.MultiPaneChallengeLayout>
+</com.android.internal.policy.impl.keyguard.KeyguardHostView>
diff --git a/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml b/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml
index 1eef099e2985..930b14e925ec 100644
--- a/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml
+++ b/core/res/res/layout-sw600dp/keyguard_glow_pad_container.xml
@@ -18,7 +18,7 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<include layout="@layout/keyguard_glow_pad_view"
- android:layout_width="@dimen/kg_glow_pad_size"
- android:layout_height="@dimen/kg_glow_pad_size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center" />
</merge> \ No newline at end of file
diff --git a/core/res/res/layout/keyguard_account_view.xml b/core/res/res/layout/keyguard_account_view.xml
index d1f9225ece83..78adfe754917 100644
--- a/core/res/res/layout/keyguard_account_view.xml
+++ b/core/res/res/layout/keyguard_account_view.xml
@@ -23,7 +23,9 @@
android:layout_height="match_parent"
android:orientation="vertical">
- <include layout="@layout/keyguard_sim_puk_pin_account_navigation"/>
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="match_parent"
diff --git a/core/res/res/layout/keyguard_add_widget.xml b/core/res/res/layout/keyguard_add_widget.xml
new file mode 100644
index 000000000000..fd5e565faa0f
--- /dev/null
+++ b/core/res/res/layout/keyguard_add_widget.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2009, 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.
+*/
+-->
+
+<!-- This is a view that shows general status information in Keyguard. -->
+<com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_add_widget"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+ <ImageView
+ android:id="@+id/keyguard_add_widget_view"
+ android:clickable="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:padding="24dp"
+ android:src="@drawable/kg_add_widget" />
+ </FrameLayout>
+</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
diff --git a/core/res/res/layout/keyguard_emergency_carrier_area.xml b/core/res/res/layout/keyguard_emergency_carrier_area.xml
index f9a593fd5038..655b77dbf718 100644
--- a/core/res/res/layout/keyguard_emergency_carrier_area.xml
+++ b/core/res/res/layout/keyguard_emergency_carrier_area.xml
@@ -36,16 +36,39 @@
android:textSize="@dimen/kg_status_line_font_size"
android:textColor="?android:attr/textColorSecondary"/>
- <com.android.internal.policy.impl.keyguard.EmergencyButton
- android:id="@+id/emergency_call_button"
- android:layout_width="wrap_content"
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:drawableLeft="@*android:drawable/lockscreen_emergency_button"
- android:text="@string/kg_emergency_call_label"
- style="?android:attr/buttonBarButtonStyle"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"
- android:drawablePadding="8dip" />
+ android:layout_marginTop="-10dip"
+ style="?android:attr/buttonBarStyle"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:weightSum="2">
+
+ <com.android.internal.policy.impl.keyguard.EmergencyButton
+ android:id="@+id/emergency_call_button"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableLeft="@*android:drawable/lockscreen_emergency_button"
+ android:text="@string/kg_emergency_call_label"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@dimen/kg_status_line_font_size"
+ android:textColor="?android:attr/textColorSecondary"
+ android:drawablePadding="8dip" />
+
+ <Button android:id="@+id/forgot_password_button"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableLeft="@*android:drawable/lockscreen_forgot_password_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="@dimen/kg_status_line_font_size"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:drawablePadding="8dip"
+ android:visibility="gone"/>
+ </LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml b/core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml
deleted file mode 100644
index 68840abc051b..000000000000
--- a/core/res/res/layout/keyguard_emergency_carrier_area_and_recovery.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center"
- android:layout_gravity="center_horizontal"
- android:layout_alignParentBottom="true">
-
- <com.android.internal.policy.impl.keyguard.CarrierText
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="?android:attr/buttonBarStyle"
- android:orientation="horizontal"
- android:gravity="center"
- android:weightSum="2">
-
- <com.android.internal.policy.impl.keyguard.EmergencyButton
- android:id="@+id/emergency_call_button"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:drawableLeft="@*android:drawable/lockscreen_emergency_button"
- android:text="@string/kg_emergency_call_label"
- style="?android:attr/buttonBarButtonStyle"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"
- android:drawablePadding="8dip" />
-
- <Button android:id="@+id/forgot_password_button"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:drawableLeft="@drawable/lockscreen_forgot_password_button"
- style="?android:attr/buttonBarButtonStyle"
- android:textSize="@dimen/kg_status_line_font_size"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:drawablePadding="8dip"
- android:visibility="gone"/>
- </LinearLayout>
-
-</LinearLayout>
diff --git a/core/res/res/layout/keyguard_face_unlock_view.xml b/core/res/res/layout/keyguard_face_unlock_view.xml
index 845c265fdb29..ae7984c78333 100644
--- a/core/res/res/layout/keyguard_face_unlock_view.xml
+++ b/core/res/res/layout/keyguard_face_unlock_view.xml
@@ -25,7 +25,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <include layout="@layout/keyguard_navigation"/>
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
<RelativeLayout
android:id="@+id/face_unlock_area_view"
@@ -39,7 +42,7 @@
android:id="@+id/spotlightMask"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/facelock_spotlight_mask"
+ android:background="@*android:color/facelock_spotlight_mask"
/>
<ImageButton
@@ -50,7 +53,7 @@
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:background="#00000000"
- android:src="@drawable/ic_facial_backup"
+ android:src="@*android:drawable/ic_facial_backup"
/>
</RelativeLayout>
diff --git a/core/res/res/layout/keyguard_glow_pad_view.xml b/core/res/res/layout/keyguard_glow_pad_view.xml
index 509e77b95bb1..a416a777cefd 100644
--- a/core/res/res/layout/keyguard_glow_pad_view.xml
+++ b/core/res/res/layout/keyguard_glow_pad_view.xml
@@ -29,14 +29,16 @@
android:gravity="@integer/kg_selector_gravity"
android:focusable="true"
- prvandroid:targetDrawables="@*android:array/lockscreen_targets_with_camera"
- prvandroid:targetDescriptions="@*android:array/lockscreen_target_descriptions_with_camera"
+ prvandroid:targetDrawables="@array/lockscreen_targets_unlock_only"
+ prvandroid:targetDescriptions="@array/lockscreen_target_descriptions_unlock_only"
prvandroid:directionDescriptions="@*android:array/lockscreen_direction_descriptions"
prvandroid:handleDrawable="@*android:drawable/ic_lockscreen_handle"
prvandroid:outerRingDrawable="@*android:drawable/ic_lockscreen_outerring"
prvandroid:outerRadius="@*android:dimen/glowpadview_target_placement_radius"
prvandroid:innerRadius="@*android:dimen/glowpadview_inner_radius"
prvandroid:snapMargin="@*android:dimen/glowpadview_snap_margin"
+ prvandroid:firstItemOffset="@integer/kg_glowpad_rotation_offset"
+ prvandroid:magneticTargets="true"
prvandroid:feedbackCount="1"
prvandroid:vibrationDuration="20"
prvandroid:glowRadius="@*android:dimen/glowpadview_glow_radius"
diff --git a/core/res/res/layout/keyguard_message_area.xml b/core/res/res/layout/keyguard_message_area.xml
new file mode 100644
index 000000000000..9f11a2c4705f
--- /dev/null
+++ b/core/res/res/layout/keyguard_message_area.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
+<com.android.internal.policy.impl.keyguard.KeyguardMessageArea
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:id="@+id/keyguard_message_area"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearance"
+ android:textSize="@dimen/kg_status_line_font_size"
+ android:textColor="?android:attr/textColorSecondary" />
+
diff --git a/core/res/res/layout/keyguard_message_area_large.xml b/core/res/res/layout/keyguard_message_area_large.xml
new file mode 100644
index 000000000000..584cec4e5a47
--- /dev/null
+++ b/core/res/res/layout/keyguard_message_area_large.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
+<com.android.internal.policy.impl.keyguard.KeyguardMessageArea
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:id="@+id/keyguard_message_area"
+ android:maxLines="4"
+ android:textAppearance="?android:attr/textAppearance"
+ android:textSize="@dimen/kg_status_line_font_size"
+ android:textColor="?android:attr/textColorSecondary" />
+
diff --git a/core/res/res/layout/keyguard_multi_user_avatar.xml b/core/res/res/layout/keyguard_multi_user_avatar.xml
index 0e851e378c87..2d8f02d6aa09 100644
--- a/core/res/res/layout/keyguard_multi_user_avatar.xml
+++ b/core/res/res/layout/keyguard_multi_user_avatar.xml
@@ -20,35 +20,26 @@
<!-- This is a view that shows general status information in Keyguard. -->
<com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="125dp"
- android:layout_height="125dp"
- android:background="#000000"
+ android:layout_width="@dimen/keyguard_avatar_size"
+ android:layout_height="@dimen/keyguard_avatar_size"
+ android:background="#00000000"
android:gravity="center_horizontal">
<ImageView
android:id="@+id/keyguard_user_avatar"
- android:scaleType="centerCrop"
+ android:scaleType="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"/>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="0.78" />
- <TextView
- android:id="@+id/keyguard_user_name"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="0.22"
- android:paddingLeft="6dp"
- android:layout_gravity="center_vertical|left"
- android:textSize="16sp"
- android:textColor="#ffffff"
- android:singleLine="true"
- android:ellipsize="end"
- android:background="#808080" />
- </LinearLayout>
-</com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar> \ No newline at end of file
+ <TextView
+ android:id="@+id/keyguard_user_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:gravity="center"
+ android:textSize="@dimen/keyguard_avatar_name_size"
+ android:textColor="#ffffff"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:paddingLeft="2dp"
+ android:paddingRight="2dp" />
+</com.android.internal.policy.impl.keyguard.KeyguardMultiUserAvatar>
diff --git a/core/res/res/layout/keyguard_multi_user_selector.xml b/core/res/res/layout/keyguard_multi_user_selector.xml
index 5a6e9989c140..ee01285062a1 100644
--- a/core/res/res/layout/keyguard_multi_user_selector.xml
+++ b/core/res/res/layout/keyguard_multi_user_selector.xml
@@ -17,18 +17,23 @@
*/
-->
<com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView
+ xmlns:androidprv="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
+ androidprv:layout_childType="userSwitcher"
+ android:id="@+id/keyguard_user_selector"
android:orientation="horizontal"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:contentDescription="@string/keyguard_accessibility_user_selector">
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:contentDescription="@*android:string/keyguard_accessibility_user_selector"
+ android:visibility="gone">
- <com.android.internal.policy.impl.keyguard.KeyguardSubdivisionLayout
+ <com.android.internal.policy.impl.keyguard.KeyguardLinearLayout
android:id="@+id/keyguard_users_grid"
android:orientation="horizontal"
- android:layout_width="300dp"
- android:layout_height="300dp"
- android:layout_gravity="center" />
+ android:layout_width="wrap_content"
+ android:layout_marginBottom="@dimen/keyguard_muliuser_selector_margin"
+ android:layout_height="@dimen/keyguard_avatar_size"
+ android:layout_gravity="center|bottom" />
-</com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView> \ No newline at end of file
+</com.android.internal.policy.impl.keyguard.KeyguardMultiUserSelectorView>
diff --git a/core/res/res/layout/keyguard_multi_user_selector_widget.xml b/core/res/res/layout/keyguard_multi_user_selector_widget.xml
index ad9fdfee4849..fc126fec9a1c 100644
--- a/core/res/res/layout/keyguard_multi_user_selector_widget.xml
+++ b/core/res/res/layout/keyguard_multi_user_selector_widget.xml
@@ -23,7 +23,4 @@
android:id="@+id/keyguard_multi_user_selector"
android:layout_width="match_parent"
android:layout_height="match_parent">
-
- <include layout="@layout/keyguard_multi_user_selector"/>
-
</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame> \ No newline at end of file
diff --git a/core/res/res/layout/keyguard_password_view.xml b/core/res/res/layout/keyguard_password_view.xml
index 81916f73cb09..a184415919f6 100644
--- a/core/res/res/layout/keyguard_password_view.xml
+++ b/core/res/res/layout/keyguard_password_view.xml
@@ -29,6 +29,11 @@
android:layout_height="0dp"
android:layout_weight="1">
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -36,14 +41,6 @@
android:layout_gravity="center">
<LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:orientation="vertical"
- android:gravity="center">
- <include layout="@layout/keyguard_navigation"/>
- </LinearLayout>
-
- <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -77,18 +74,6 @@
android:imeOptions="flagForceAscii|actionDone"
/>
- <!-- This delete button is only visible for numeric PIN entry -->
- <ImageButton android:id="@+id/delete_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:src="@*android:drawable/ic_input_delete"
- android:clickable="true"
- android:padding="8dip"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone"
- />
-
<ImageView android:id="@+id/switch_ime_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -101,20 +86,6 @@
/>
</LinearLayout>
-
- <!-- Numeric keyboard -->
- <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="4dip"
- android:layout_marginEnd="4dip"
- android:paddingTop="4dip"
- android:paddingBottom="4dip"
- android:background="#40000000"
- android:keyBackground="@*android:drawable/btn_keyboard_key_ics"
- android:visibility="gone"
- android:clickable="true"
- />
</LinearLayout>
</LinearLayout>
</FrameLayout>
diff --git a/core/res/res/layout/keyguard_pattern_view.xml b/core/res/res/layout/keyguard_pattern_view.xml
index bec03b41173d..0f6256a20ad9 100644
--- a/core/res/res/layout/keyguard_pattern_view.xml
+++ b/core/res/res/layout/keyguard_pattern_view.xml
@@ -32,14 +32,16 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:orientation="vertical"
android:layout_gravity="center">
- <include layout="@layout/keyguard_navigation"/>
-
<!-- We need MATCH_PARENT here only to force the size of the parent to be passed to
the pattern view for it to compute its size. This is an unusual case, caused by
LockPatternView's requirement to maintain a square aspect ratio based on the width
@@ -55,7 +57,7 @@
android:layout_gravity="center_horizontal"
android:gravity="center" />
- <include layout="@layout/keyguard_emergency_carrier_area_and_recovery"
+ <include layout="@layout/keyguard_emergency_carrier_area"
android:id="@+id/keyguard_selector_fade_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout/keyguard_pin_view.xml b/core/res/res/layout/keyguard_pin_view.xml
new file mode 100644
index 000000000000..d62570b4a857
--- /dev/null
+++ b/core/res/res/layout/keyguard_pin_view.xml
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.internal.policy.impl.keyguard.KeyguardPINView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_pin_view"
+ android:layout_width="350dp"
+ android:layout_height="350dp"
+ android:orientation="vertical"
+ >
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ >
+ <TextView android:id="@+id/passwordEntry"
+ android:editable="true"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left"
+ android:singleLine="true"
+ android:cursorVisible="false"
+ android:background="@null"
+ android:textAppearance="@android:style/TextAppearance.NumPadKey"
+ android:imeOptions="flagForceAscii|actionDone"
+ />
+ <ImageButton android:id="@+id/delete_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:src="@*android:drawable/ic_input_delete"
+ android:clickable="true"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:background="?android:attr/selectableItemBackground"
+ />
+ </LinearLayout>
+ <View
+ android:layout_width="wrap_content"
+ android:layout_height="1dp"
+ android:background="#55FFFFFF"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+ android:id="@+id/key1"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/passwordEntry"
+ androidprv:digit="1"
+ />
+ <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+ android:id="@+id/key2"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/passwordEntry"
+ androidprv:digit="2"
+ />
+ <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+ android:id="@+id/key3"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/passwordEntry"
+ androidprv:digit="3"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+ android:id="@+id/key4"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/passwordEntry"
+ androidprv:digit="4"
+ />
+ <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+ android:id="@+id/key5"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/passwordEntry"
+ androidprv:digit="5"
+ />
+ <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+ android:id="@+id/key6"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/passwordEntry"
+ androidprv:digit="6"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ >
+ <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+ android:id="@+id/key7"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/passwordEntry"
+ androidprv:digit="7"
+ />
+ <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+ android:id="@+id/key8"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/passwordEntry"
+ androidprv:digit="8"
+ />
+ <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+ android:id="@+id/key9"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/passwordEntry"
+ androidprv:digit="9"
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ >
+ <Space
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+ <view class="com.android.internal.policy.impl.keyguard.NumPadKey"
+ android:id="@+id/key0"
+ style="@style/Widget.Button.NumPadKey"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ androidprv:textView="@+id/passwordEntry"
+ androidprv:digit="0"
+ />
+ <ImageButton
+ android:id="@+id/key_enter"
+ style="@style/Widget.Button.NumPadKey"
+ android:gravity="center"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:src="@drawable/sym_keyboard_return_holo"
+ />
+ </LinearLayout>
+
+ <include layout="@layout/keyguard_emergency_carrier_area"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal" />
+
+</com.android.internal.policy.impl.keyguard.KeyguardPINView>
diff --git a/core/res/res/layout/keyguard_selector_view.xml b/core/res/res/layout/keyguard_selector_view.xml
index daaf35bcdf5e..74034082747c 100644
--- a/core/res/res/layout/keyguard_selector_view.xml
+++ b/core/res/res/layout/keyguard_selector_view.xml
@@ -36,6 +36,10 @@
android:clipToPadding="false"
android:gravity="center">
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
<include layout="@layout/keyguard_glow_pad_container" />
<include layout="@layout/keyguard_emergency_carrier_area"
diff --git a/core/res/res/layout/keyguard_sim_pin_view.xml b/core/res/res/layout/keyguard_sim_pin_view.xml
index ae59d1de99ec..ad617091b18d 100644
--- a/core/res/res/layout/keyguard_sim_pin_view.xml
+++ b/core/res/res/layout/keyguard_sim_pin_view.xml
@@ -43,7 +43,10 @@
android:layout_height="wrap_content"
android:src="@drawable/ic_lockscreen_sim"/>
- <include layout="@layout/keyguard_sim_puk_pin_account_navigation"/>
+ <include layout="@layout/keyguard_message_area_large"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
</LinearLayout>
<!-- Password entry field -->
diff --git a/core/res/res/layout/keyguard_sim_puk_view.xml b/core/res/res/layout/keyguard_sim_puk_view.xml
index 414806fb4097..cc97005a6907 100644
--- a/core/res/res/layout/keyguard_sim_puk_view.xml
+++ b/core/res/res/layout/keyguard_sim_puk_view.xml
@@ -44,7 +44,9 @@
android:layout_height="wrap_content"
android:src="@drawable/ic_lockscreen_sim"/>
- <include layout="@layout/keyguard_sim_puk_pin_account_navigation"/>
+ <include layout="@layout/keyguard_message_area_large"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
</LinearLayout>
diff --git a/core/res/res/layout/keyguard_status_view.xml b/core/res/res/layout/keyguard_status_view.xml
index 1de0f6ccad80..9532a889c783 100644
--- a/core/res/res/layout/keyguard_status_view.xml
+++ b/core/res/res/layout/keyguard_status_view.xml
@@ -35,7 +35,7 @@
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_gravity="center_horizontal|top"
android:orientation="vertical">
<com.android.internal.policy.impl.keyguard.ClockView
android:id="@+id/clock_view"
diff --git a/core/res/res/layout/keyguard_transport_control_view.xml b/core/res/res/layout/keyguard_transport_control_view.xml
index 5a6083a2c084..532322c764bf 100644
--- a/core/res/res/layout/keyguard_transport_control_view.xml
+++ b/core/res/res/layout/keyguard_transport_control_view.xml
@@ -26,8 +26,8 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:foreground="@drawable/ic_lockscreen_player_background"
- android:contentDescription="@string/keygaurd_accessibility_media_controls">
+ android:foreground="@*android:drawable/ic_lockscreen_player_background"
+ android:contentDescription="@*android:string/keygaurd_accessibility_media_controls">
<!-- Use ImageView for its cropping features; otherwise could be android:background -->
<ImageView
android:id="@+id/albumart"
@@ -70,11 +70,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:src="@drawable/ic_media_previous"
+ android:src="@*android:drawable/ic_media_previous"
android:clickable="true"
android:background="?android:attr/selectableItemBackground"
android:padding="10dip"
- android:contentDescription="@string/lockscreen_transport_prev_description"/>
+ android:contentDescription="@*android:string/lockscreen_transport_prev_description"/>
</FrameLayout>
<FrameLayout
android:layout_width="wrap_content"
@@ -86,10 +86,10 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
- android:src="@drawable/ic_media_play"
+ android:src="@*android:drawable/ic_media_play"
android:background="?android:attr/selectableItemBackground"
android:padding="10dip"
- android:contentDescription="@string/lockscreen_transport_play_description"/>
+ android:contentDescription="@*android:string/lockscreen_transport_play_description"/>
</FrameLayout>
<FrameLayout
android:layout_width="wrap_content"
@@ -101,10 +101,10 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
- android:src="@drawable/ic_media_next"
+ android:src="@*android:drawable/ic_media_next"
android:background="?android:attr/selectableItemBackground"
android:padding="10dip"
- android:contentDescription="@string/lockscreen_transport_next_description"/>
+ android:contentDescription="@*android:string/lockscreen_transport_next_description"/>
</FrameLayout>
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/keyguard_widget_region.xml b/core/res/res/layout/keyguard_widget_region.xml
deleted file mode 100644
index ed10c2bdf6e0..000000000000
--- a/core/res/res/layout/keyguard_widget_region.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License")
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- This is the selector widget that allows the user to select an action. -->
-<com.android.internal.policy.impl.keyguard.KeyguardWidgetRegion
- xmlns:prvandroid="http://schemas.android.com/apk/prv/res/android"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/kg_widget_region"
- android:visibility="gone"
- android:gravity="center"
- android:orientation="vertical">
- <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager
- android:id="@+id/app_widget_container"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:clipChildren="false"
- android:clipToPadding="false">
- </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="10dp"
- android:orientation="horizontal"
- android:paddingLeft="@dimen/kg_widget_pager_horizontal_padding"
- android:paddingRight="@dimen/kg_widget_pager_horizontal_padding"
- android:layout_marginTop="@dimen/kg_runway_lights_top_margin"
- android:visibility="gone">
- <com.android.internal.policy.impl.keyguard.KeyguardGlowStripView
- android:id="@+id/left_strip"
- android:paddingTop="@dimen/kg_runway_lights_vertical_padding"
- android:paddingBottom="@dimen/kg_runway_lights_vertical_padding"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- prvandroid:numDots="5"
- prvandroid:dotSize="@dimen/kg_runway_lights_height"
- prvandroid:leftToRight="false"
- prvandroid:glowDot="@*android:drawable/ic_lockscreen_glowdot" />
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1.6"/>
- <com.android.internal.policy.impl.keyguard.KeyguardGlowStripView
- android:id="@+id/right_strip"
- android:paddingTop="@dimen/kg_runway_lights_vertical_padding"
- android:paddingBottom="@dimen/kg_runway_lights_vertical_padding"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- prvandroid:numDots="5"
- prvandroid:dotSize="@dimen/kg_runway_lights_height"
- prvandroid:leftToRight="true"
- prvandroid:glowDot="@*android:drawable/ic_lockscreen_glowdot" />
- </LinearLayout>
-</com.android.internal.policy.impl.keyguard.KeyguardWidgetRegion>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 6d6d5e578bfc..fe77b054ae46 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Laat die program toe om nuwe woorde in die gebruikerwoordeboek te skryf."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"toets toegang tot beskermde berging"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"toets toegang tot beskermde berging"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Laat die program toe om \'n toestemming vir USB-storing wat op toekomstige toestelle beskikbaar sal wees, te toets."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Laat die program toe om \'n toestemming te toets vir USB-berging wat op toekomstige toestelle beskikbaar sal wees."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Laat die program toe om \'n toestemming vir die SD-kaart wat op toekomstige toestelle beskikbaar sal wees, te toets."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"verander of vee die inhoud van jou USB-berging uit"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"Verander of vee die inhoud van jou SD-kaart uit"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Toeganklikheid geaktiveer."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Toeganklikheid gekanselleer."</string>
<string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g> ."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Eienaar"</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index eea1fac59393..b89e01bc365b 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -585,7 +585,8 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"በተጠቃሚ መዝገበ ቃላት ውስጥ አዲስ ቃል እንዲጽፍ ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"ጥበቃ ወደሚደረግለት ማከማቻ ያለ መዳረሻን ፈትሽ"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"ጥበቃ ወደሚደረግለት ማከማቻ ያለ መዳረሻን ፈትሽ"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"መተግበሪያው በወደፊት መሳሪዎች ላይ ለሚኖር የUSB ማህደረ ትውስታ ፈቃድ እንዲሞክር ይፈቅድለታል።"</string>
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
+ <skip />
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"መተግበሪያው በወደፊት መሳሪዎች ላይ ለሚኖረው SD ካርድ ፈቃድ እንዲሞክር ይፈቅድለታል።"</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"የUSB ማከማቻህን ይዘቶች ቀይር ወይም ሰርዝ"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"የSD ካርድህን ይዘቶች ቀይር ወይም ሰርዝ"</string>
@@ -1425,4 +1426,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"ተደራሽነት ነቅቷል።"</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"ተደራሽነት ተሰርዟል።"</string>
<string name="user_switched" msgid="3768006783166984410">"የአሁኑ ተጠቃሚ <xliff:g id="NAME">%1$s</xliff:g>።"</string>
+ <string name="owner_name" msgid="2716755460376028154">"ባለቤት"</string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index be9d2bfa7a98..5bd5f28fdb89 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"للسماح للتطبيق بكتابة كلمات جديدة في قاموس المستخدم."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"اختبار إمكانية الدخول إلى وحدة تخزين محمية"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"اختبار إمكانية الدخول إلى وحدة تخزين محمية"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"للسماح للتطبيق باختبار إذن لوحدة تخزين USB سيتم توفيرها على أجهزة مستقبلية."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"للسماح للتطبيق باختبار إذن لوحدة تخزين USB التي ستتوفر على أجهزة مستقبلية."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"للسماح للتطبيق باختبار إذن لبطاقة SD سيتم توفيرها على أجهزة مستقبلية."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"تعديل محتويات وحدة تخزين USB أو حذفها"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"تعديل محتويات بطاقة SD أو حذفها"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"تم تمكين إمكانية الدخول."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"تم إلغاء تسهيل الدخول."</string>
<string name="user_switched" msgid="3768006783166984410">"المستخدم الحالي <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"المالك"</string>
</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index bed4196c2a24..5d7009bdb3f3 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Дазваляе прыкладанням запісваць новыя словы ў карыстальніцкі слоўнік."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"выпрабавальны доступ да абароненага сховiшча"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"выпрабавальны доступ да абароненага сховiшча"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Дазваляе прыкладанню правяраць дазвол на USB-назапашвальнiк, якi будзе даступны для прылад у будучынi."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Дазваляе прыкладанню правяраць дазвол на USB-назапашвальнiк, якi будзе даступны для прылад у будучынi."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Дазваляе прыкладанню правяраць дазвол на SD-карту, якая будзе даступна для прылад у будучынi."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"зм. або выд. змес. USB-назап."</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"змяніць або выдаліць змесціва SD-карты"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Даступнасць уключана."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Даступнасць адменена."</string>
<string name="user_switched" msgid="3768006783166984410">"Бягучы карыстальнік <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Уладальнік"</string>
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index f9ca00a9e01d..d55d89ee793b 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -585,7 +585,8 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Разрешава на приложението да записва нови думи в потребителския речник."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"изпробване на достъп до защитено хранилище"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"изпробване на достъп до защитено хранилище"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Разрешава на приложението да изпробва разрешение за USB хран., което ще е налице на бъдещи у-ва."</string>
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
+ <skip />
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Разрешава на приложението да изпробва разрешение за SD картата, което ще бъде налице на бъдещи устройства."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"промяна или изтрив. на съдърж. от USB хран. ви"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"промяна или изтриване на съдържанието от SD картата ви"</string>
@@ -1425,4 +1426,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Достъпността е активирана."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Функцията за достъпност е анулирана."</string>
<string name="user_switched" msgid="3768006783166984410">"Текущ потребител <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Собственик"</string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 32cac859e228..df2d08566e77 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Permet que l\'aplicació escrigui paraules noves al diccionari de l\'usuari."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"accés de prova a emmagatzematge protegit"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"accés de prova a emmagatzematge protegit"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Permet que l\'aplicació provi un permís per a emmagatzematge USB que estarà disponible als dispositius en el futur."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Permet que l\'aplicació provi un permís per a emmagatzematge USB que estarà disponible a propers dispositius."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Permet que l\'aplicació provi un permís per a la targeta SD que estarà disponible als dispositius en el futur."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modificació o supressió del contingut de l\'emmagatzematge USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifica o suprimeix el contingut de la targeta SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"S\'ha activat l\'accessibilitat."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibilitat cancel·lada."</string>
<string name="user_switched" msgid="3768006783166984410">"Usuari actual: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Propietari"</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 3525a4ee1895..d7af330b8954 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Umožňuje aplikaci zapisovat nová slova do uživatelského slovníku."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"testování přístupu do chráněného úložiště"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testování přístupu do chráněného úložiště"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Umožňuje aplikaci testovat oprávnění pro úložiště USB, které bude dostupné v budoucích zařízeních."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Umožňuje aplikaci testovat oprávnění pro úložiště USB, které bude dostupné v budoucích zařízeních."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Umožňuje aplikaci testovat oprávnění pro kartu SD, která bude dostupná v budoucích zařízeních."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"úprava nebo smazání obsahu v úložišti USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"úprava nebo smazání obsahu na kartě SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Usnadnění přístupu je aktivováno."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Usnadnění zrušeno."</string>
<string name="user_switched" msgid="3768006783166984410">"Aktuální uživatel je <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Vlastník"</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 164657eeec11..7d0394c071c1 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -585,7 +585,8 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Tillader, at appen kan skrive nye ord i brugerordbogen."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"test adgangen til beskyttet lagring"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"test adgangen til beskyttet lagring"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Tillader, at appen tester en tilladelse til USB-lagring, der vil være tilgængelig på fremtidige enheder."</string>
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
+ <skip />
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Tillader, at appen kan teste en tilladelse for SD-kortet, der vil være tilgængelig på fremtidige enheder."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ændre eller slette indhold på USB-lager"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ændre eller slette indholdet på dit SD-kort"</string>
@@ -1425,4 +1426,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Tilgængelighed aktiveret."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Tilgængelighed er annulleret."</string>
<string name="user_switched" msgid="3768006783166984410">"Nuværende bruger <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Ejer"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index c1a9741e2ead..0fa14b342f71 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Ermöglicht der App, dem Nutzerwörterbuch neue Einträge hinzuzufügen"</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"Zugriff auf geschützten Speicher testen"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"Zugriff auf geschützten Speicher testen"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Ermöglicht der App, eine Berechtigung für USB-Speicher zu testen, die künftig auf neuen Geräten verfügbar sein wird."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Ermöglicht der App, eine Berechtigung für USB-Speicher zu testen, die künftig auf neuen Geräten verfügbar sein wird"</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Ermöglicht der App, eine Berechtigung für SD-Karten zu testen, die künftig auf neuen Geräten verfügbar sein wird"</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB-Speicherinhalte ändern oder löschen"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD-Karteninhalte ändern oder löschen"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Bedienungshilfen aktiviert"</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Bedienungshilfen abgebrochen"</string>
<string name="user_switched" msgid="3768006783166984410">"Aktueller Nutzer <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="owner_name" msgid="2716755460376028154">"Eigentümer"</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 52f00706e14d..33899baf7f83 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Επιτρέπει στην εφαρμογή την εγγραφή νέων λέξεων στο λεξικό χρήστη."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"δοκιμή πρόσβασης σε προστατευμένο χώρο αποθήκευσης"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"δοκιμή πρόσβασης σε προστατευμένο χώρο αποθήκευσης"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Επιτρέπει στην εφαρμογή τη δοκιμή μια άδειας για το χώρο αποθήκευσης USB που θα διατίθεται σε μελλοντικές συσκευές."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Επιτρ.στην εφαρμ. τη δοκ.άδειας αποθ. χώρ.USB που θα διατ. σε μελλ.συσκ."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Επιτρέπει στην εφαρμογή τη δοκιμή μια άδειας για την κάρτα SD που θα διατίθεται σε μελλοντικές συσκευές."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"τροπ. ή διαγρ. περιεχ. αποθ. χώρ. USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"τροποποίηση ή διαγραφή των περιεχομένων της κάρτας SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Ενεργοποιήθηκε η προσβασιμότητα."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Η λειτουργία προσβασιμότητας ακυρώθηκε."</string>
<string name="user_switched" msgid="3768006783166984410">"Τρέχων χρήστης <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Κάτοχος"</string>
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index fdd35a163014..1267b14ab5ab 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Allows the app to write new words into the user dictionary."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"test access to protected storage"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"test access to protected storage"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Allows the app to test a permission for USB storage that will be availabe on future devices."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Allows the app to test a permission for USB storage that will be available on future devices."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Allows the app to test a permission for the SD card that will be available on future devices."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modify or delete the contents of your USB storage"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modify or delete the contents of your SD card"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Accessibility enabled."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibility cancelled."</string>
<string name="user_switched" msgid="3768006783166984410">"Current user <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Owner"</string>
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 93895790b83a..ebeb2960c349 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -585,7 +585,8 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Permite que la aplicación ingrese palabras nuevas en el diccionario del usuario."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"probar acceso a almacenamiento protegido"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"probar acceso a almacenamiento protegido"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Permite que la aplicación pruebe un permiso de almacenamiento USB que estará disponible en futuros dispositivos."</string>
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
+ <skip />
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Permite que la aplicación pruebe un permiso para la tarjeta SD que estará disponible en futuros dispositivos."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modificar/borrar contenido USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modificar o eliminar el contenido de la tarjeta SD"</string>
@@ -1425,4 +1426,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Se activó la accesibilidad."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Se canceló la accesibilidad."</string>
<string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="owner_name" msgid="2716755460376028154">"Propietario"</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 53f0eff47985..3ea30d14bd68 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Permite que la aplicación escriba palabras nuevas en el diccionario de usuario."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"probar acceso a almacenamiento protegido"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"probar acceso a almacenamiento protegido"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Probar permiso USB para futuros dispositivos"</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Probar permiso USB para futuros dispositivos"</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Permite que la aplicación pruebe un permiso para la tarjeta SD que estará disponible en futuros dispositivos."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"editar o borrar contenido de USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modificar o eliminar el contenido de la tarjeta SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Accesibilidad habilitada"</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accesibilidad cancelada"</string>
<string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="owner_name" msgid="2716755460376028154">"Propietario"</string>
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 17d0724a8f0b..03fcfbbee8a6 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Võimaldab rakendusel kirjutada kasutajasõnastikku uusi sõnu."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"kaitstud salvestusruumi juurdepääsu katsetamine"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"kaitstud salvestusruumi juurdepääsu katsetamine"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Võimaldab rakendusel katsetada USB-salvestusruumi luba, mis on saadaval tulevastel seadmetel."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Võimaldab rakendusel testida luba USB-salvestuseks, mis on saadaval tulevastes seadmetes."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Võimaldab rakendusel katsetada SD-kaardi luba, mis on saadaval tulevastel seadmetel."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"muutke, kustut. USB-ruumi sisu"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD-kaardi sisu muutmine või kustutamine"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Hõlbustus on lubatud."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Hõlbustus on tühistatud."</string>
<string name="user_switched" msgid="3768006783166984410">"Praegune kasutaja <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Omanik"</string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 10434f86a42e..de1e9a9ef6d5 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"به برنامه اجازه می‎دهد تا کلمات جدید را در فهرست کاربر بنویسد."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"تست کردن دسترسی به حافظهٔ محافظت‌شده"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"تست کردن دسترسی به حافظهٔ محافظت‌شده"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"به برنامه اجازه می‌دهد یک مجوز را برای حافظهٔ USB که در دستگاه‌های آتی موجود خواهد بود تست کند."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"به برنامه اجازه می‌دهد یک مجوز را برای حافظه USB که در دستگاه‌های آتی ارائه خواهد شد، تست کند."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"به برنامه اجازه می‌دهد یک مجوز را برای کارت SD که در دستگاه‌های آتی موجود خواهد بود تست کند."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"اصلاح یا حذف محتویات حافظهٔ USB شما"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"محتوای کارت SD شما را اصلاح کرده یا تغییر دهد"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"قابلیت دسترسی فعال شد."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"قابلیت دسترسی لغو شد."</string>
<string name="user_switched" msgid="3768006783166984410">"کاربر کنونی <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"دارنده"</string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index a0ec3ad26c3e..ec48526bbbc5 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Antaa sovelluksen kirjoittaa uusia sanoja käyttäjän sanakirjaan."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"suojatun tallennustilan käyttöoikeuden testaus"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"suojatun tallennustilan käyttöoikeuden testaus"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Antaa sovelluksen testata muiden laitteiden käyttämän USB-tallennustilan käyttölupaa."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Antaa sovelluksen testata tulevien laitteiden USB-tallennustilan käyttölupaa."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Antaa sovelluksen testata muiden laitteiden käyttämän SD-kortin käyttölupaa."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"muokkaa tai poista USB:n sis."</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"muokkaa tai poista SD-kortin sisältöä"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Esteettömyystila käytössä."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Esteettömyystila peruutettu."</string>
<string name="user_switched" msgid="3768006783166984410">"Nykyinen käyttäjä: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Omistaja"</string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 1f82449aebca..912bda059dba 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Permet à l\'application d\'enregistrer de nouveaux mots dans le dictionnaire personnel de l\'utilisateur."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"tester l\'accès à la mémoire de stockage protégée"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"tester l\'accès à la mémoire de stockage protégée"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Permet à l\'application de tester une autorisation pour la mémoire de stockage USB qui sera ensuite proposée sur les futurs appareils."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Permet à l\'application de tester une autorisation pour la mémoire de stockage USB qui sera ensuite proposée sur les futurs appareils."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Permet à l\'application de tester une autorisation pour la carte SD qui sera ensuite proposée sur les futurs appareils."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modifier ou supprimer le contenu de la mémoire USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifier ou supprimer le contenu de la carte SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"L\'accessibilité a bien été activée."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibilité annulée."</string>
<string name="user_switched" msgid="3768006783166984410">"Utilisateur actuel : <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="owner_name" msgid="2716755460376028154">"Propriétaire"</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 0a2c1fb35cca..67148f1bc2fc 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"एप्लिकेशन को उपयोगकर्ता डिक्शनरी में नए शब्द लिखने देता है."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"संरक्ष‍ित संग्रहण पर पहुंच का परीक्षण करें"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"संरक्ष‍ित संग्रहण पर पहुंच का परीक्षण करें"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"एप्लि. को USB संग्रहण अनुमति जांचने देता है जो भावी उपकरणों में उपलब्‍ध होगा."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"एप्लि. को USB संग्रहण अनुमति का परीक्षण करने देता है जो भविष्‍य के उपकरणों में उपलब्‍ध होगा."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"एप्लिकेशन को SD कार्ड के लिए किसी अनुमति का परीक्षण करने देता है जो भविष्‍य के उपकरणों में उपलब्‍ध होगा."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"अपने USB संग्रहण की सामग्री बदलें या हटाएं"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"अपने SD कार्ड की सामग्री बदलें या हटाएं"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"पहुंच-योग्यता सक्षम कर दी है."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"पहुंच-योग्यता रद्द की गई."</string>
<string name="user_switched" msgid="3768006783166984410">"वर्तमान उपयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"स्वामी"</string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 78d7d2896a7a..131445838bf8 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Aplikaciji omogućuje pisanje novih riječi u korisnički rječnik."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"testiranje pristupa zaštićenoj pohrani"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testiranje pristupa zaštićenoj pohrani"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Aplikaciji omogućuje testiranje dozvole za USB pohranu koja će biti dostupna na budućim uređajima."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Aplikaciji omogućuje testiranje dozvole za USB pohranu koja će biti dostupna na budućim uređajima."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Aplikaciji omogućuje testiranje dozvole za SD karticu koja će biti dostupna na budućim uređajima."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"izmjena/brisanje sadrž. USB-a"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"izmjena ili brisanje sadržaja SD kartice"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Dostupnost je omogućena."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Pristupačnost otkazana."</string>
<string name="user_switched" msgid="3768006783166984410">"Trenutačni korisnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Vlasnik"</string>
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index d2e2bcb55ad4..6a9e54df6d3e 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Lehetővé teszi az alkalmazás számára, hogy új szavakat írjon a felhasználói szótárba."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"védett tárhelyhez való hozzáférés tesztelése"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"védett tárhelyhez való hozzáférés tesztelése"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Lehetővé teszi az alkalmazás számára egy olyan USB-háttértár engedélyének tesztelését, amely későbbi eszközökön lesz elérhető."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Lehetővé teszi az alkalmazás számára egy olyan USB-háttértár engedélyének tesztelését, amely későbbi eszközökön lesz elérhető."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Lehetővé teszi az alkalmazás számára egy olyan SD-kártya engedélyének tesztelését, amely későbbi eszközökön lesz elérhető."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB-tár törlése/módosítása"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD-kártya tartalmának módosítása vagy törlése"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Hozzáférés engedélyezve"</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Hozzáférés megszakítva."</string>
<string name="user_switched" msgid="3768006783166984410">"<xliff:g id="NAME">%1$s</xliff:g> az aktuális felhasználó."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Tulajdonos"</string>
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index e2586708e924..8822ec19472a 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Mengizinkan apl menulis kata-kata baru ke dalam kamus pengguna."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"akses uji coba ke penyimpanan yang dilindungi"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"akses uji coba ke penyimpanan yang dilindungi"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Memungkinkan aplikasi menguji izin untuk penyimpanan USB yang akan tersedia pada perangkat yang akan datang."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Memungkinkan aplikasi menguji izin penyimpanan USB yang akan tersedia di perangkat mendatang."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Memungkinkan aplikasi menguji izin untuk kartu SD yang akan tersedia pada perangkat yang akan datang."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ubah/hapus konten pympanan USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"mengubah atau menghapus konten kartu SD Anda"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Aksesibilitas diaktifkan."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Aksesibilitas dibatalkan."</string>
<string name="user_switched" msgid="3768006783166984410">"Pengguna saat ini <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Pemilik"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b29d0b6c1709..214876fe1fff 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Consente all\'applicazione di scrivere nuove parole nel dizionario utente."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"test dell\'accesso all\'archivio protetto"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"test dell\'accesso all\'archivio protetto"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Consente all\'applicazione di testare un\'autorizzazione relativa all\'archivio USB che sarà disponibile su dispositivi futuri."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Consente all\'applicazione di testare un\'autorizzazione relativa all\'archivio USB che sarà disponibile su dispositivi futuri."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Consente all\'applicazione di testare un\'autorizzazione relativa alla scheda SD che sarà disponibile su dispositivi futuri."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"mod./elimin. cont. archivio USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifica o eliminazione dei contenuti della scheda SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Accessibilità attivata."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibilità annullata."</string>
<string name="user_switched" msgid="3768006783166984410">"Utente corrente <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Proprietario"</string>
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 18de68f457ce..a2e9f87ac4f7 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"מאפשר ליישום לכתוב מילים חדשות במילון המשתמש."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"בדיקת גישה לאחסון מוגן"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"בדיקת גישה לאחסון מוגן"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"מאפשר ליישום לבדוק אישור לאחסון USB שיהיה זמין במכשירים עתידיים."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"מאפשר ליישום לבדוק אישור לאחסון USB שיהיה זמין במכשירים עתידיים."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"מאפשר ליישום לבדוק אישור לכרטיס SD שיהיה זמין במכשירים עתידיים."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"שינוי או מחיקה של תוכן אחסון ה-USB שלך"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"שינוי או מחיקה של תוכן כרטיס ה-SD שלך"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"נגישות הופעלה."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"נגישות בוטלה."</string>
<string name="user_switched" msgid="3768006783166984410">"המשתמש הנוכחי <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"בעלים"</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index ac81bf9696c7..34dc52a88f84 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"単語リストに新しい語句を書き込むことをアプリに許可します。"</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"保護されたストレージへのテストアクセス"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"保護されたストレージへのテストアクセス"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"今後追加するデバイスで使用できるUSBストレージの権限のテストをアプリに許可します。"</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"今後追加する端末で使用できるUSBストレージの権限のテストをアプリに許可します。"</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"今後追加するデバイスで使用できるSDカードの権限のテストをアプリに許可します。"</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USBストレージのコンテンツの変更または削除"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SDカードのコンテンツの変更または削除"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"ユーザー補助が有効になりました。"</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"ユーザー補助をキャンセルしました。"</string>
<string name="user_switched" msgid="3768006783166984410">"現在のユーザーは<xliff:g id="NAME">%1$s</xliff:g>です。"</string>
+ <string name="owner_name" msgid="2716755460376028154">"所有者"</string>
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index ccffc49fa81f..c99dbf14619f 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -585,7 +585,8 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"앱이 사용자 사전에 새 단어를 입력할 수 있도록 허용합니다."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"보호된 저장소에 액세스 테스트"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"보호된 저장소에 액세스 테스트"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"앱이 미래의 기기에서 사용할 수 있는 USB 저장소의 권한을 테스트하도록 허용합니다."</string>
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
+ <skip />
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"앱이 미래의 기기에서 사용할 수 있는 SD 카드의 권한을 테스트하도록 허용합니다."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB 저장소의 콘텐츠 수정 또는 삭제"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD 카드의 콘텐츠 수정 또는 삭제"</string>
@@ -1425,4 +1426,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"접근성을 사용 설정했습니다."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"접근성이 취소되었습니다."</string>
<string name="user_switched" msgid="3768006783166984410">"현재 사용자는 <xliff:g id="NAME">%1$s</xliff:g>님입니다."</string>
+ <string name="owner_name" msgid="2716755460376028154">"소유자"</string>
</resources>
diff --git a/core/res/res/values-land/bools.xml b/core/res/res/values-land/bools.xml
index b0630ad30391..85c64d939afe 100644
--- a/core/res/res/values-land/bools.xml
+++ b/core/res/res/values-land/bools.xml
@@ -15,6 +15,7 @@
-->
<resources>
+ <bool name="kg_enable_camera_default_widget">false</bool>
<bool name="kg_share_status_area">false</bool>
<bool name="kg_sim_puk_account_full_screen">false</bool>
</resources>
diff --git a/core/res/res/values-land/integers.xml b/core/res/res/values-land/integers.xml
index 6613d6897ac3..020fd23cec81 100644
--- a/core/res/res/values-land/integers.xml
+++ b/core/res/res/values-land/integers.xml
@@ -22,4 +22,5 @@
<integer name="kg_selector_gravity">0x13</integer>
<integer name="kg_widget_region_weight">45</integer>
<integer name="kg_security_flipper_weight">55</integer>
-</resources> \ No newline at end of file
+ <integer name="kg_glowpad_rotation_offset">-90</integer>
+</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 2689ff34bc6f..0de01b243300 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Leidžiama programai rašyti naujus žodžius į naudotojo žodyną."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"prieigos prie apsaugotos saugyklos tikrinimas"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"prieigos prie apsaugotos saugyklos tikrinimas"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Leidž. progr. tikr. USB atm., kuri bus pasiek. ateityje naud. kt. įreng., leidimą."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Leidžia progr. tikr. leidimą USB atm., kuri bus vėlesn. įreng."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Leidžiama programai tikrinti SD kortelės, kuri bus pasiekiama ateityje naudojant kitus įrenginius, leidimą."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"keisti / trinti USB atm. turinį"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"keisti arba trinti SD kortelės turinį"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Pritaikymas neįgaliesiems įgalintas."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Pritaikymo neįgaliesiems režimas atšauktas."</string>
<string name="user_switched" msgid="3768006783166984410">"Dabartinis naudotojas: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Savininkas"</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 4f8fb16cb663..0b1519edf14f 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Ļauj lietotnei rakstīt jaunus vārdus lietotāja vārdnīcā."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"aizsargātai krātuvei pieejamas piekļuves pārbaude"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"aizsargātai krātuvei pieejamas piekļuves pārbaude"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Ļauj lietotnei pārbaudīt atļauju USB krātuvei, kas būs pieejama turpmākajās ierīcēs."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Ļauj lietotnei pārbaudīt atļauju USB krātuvei, kas būs pieejama turpmākajās ierīcēs."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Ļauj lietotnei pārbaudīt atļauju SD kartei, kas būs pieejama turpmākajās ierīcēs."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"mainīt vai dzēst USB atm. sat."</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD kartes satura pārveidošana vai dzēšana"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Pieejamības režīms ir iespējots."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Pieejamība ir atcelta."</string>
<string name="user_switched" msgid="3768006783166984410">"Pašreizējais lietotājs: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Īpašnieks"</string>
</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 636a16822b3c..704a0fdb0880 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Membenarkan apl menulis perkataan baharu ke dalam kamus pengguna."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"uji akses ke storan dilindungi"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"uji akses ke storan dilindungi"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Membenarkan apl menguji kebenaran untuk storan USB yang akan tersedia pada peranti akan datang."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Apl boleh uji kebenaran USB."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Membenarkan apl menguji kebenaran untuk kad SD yang akan tersedia pada peranti akan datang."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ubah suai atau padam kandungan storan USB anda"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ubah suai atau padam kandungan kad SD anda"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Kebolehcapaian didayakan."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Kebolehcapaian dibatalkan."</string>
<string name="user_switched" msgid="3768006783166984410">"Pengguna semasa <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Pemilik"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 38163b5ab1ec..f54990e2091b 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -585,7 +585,8 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Lar appen skrive nye ord i brukerordlisten."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"testadgang til beskyttet lagring"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testadgang til beskyttet lagring"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Lar appen teste en tillatelse for USB-lagring som kommer til å bli tilgjengelig på fremtidige enheter."</string>
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
+ <skip />
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Lar appen teste en tillatelse for SD-kortet som kommer til å bli tilgjengelig på fremtidige enheter."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"endre eller slette innholdet i USB-lagringen"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"endre eller slette innhold i SD-kortet"</string>
@@ -1425,4 +1426,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Tilgjengelighet er aktivert."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Tilgjengelighetstjenesten ble avbrutt."</string>
<string name="user_switched" msgid="3768006783166984410">"Gjeldende bruker: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Eier"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index d896f1a33481..bb48630bb56e 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Hiermee kan de app nieuwe woorden schrijven naar het gebruikerswoordenboek."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"testtoegang tot beveiligde opslag"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testtoegang tot beveiligde opslag"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Hiermee kan de app toestemming testen voor USB-opslag die beschikbaar komt op toekomstige apparaten."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Hiermee kan de app toestemming testen voor USB-opslag op toekomstige apparaten."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Hiermee kan de app toestemming testen voor de SD-kaart die beschikbaar komt op toekomstige apparaten."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"de inhoud van uw USB-opslag aanpassen of verwijderen"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"de inhoud van uw SD-kaart aanpassen of verwijderen"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Toegankelijkheid ingeschakeld."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Toegankelijkheid geannuleerd."</string>
<string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Eigenaar"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 0472a10b8509..da09bcb154ad 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Pozwala aplikacji na zapisywanie nowych słów do słownika użytkownika."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"testowanie dostępu do chronionej pamięci"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testowanie dostępu do chronionej pamięci"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Pozwala aplikacji na testowanie uprawnienia do pamięci USB, które będzie dostępne w przyszłych urządzeniach."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Aplikacja może testować uprawnienie do pamięci USB, dostępne w przyszłych urządzeniach."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Pozwala aplikacji na testowanie uprawnienia do karty SD, które będzie dostępne w przyszłych urządzeniach."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modyfikowanie i usuwanie zawartości pamięci USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modyfikowanie i usuwanie zawartości karty SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Włączono ułatwienia dostępu."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ułatwienia dostępu zostały anulowane."</string>
<string name="user_switched" msgid="3768006783166984410">"Bieżący użytkownik: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Właściciel"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index e800e9d0d670..7ce4a34979db 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Permite à aplicação escrever novas palavras no dicionário do utilizador."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"testar o acesso a armazenamento protegido"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testar o acesso a armazenamento protegido"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Permite que a aplicação teste uma autorização para a memória USB que irá estar disponível em dispositivos futuros."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Permite que a aplicação teste uma autorização para a memória USB que irá estar disponível em dispositivos futuros."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Permite que a aplicação teste uma autorização para o cartão SD que irá estar disponível em dispositivos futuros."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modificar ou eliminar os conteúdos da memória USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modificar ou eliminar os conteúdos do cartão SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Acessibilidade ativada."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Acessibilidade cancelada."</string>
<string name="user_switched" msgid="3768006783166984410">"<xliff:g id="NAME">%1$s</xliff:g> do utilizador atual."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Proprietário"</string>
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 44c1c46ad432..07806305eefc 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Permite que o aplicativo grave novas palavras no dicionário do usuário."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"testar o acesso ao armazenamento protegido"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testar o acesso ao armazenamento protegido"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Permite que o aplicativo teste uma permissão para o armazenamento USB que estará disponível em dispositivos futuros."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Permite que o aplicativo teste uma permissão para o armazenamento USB que estará disponível em dispositivos futuros."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Permite que o aplicativo teste uma permissão para o cartão SD que estará disponível em dispositivos futuros."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modif ou excl cont. armaz USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modificar ou excluir o conteúdo do cartão SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Acessibilidade ativada."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Acessibilidade cancelada."</string>
<string name="user_switched" msgid="3768006783166984410">"Usuário atual <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Proprietário"</string>
</resources>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index 3a01bac8ce7a..5ea84c76db31 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -969,7 +969,7 @@
<skip />
<!-- no translation found for permlab_sdcardRead (8235341515605559677) -->
<skip />
- <!-- no translation found for permdesc_sdcardRead (5791957130190763289) -->
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
<skip />
<!-- no translation found for permdesc_sdcardRead (5914402684685848828) -->
<skip />
@@ -2304,4 +2304,7 @@
<skip />
<!-- no translation found for user_switched (3768006783166984410) -->
<skip />
+ <!-- no translation found for owner_name (2716755460376028154) -->
+ <!-- no translation found for owner_name (3879126011135546571) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index edbb3c2b675b..438522d7bd57 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -585,7 +585,8 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Permite aplicaţiei să scrie cuvinte noi în dicţionarul utilizatorului."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"testează accesul la stocarea protejată"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testează accesul la stocarea protejată"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Permite aplic. să testeze o permis. pt. stoc. USB care va fi dispon. pe dispozit. viitoare."</string>
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
+ <skip />
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Permite aplicaţiei să testeze o permisiune pentru cardul SD care va fi disponibil pe dispozitivele viitoare."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"modifică sau şterge conţinutul stocării USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"modifică sau şterge conţinutul cardului SD"</string>
@@ -1425,4 +1426,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"S-a activat accesibilitatea."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accesibilitatea a fost anulată"</string>
<string name="user_switched" msgid="3768006783166984410">"Utilizator curent: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Proprietar"</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 855f461a2686..85136590ad0c 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -426,7 +426,7 @@
<string name="permlab_writeCalendar" msgid="8438874755193825647">"Добавление/изменение мероприятий и отправление гостям эл. сообщений без предупреждения владельца календаря"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Приложение сможет добавлять, удалять и изменять мероприятия, доступные для редактирования на вашем планшетном ПК, включая мероприятия, добавленные другими людьми. Так приложение сможет рассылать сообщения от имени владельца календаря и изменять мероприятия без его ведома."</string>
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Приложение сможет добавлять, удалять и изменять мероприятия, доступные для редактирования на вашем телефоне, включая мероприятия, добавленные другими людьми. Так приложение сможет рассылать сообщения от имени владельца календаря и изменять мероприятия без его ведома."</string>
- <string name="permlab_accessMockLocation" msgid="8688334974036823330">"копировать источники мест для проверки"</string>
+ <string name="permlab_accessMockLocation" msgid="8688334974036823330">"Установка фиктивного местоположения для отладки"</string>
<string name="permdesc_accessMockLocation" msgid="5808711039482051824">"Приложение сможет создавать фиктивные местоположения для тестирования или установки нового источника геоданных и переопределять местоположение и/или статус, возвращаемые другими источниками, такими как система GPS."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"получать доступ к дополнительным командам источника данных о местоположении"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="5945166642335800763">"Приложение получит доступ к дополнительным командам управления источниками геоданных и сможет вмешиваться в работу системы GPS или других источников геоданных."</string>
@@ -585,7 +585,8 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Приложение сможет добавлять слова в пользовательский словарь."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"Проверка доступа к защищенному хранилищу"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"Проверка доступа к защищенному хранилищу"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Приложение сможет проверять разрешение для USB-накопителя, которое в дальнейшем будет предоставляться на других устройствах."</string>
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
+ <skip />
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Приложение сможет проверять разрешение для SD-карты, которое в дальнейшем будет предоставляться на других устройствах."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"Изменение/удаление данных на USB-накопителе"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"Изменение или удаление содержимого SD-карты"</string>
@@ -1425,4 +1426,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Специальные возможности включены."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Специальные возможности не будут включены."</string>
<string name="user_switched" msgid="3768006783166984410">"Выбран аккаунт пользователя <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Владелец"</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index a40bb67888aa..fc4085211228 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -585,7 +585,8 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Umožňuje aplikácii zapisovať nové slová do používateľského slovníka."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"testovanie prístupu do chráneného ukladacieho priestoru"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testovanie prístupu do chráneného ukladacieho priestoru"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Umožňuje aplikácii testovať povolenie pre ukladací priestor USB, ktorý bude k dispozícii v budúcich zariadeniach."</string>
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
+ <skip />
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Umožňuje aplikácii testovať povolenie pre kartu SD, ktorá bude k dispozícii v budúcich zariadeniach."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"upraviť alebo odstrániť obsah úložiska USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"úprava alebo odstránenie obsahu na karte SD"</string>
@@ -1425,4 +1426,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Zjednodušenie ovládania je povolené."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Zjednodušenie ovládania bolo zrušené."</string>
<string name="user_switched" msgid="3768006783166984410">"Aktuálny používateľ je <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Vlastník"</string>
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 85c2ff4fa936..3dda747f0530 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Programu omogoča pisanje nove besede v uporabniški slovar."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"preskus dostopa do zaščitene shrambe"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"preskus dostopa do zaščitene shrambe"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Aplikaciji omogoča preskušanje dovoljenja za pomnilnik USB, ki bo na voljo v prihodnjih napravah."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Aplikaciji omogoča preskušanje dovoljenja za shrambo USB, ki bo na voljo v prihodnjih napravah."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Aplikaciji omogoča preskušanje dovoljenja za kartico SD, ki bo na voljo v prihodnjih napravah."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"spr. ali bris. vseb. pomn. USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"spreminjanje ali brisanje vsebine kartice SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Pripomočki za ljudi s posebnimi potrebami so omogočeni."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Omogočanje pripomočkov za ljudi s posebnimi potrebami preklicano."</string>
<string name="user_switched" msgid="3768006783166984410">"Trenutni uporabnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Lastnik"</string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index b08bce77c0eb..92d8acb2c708 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -306,7 +306,7 @@
<string name="permlab_broadcastSmsReceived" msgid="5689095009030336593">"слање емитовања примљених путем SMS порука"</string>
<string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"Дозвољава апликацији да емитује обавештење да је SMS порука примљена. Злонамерне апликације на тај начин могу да фалсификују долазне SMS поруке."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"слање примљених PUSH емитовања преко WAP-а"</string>
- <string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Дозвољава апликацији да емитује обавештење да је примљена PUSH порука преко WAP-а. Злонамерне апликације то могу да искористе да фалсификују пријем MMS порука или да кришом замене садржај било које веб странице уносом злонамерног садржаја."</string>
+ <string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"Дозвољава апликацији да емитује обавештење да је примљена PUSH порука преко WAP-а. Злонамерне апликације то могу да искористе да фалсификују пријем MMS порука или да кришом замене садржај било које веб-странице уносом злонамерног садржаја."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"ограничење броја покренутих процеса"</string>
<string name="permdesc_setProcessLimit" msgid="7318061314040879542">"Дозвољава апликацији да управља максималним бројем процеса који ће моћи да се покрену. Никада није потребна уобичајеним апликацијама."</string>
<string name="permlab_setAlwaysFinish" msgid="550958507798796965">"принудно затварање позадинских апликација"</string>
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Дозвољава апликацији да уписује нове речи у кориснички речник."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"тестирање приступа заштићеној меморији"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"тестирање приступа заштићеној меморији"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Дозвољава апликацији да тестира дозволу за USB меморију која ће бити доступна на будућим уређајима."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Дозвољава апликацији да тестира дозволу за USB меморију која ће бити доступна на будућим уређајима."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Дозвољава апликацији да тестира дозволу за SD картицу која ће бити доступна на будућим уређајима."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"измена или брисање садржаја USB меморије"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"мењање или брисање садржаја SD картице"</string>
@@ -1264,7 +1264,7 @@
<string name="media_unknown_state" msgid="729192782197290385">"Екстерни медиј непознатог статуса."</string>
<string name="share" msgid="1778686618230011964">"Дели"</string>
<string name="find" msgid="4808270900322985960">"Пронађи"</string>
- <string name="websearch" msgid="4337157977400211589">"Веб претрага"</string>
+ <string name="websearch" msgid="4337157977400211589">"Веб-претрага"</string>
<string name="find_next" msgid="5742124618942193978">"Пронађи следеће"</string>
<string name="find_previous" msgid="2196723669388360506">"Пронађи претходно"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"Захтев за локацију од корисника <xliff:g id="NAME">%s</xliff:g>"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Приступачност је омогућена."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Приступачност је отказана."</string>
<string name="user_switched" msgid="3768006783166984410">"Актуелни корисник <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Власник"</string>
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 84634432b10c..97760297bfb9 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Tillåter att appen anger nya ord i användarordlistan."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"testa åtkomst till skyddad lagringsenhet"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testa åtkomst till skyddad lagringsenhet"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Tillåter appen att testa behörighet till USB-enheter för användning på framtida enheter."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Tillåter att appen testar behörighet till USB-enheter för användning på framtida enheter."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Tillåter appen att testa behörighet till SD-kortet för användning på framtida enheter."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ändra eller ta bort innehållet"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ändra eller ta bort innehåll på SD-kortet"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Tillgänglighetsläget har aktiverats."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Byte till tillgänglighetsläge avbrutet."</string>
<string name="user_switched" msgid="3768006783166984410">"Nuvarande användare: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Ägare"</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index f4a4d644c887..cd397fc8a983 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Inaruhusu programu kuandika maneno mapya katika kamusi ya mtumiaji."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"jaribu mfikio kwa hifadhi iliyolindwa"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"jaribu mfikio kwa hifadhi iliyolindwa"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Inaruhusu programu kujaribu idhini ya hifadhi ya USB itakayokuwa kwenye vifaa vya baadaye."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Inaruhusu programu kujaribu idhini ya hifadhi ya USB itakayopatikana kwenye vifaa vya baadaye."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Inaruhusu programu kujaribu idhini ya kadi ya SD itakayokuwa kwenye vifaa vya baadaye."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"rekebisha au ufute maudhui ya hifadhi yako ya USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"rekebisha au ufute maudhui ya kadi yako ya SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Ufikivu umewezeshwa."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ufikivu umeghairiwa."</string>
<string name="user_switched" msgid="3768006783166984410">"Mtumiaji wa sasa <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Mmiliki"</string>
</resources>
diff --git a/core/res/res/values-sw600dp-land/integers.xml b/core/res/res/values-sw600dp-land/integers.xml
index 5f5d263b29dd..b724c9099d87 100644
--- a/core/res/res/values-sw600dp-land/integers.xml
+++ b/core/res/res/values-sw600dp-land/integers.xml
@@ -19,4 +19,5 @@
<resources>
<integer name="kg_widget_region_weight">50</integer>
<integer name="kg_security_flipper_weight">50</integer>
-</resources> \ No newline at end of file
+ <integer name="kg_glowpad_rotation_offset">0</integer>
+</resources>
diff --git a/core/res/res/values-sw600dp/bools.xml b/core/res/res/values-sw600dp/bools.xml
index 3753aba3749a..eae4f87d7b09 100644
--- a/core/res/res/values-sw600dp/bools.xml
+++ b/core/res/res/values-sw600dp/bools.xml
@@ -19,4 +19,6 @@
<bool name="show_ongoing_ime_switcher">true</bool>
<bool name="kg_share_status_area">false</bool>
<bool name="kg_sim_puk_account_full_screen">false</bool>
+ <!-- No camera for you, tablet user -->
+ <bool name="kg_enable_camera_default_widget">false</bool>
</resources>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 564545adb1fe..0d01df4e9824 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -108,5 +108,9 @@
<dimen name="kg_runway_lights_top_margin">-10dp</dimen>
<!-- Margin around the various security views -->
- <dimen name="keyguard_security_view_margin">24dp</dimen>
+ <dimen name="keyguard_security_view_margin">12dp</dimen>
+
+ <!-- Margin around the various security views -->
+ <dimen name="keyguard_muliuser_selector_margin">12dp</dimen>
+
</resources>
diff --git a/core/res/res/layout/empty_navigation.xml b/core/res/res/values-sw600dp/integers.xml
index 642207066b80..de9829c2cf95 100644
--- a/core/res/res/layout/empty_navigation.xml
+++ b/core/res/res/values-sw600dp/integers.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-**
+/*
** Copyright 2012, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License")
+** 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
**
@@ -16,4 +16,6 @@
** limitations under the License.
*/
-->
-<merge xmlns:android="http://schemas.android.com/apk/res/android" /> \ No newline at end of file
+<resources>
+ <integer name="kg_carousel_angle">60</integer>
+</resources>
diff --git a/core/res/res/values-sw720dp/dimens.xml b/core/res/res/values-sw720dp/dimens.xml
index 6144961b120a..d6d2b6621a0d 100644
--- a/core/res/res/values-sw720dp/dimens.xml
+++ b/core/res/res/values-sw720dp/dimens.xml
@@ -101,5 +101,15 @@
<dimen name="kg_runway_lights_top_margin">-30dp</dimen>
<!-- Margin around the various security views -->
- <dimen name="keyguard_security_view_margin">100dp</dimen>
+ <dimen name="keyguard_muliuser_selector_margin">24dp</dimen>
+
+ <!-- Stroke width of the frame for the circular avatars. -->
+ <dimen name="keyguard_avatar_frame_stroke_width">3dp</dimen>
+
+ <!-- Size of the avator on the multiuser lockscreen. -->
+ <dimen name="keyguard_avatar_size">88dp</dimen>
+
+ <!-- Size of the text under the avator on the multiuser lockscreen. -->
+ <dimen name="keyguard_avatar_name_size">12sp</dimen>
+
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 38cf2f6ff563..5609a77f690c 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"อนุญาตให้แอปพลิเคชันเขียนคำใหม่ลงในพจนานุกรมผู้ใช้"</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"ทดสอบการเข้าถึงที่จัดเก็บข้อมูลที่มีการป้องกัน"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"ทดสอบการเข้าถึงที่จัดเก็บข้อมูลที่มีการป้องกัน"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"อนุญาตให้แอปพลิเคชันทดสอบการอนุญาตสำหรับที่จัดเก็บข้อมููล USB ที่จะสามารถใช้งานได้ในอุปกรณ์ในอนาคต"</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"อนุญาตให้แอปพลิเคชันทดสอบการอนุญาตสำหรับที่จัดเก็บข้อมููล USB ที่จะสามารถใช้งานได้ในอุปกรณ์ในอนาคต"</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"อนุญาตให้แอปพลิเคชันทดสอบการอนุญาตสำหรับการ์ด SD ที่จะสามารถใช้งานได้ในอุปกรณ์ในอนาคต"</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"แก้ไขหรือลบเนื้อหาใน USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"แก้ไขหรือลบเนื้อหาในการ์ด SD ของคุณ"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"เปิดใช้งานการเข้าถึงแล้ว"</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"ยกเลิกการเข้าถึงแล้ว"</string>
<string name="user_switched" msgid="3768006783166984410">"ผู้ใช้ปัจจุบัน <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="owner_name" msgid="2716755460376028154">"เจ้าของ"</string>
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 205dfa6e9995..cf6314fbf6c9 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Pinapayagan ang app na magsulat ng mga bagong salita sa diksyunaryo ng user."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"subukan ang access sa pinoprotektahang storage"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"subukan ang access sa pinoprotektahang storage"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Pinapayagan ang app na subukan ang isang pahintulot para sa USB storage na magiging availabe sa mga device sa hinaharap."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Binibigyang-daan ang app na subukan ang isang pagpapahintulot para sa USB storage na magiging availabe sa mga device sa hinaharap."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Pinapayagan ang app na subukan ang isang pahintulot para sa SD card na magiging available sa mga device sa hinaharap."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"bago tanggal laman USB storage"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"baguhin o tanggalin ang mga nilalaman ng iyong SD card"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Pinagana ang accessibility."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Nakansela ang pagiging naa-access."</string>
<string name="user_switched" msgid="3768006783166984410">"Kasalukuyang user <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"May-ari"</string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 5056a79c8059..f037250247fb 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -585,7 +585,8 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Uygulamaya, kullanıcı sözlüğüne yeni kelimeler yazma izni verir."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"korumalı depolama birimine erişimi test et"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"korumalı depolama birimine erişimi test et"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Uygulamaya gelecekteki cihazlarda kullanılabilecek USB belleğe ilişkin bir izni test etme izni verir."</string>
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
+ <skip />
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Uygulamaya gelecekteki cihazlarda kullanılabilecek SD karta ilişkin bir izni test etme olanağı verir."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"USB belleğimin içeriğini değiştir veya sil"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"SD kartın içeriğini değiştir veya sil"</string>
@@ -1425,4 +1426,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Erişilebilirlik etkinleştirildi."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Erişilebilirlik iptal edildi."</string>
<string name="user_switched" msgid="3768006783166984410">"Geçerli kullanıcı: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Sahibi"</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index acfa7bb2aa25..8ce89f28b8ee 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Дозволяє програмі писати нові слова в словник користувача."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"тестувати доступ до захищеної пам’яті"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"тестувати доступ до захищеної пам’яті"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Дозволяє програмі тестувати дозвіл для носія USB, який буде доступний на майбутніх пристроях."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Дозволяє програмі тестувати дозвіл для носія USB, який буде доступний на майбутніх пристроях."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Дозволяє програмі тестувати дозвіл для карти SD, яка буде доступна на майбутніх пристроях."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"змінювати чи видаляти вміст USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"змінювати чи видаляти вміст на карті SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Доступність увімкнено."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Доступність скасовано."</string>
<string name="user_switched" msgid="3768006783166984410">"Поточний користувач: <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Власник"</string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 97dcf3edfec7..160dda7b916a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Cho phép ứng dụng ghi từ mới vào từ điển của người dùng."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"kiểm tra quyền truy cập vào bộ nhớ được bảo vệ"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"kiểm tra quyền truy cập vào bộ nhớ được bảo vệ"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Cho phép ứng dụng kiểm tra quyền cho bộ lưu trữ USB trên các thiết bị trong tương lai."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Cho phép ứng dụng kiểm tra quyền của bộ lưu trữ USB trên các thiết bị trong tương lai."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Cho phép ứng dụng kiểm tra quyền cho thẻ SD sẽ các thiết bị trong tương lai."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"sửa đổi hoặc xóa nội dung của bộ lưu trữ USB của bạn"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"sửa đổi hoặc xóa nội dung của thẻ SD của bạn"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Trợ năng đã được bật."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Đã hủy trợ năng."</string>
<string name="user_switched" msgid="3768006783166984410">"Người dùng hiện tại <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Chủ sở hữu"</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index b0c363bc64c4..028b4767d331 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -585,7 +585,8 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"允许应用向用户词典中写入新词。"</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"测试对受保护存储空间的访问权限"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"测试对受保护存储空间的访问权限"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"允许该应用测试将对以后的设备开放的 USB 存储设备权限。"</string>
+ <!-- no translation found for permdesc_sdcardRead (3642473292348132072) -->
+ <skip />
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"允许该应用测试将对以后的设备开放的 SD 卡权限。"</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"修改或删除您的 USB 存储设备中的内容"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"修改或删除您的 SD 卡中的内容"</string>
@@ -1425,4 +1426,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"辅助功能已启用。"</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"已取消辅助功能。"</string>
<string name="user_switched" msgid="3768006783166984410">"当前用户是<xliff:g id="NAME">%1$s</xliff:g>。"</string>
+ <string name="owner_name" msgid="2716755460376028154">"机主"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 635b167f82aa..10af434423c3 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"允許應用程式將新字詞寫入使用者的字典。"</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"測試能否存取受保護的儲存裝置"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"測試能否存取受保護的儲存裝置"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"允許應用程式測試未來裝置將支援的 USB 儲存裝置權限。"</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"允許應用程式測試未來裝置將支援的 USB 儲存裝置權限。"</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"允許應用程式測試未來裝置將支援的 SD 卡權限。"</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"修改或刪除 USB 儲存裝置的內容"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"修改或刪除 SD 卡的內容"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"協助工具已啟用。"</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"協助工具已取消。"</string>
<string name="user_switched" msgid="3768006783166984410">"目前的使用者是 <xliff:g id="NAME">%1$s</xliff:g>。"</string>
+ <string name="owner_name" msgid="2716755460376028154">"擁有者"</string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index cb2f6ebc157e..02f97053b4c2 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -585,7 +585,7 @@
<string name="permdesc_writeDictionary" msgid="8185385716255065291">"Ivumela insiza ukuthi ibhale amagama amasha esichazinimazwi."</string>
<string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"ukufinyelela kokuhlola esilondolozini esivikelekile"</string>
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"ukufinyelela kokuhlola esilondolozini esivikelekile"</string>
- <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"Ivumela uhlelo lokusebenza ukuhlola imvume yokugciniwe okufinyeleleka nge-USB okuzotholakala kumadivayisi alandelayo."</string>
+ <string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Ivumela uhlelo lokusebenza ukuhlola imvume yokugciniwe okufinyeleleka nge-USB okuzotholakala kumadivayisi alandelayo."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Ivumela uhlelo lokusebenza ukuhlola imvume yekhadi le-SD okuzotholakala kumadivayisi alandelayo."</string>
<string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"guqula noma ususe okuqukethwe kwakho okugciniwe okufinyeleleka nge-USB"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"shintsha noma ususe okuqukethwe ekhadini lakho le-SD"</string>
@@ -1425,4 +1425,5 @@
<string name="accessibility_enabled" msgid="1381972048564547685">"Ukufinyelela kunikwe amandla."</string>
<string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ukufinyelela kukhanseliwe."</string>
<string name="user_switched" msgid="3768006783166984410">"Umsebenzisi wamanje <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="owner_name" msgid="2716755460376028154">"Umnikazi"</string>
</resources>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 86154762d597..1e966f7cc508 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -398,4 +398,25 @@
<item>@null</item>
</array>
+ <array name="lockscreen_targets_unlock_only">
+ <item>@*android:drawable/ic_lockscreen_unlock</item>
+ </array>
+
+ <array name="lockscreen_target_descriptions_unlock_only">
+ <item>@*android:string/description_target_unlock</item>
+ </array>
+
+ <!-- list of 3- or 4-letter mnemonics for a 10-key numeric keypad -->
+ <string-array translatable="false" name="lockscreen_num_pad_klondike">
+ <item></item><!-- 0 -->
+ <item></item><!-- 1 -->
+ <item>ABC</item><!-- 2 -->
+ <item>DEF</item><!-- 3 -->
+ <item>GHI</item><!-- 4 -->
+ <item>JKL</item><!-- 5 -->
+ <item>MNO</item><!-- 6 -->
+ <item>PQRS</item><!-- 7 -->
+ <item>TUV</item><!-- 8 -->
+ <item>WXYZ</item><!-- 9 -->
+ </string-array>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3550df93c4f2..d186c4a85f66 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5464,6 +5464,14 @@
<!-- Used when the handle shouldn't wait to be hit before following the finger -->
<attr name="alwaysTrackFinger"/>
+
+ <!-- Location along the circle of the first item, in degrees.-->
+ <attr name="firstItemOffset" format="float" />
+
+ <!-- Causes targets to snap to the finger location on activation. -->
+ <attr name="magneticTargets" format="boolean" />
+
+ <attr name="gravity" />
</declare-styleable>
<!-- =============================== -->
@@ -5759,14 +5767,6 @@
<!-- PagedView specific attributes. These attributes are used to customize
a PagedView view in XML files. -->
<declare-styleable name="PagedView">
- <!-- A spacing override for the icons within a page -->
- <attr name="pageLayoutWidthGap" format="dimension" />
- <attr name="pageLayoutHeightGap" format="dimension" />
- <!-- The padding of the pages that are dynamically created per page -->
- <attr name="pageLayoutPaddingTop" format="dimension" />
- <attr name="pageLayoutPaddingBottom" format="dimension" />
- <attr name="pageLayoutPaddingLeft" format="dimension" />
- <attr name="pageLayoutPaddingRight" format="dimension" />
<!-- The space between adjacent pages of the PagedView. -->
<attr name="pageSpacing" format="dimension" />
<!-- The padding for the scroll indicator area -->
@@ -5781,10 +5781,58 @@
<attr name="leftToRight" format="boolean" />
</declare-styleable>
+ <!-- Some child types have special behavior. -->
+ <attr name="layout_childType">
+ <!-- No special behavior. Layout will proceed as normal. -->
+ <enum name="none" value="0" />
+ <!-- Widget container.
+ This will be resized in response to certain events. -->
+ <enum name="widget" value="1" />
+ <!-- Security challenge container.
+ This will be dismissed/shown in response to certain events,
+ possibly obscuring widget elements. -->
+ <enum name="challenge" value="2" />
+ <!-- User switcher.
+ This will consume space from the total layout area. -->
+ <enum name="userSwitcher" value="3" />
+ <!-- Scrim. This will block access to child views that
+ come before it in the child list in bouncer mode. -->
+ <enum name="scrim" value="4" />
+ </attr>
+
+ <declare-styleable name="SlidingChallengeLayout">
+ <attr name="dragHandle" format="reference" />
+ <attr name="dragIcon" format="reference" />
+ </declare-styleable>
+
+ <declare-styleable name="SlidingChallengeLayout_Layout">
+ <attr name="layout_childType" />
+ </declare-styleable>
+
<!-- Attributes that can be used with <code>&lt;FragmentBreadCrumbs&gt;</code>
tags. -->
<declare-styleable name="FragmentBreadCrumbs">
<attr name="gravity" />
</declare-styleable>
+ <declare-styleable name="MultiPaneChallengeLayout">
+ <!-- Influences how layout_centerWithinArea behaves -->
+ <attr name="orientation" />
+ </declare-styleable>
+
+ <declare-styleable name="MultiPaneChallengeLayout_Layout">
+ <!-- Percentage of the screen this child should consume or center within.
+ If 0/default, the view will be measured by standard rules
+ as if this were a FrameLayout. -->
+ <attr name="layout_centerWithinArea" format="float" />
+ <attr name="layout_childType" />
+ <attr name="layout_gravity" />
+ <attr name="layout_maxWidth" format="dimension" />
+ <attr name="layout_maxHeight" />
+ </declare-styleable>
+
+ <declare-styleable name="NumPadKey">
+ <attr name="digit" format="integer" />
+ <attr name="textView" format="reference" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index f9762b18f9ad..d4ead012f3a0 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -15,6 +15,7 @@
-->
<resources>
+ <bool name="kg_enable_camera_default_widget">true</bool>
<bool name="action_bar_embed_tabs">true</bool>
<bool name="action_bar_embed_tabs_pre_jb">false</bool>
<bool name="split_action_bar_is_narrow">true</bool>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 6a93f3081863..b19e23df9825 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -190,5 +190,10 @@
<drawable name="notification_template_icon_bg">#3333B5E5</drawable>
<drawable name="notification_template_icon_low_bg">#0cffffff</drawable>
+ <!-- Keyguard colors -->
+ <color name="keyguard_avatar_frame_color">#ffffffff</color>
+ <color name="keyguard_avatar_frame_shadow_color">#80000000</color>
+ <color name="keyguard_avatar_nick_color">#ffffffff</color>
+ <color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 948a3d3c40c8..b830e798f613 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -242,8 +242,8 @@
<dimen name="notification_subtext_size">12dp</dimen>
<!-- Keyguard dimensions -->
- <!-- Width of security view in keyguard. -->
- <dimen name="kg_glow_pad_size">500dp</dimen>
+ <!-- TEMP -->
+ <dimen name="kg_security_panel_height">600dp</dimen>
<!-- Height of security view in keyguard. -->
<dimen name="kg_security_view_height">0dp</dimen>
@@ -266,7 +266,7 @@
<!-- Size of margin on the right of keyguard's status view -->
<dimen name="kg_status_line_font_right_margin">16dp</dimen>
- <!-- Top margin for the clock view -->
+ <!-- Top margin for the clock view -->
<dimen name="kg_clock_top_margin">-16dp</dimen>
<!-- Horizontal gap between keys in PIN and SIM PIN numeric keyboards in keyguard -->
@@ -305,4 +305,23 @@
<!-- Margin around the various security views -->
<dimen name="keyguard_security_view_margin">8dp</dimen>
+
+ <!-- Margin around the various security views -->
+ <dimen name="keyguard_muliuser_selector_margin">8dp</dimen>
+
+ <!-- Stroke width of the frame for the circular avatars. -->
+ <dimen name="keyguard_avatar_frame_stroke_width">2dp</dimen>
+
+ <!-- Shadow radius under the frame for the circular avatars. -->
+ <dimen name="keyguard_avatar_frame_shadow_radius">1dp</dimen>
+
+ <!-- Size of the avator on hte multiuser lockscreen. -->
+ <dimen name="keyguard_avatar_size">66dp</dimen>
+
+ <!-- Size of the text under the avator on the multiuser lockscreen. -->
+ <dimen name="keyguard_avatar_name_size">10sp</dimen>
+
+ <!-- Size of the region along the edge of the screen that will accept
+ swipes to scroll the widget area. -->
+ <dimen name="kg_edge_swipe_region_size">24dp</dimen>
</resources>
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index 6d49a91e4d26..d1df2a4ca318 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -17,6 +17,8 @@
*/
-->
<resources>
+ <integer name="kg_carousel_angle">75</integer>
<integer name="kg_security_flip_duration">75</integer>
<integer name="kg_security_fade_duration">75</integer>
+ <integer name="kg_glowpad_rotation_offset">0</integer>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f8dbd84da82c..159eec181275 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1721,7 +1721,7 @@
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_sdcardRead" product="default">test access to protected storage</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
- <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to test a permission for USB storage that will be availabe on future devices. </string>
+ <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to test a permission for USB storage that will be available on future devices. </string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_sdcardRead" product="default">Allows the app to test a permission for the SD card that will be available on future devices.</string>
@@ -2272,12 +2272,24 @@
<!-- Accessibility description sent when user completes drawing a pattern. [CHAR LIMIT=NONE] -->
<string name="lockscreen_access_pattern_detected">Pattern completed</string>
- <!-- Accessibility description sent when user changes the current lock screen widget. [CHAR_LIMIT=none] -->
- <string name="keyguard_accessibility_widget_changed">%1$s. Widget %2$d of %3$d.</string>
+ <!-- Accessibility description of the add widget button. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_add_widget">Add widget.</string>
+ <!-- Accessibility description of the empty sidget slot (place holder for a new widget). [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_widget_empty_slot">Empty</string>
+ <!-- Accessibility description of the unlock area. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_unlock_area">Unlock area.</string>
+ <!-- Accessibility description of the event of expanding an unlock area. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_unlock_area_expanded">Unlock area expanded.</string>
+ <!-- Accessibility description of the event of collapsing an unlock area. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_unlock_area_collapsed">Unlock area collapsed.</string>
+ <!-- Accessibility description of a lock screen widget. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_widget">%1$s widget.</string>
<!-- Accessibility description of the lock screen user selector widget. [CHAR_LIMIT=none] -->
<string name="keyguard_accessibility_user_selector">User selector</string>
<!-- Accessibility description of the lock screen status widget. [CHAR_LIMIT=none] -->
<string name="keyguard_accessibility_status">Status</string>
+ <!-- Accessibility description of the camera widget. [CHAR_LIMIT=none] -->
+ <string name="keyguard_accessibility_camera">Camera</string>
<!-- Accessibility description of the lock media control widget. [CHAR_LIMIT=none] -->
<string name="keygaurd_accessibility_media_controls">Media controls</string>
@@ -3950,5 +3962,7 @@
<string name="enable_accessibility_canceled">Accessibility canceled.</string>
<!-- Text spoken when the current user is switched if accessibility is enabled. [CHAR LIMIT=none] -->
<string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
+ <!-- Default name of the owner user [CHAR LIMIT=20] -->
+ <string name="owner_name" msgid="3879126011135546571">Owner</string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 4371aec76228..180f864bb3c4 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -2478,4 +2478,30 @@ please see styles_device_defaults.xml.
<item name="android:contentDescription">@android:string/media_route_button_content_description</item>
</style>
+ <!-- Keyguard PIN pad styles -->
+ <style name="Widget.Button.NumPadKey"
+ parent="@android:style/Widget.Button">
+ <item name="android:singleLine">true</item>
+ <item name="android:padding">6dip</item>
+ <item name="android:gravity">left|center_vertical</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ <item name="android:textSize">34dp</item>
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">#ffffff</item>
+ </style>
+ <style name="TextAppearance.NumPadKey"
+ parent="@android:style/TextAppearance">
+ <item name="android:textSize">34dp</item>
+ <item name="android:fontFamily">sans-serif</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">#ffffff</item>
+ </style>
+ <style name="TextAppearance.NumPadKey.Klondike">
+ <item name="android:textSize">20dp</item>
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">#80ffffff</item>
+ </style>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8ef91df1853b..7ebf7e7bfd6b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -842,6 +842,7 @@
<java-symbol type="string" name="media_route_status_connecting" />
<java-symbol type="string" name="media_route_status_available" />
<java-symbol type="string" name="media_route_status_not_available" />
+ <java-symbol type="string" name="owner_name" />
<java-symbol type="plurals" name="abbrev_in_num_days" />
<java-symbol type="plurals" name="abbrev_in_num_hours" />
@@ -962,6 +963,7 @@
<java-symbol type="drawable" name="status_bar_background" />
<java-symbol type="drawable" name="sym_keyboard_shift" />
<java-symbol type="drawable" name="sym_keyboard_shift_locked" />
+ <java-symbol type="drawable" name="sym_keyboard_return_holo" />
<java-symbol type="drawable" name="tab_bottom_left" />
<java-symbol type="drawable" name="tab_bottom_left_v4" />
<java-symbol type="drawable" name="tab_bottom_right" />
@@ -1086,8 +1088,8 @@
<java-symbol type="layout" name="notification_template_inbox" />
<java-symbol type="layout" name="keyguard_multi_user_avatar" />
<java-symbol type="layout" name="keyguard_multi_user_selector_widget" />
- <java-symbol type="layout" name="keyguard_widget_region" />
<java-symbol type="layout" name="sms_short_code_confirmation_dialog" />
+ <java-symbol type="layout" name="keyguard_add_widget" />
<java-symbol type="anim" name="slide_in_child_bottom" />
<java-symbol type="anim" name="slide_in_right" />
@@ -1177,6 +1179,7 @@
<java-symbol type="array" name="lockscreen_targets_when_silent" />
<java-symbol type="array" name="lockscreen_targets_when_soundon" />
<java-symbol type="array" name="lockscreen_targets_with_camera" />
+ <java-symbol type="array" name="lockscreen_num_pad_klondike" />
<java-symbol type="attr" name="actionModePopupWindowStyle" />
<java-symbol type="attr" name="dialogCustomTitleDecorLayout" />
<java-symbol type="attr" name="dialogTitleDecorLayout" />
@@ -1191,12 +1194,17 @@
<java-symbol type="bool" name="config_lidControlsSleep" />
<java-symbol type="bool" name="config_reverseDefaultRotation" />
<java-symbol type="bool" name="config_showNavigationBar" />
+ <java-symbol type="bool" name="kg_enable_camera_default_widget" />
<java-symbol type="bool" name="kg_share_status_area" />
- <java-symbol type="bool" name="kg_sim_puk_account_full_screen" />
+ <java-symbol type="bool" name="kg_sim_puk_account_full_screen" />
<java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
<java-symbol type="color" name="kg_multi_user_text_active" />
<java-symbol type="color" name="kg_multi_user_text_inactive" />
<java-symbol type="color" name="kg_widget_pager_gradient" />
+ <java-symbol type="color" name="keyguard_avatar_frame_color" />
+ <java-symbol type="color" name="keyguard_avatar_frame_pressed_color" />
+ <java-symbol type="color" name="keyguard_avatar_frame_shadow_color" />
+ <java-symbol type="color" name="keyguard_avatar_nick_color" />
<java-symbol type="dimen" name="navigation_bar_height" />
<java-symbol type="dimen" name="navigation_bar_height_landscape" />
<java-symbol type="dimen" name="navigation_bar_width" />
@@ -1204,6 +1212,10 @@
<java-symbol type="dimen" name="kg_widget_pager_horizontal_padding" />
<java-symbol type="dimen" name="kg_widget_pager_top_padding" />
<java-symbol type="dimen" name="kg_widget_pager_bottom_padding" />
+ <java-symbol type="dimen" name="keyguard_avatar_size" />
+ <java-symbol type="dimen" name="keyguard_avatar_frame_stroke_width" />
+ <java-symbol type="dimen" name="keyguard_avatar_frame_shadow_radius" />
+ <java-symbol type="dimen" name="kg_edge_swipe_region_size" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
<java-symbol type="drawable" name="ic_jog_dial_unlock" />
@@ -1222,6 +1234,7 @@
<java-symbol type="drawable" name="magnified_region_frame" />
<java-symbol type="drawable" name="menu_background" />
<java-symbol type="drawable" name="stat_sys_secure" />
+ <java-symbol type="drawable" name="kg_bouncer_bg_white" />
<java-symbol type="id" name="action_mode_bar_stub" />
<java-symbol type="id" name="alarm_status" />
<java-symbol type="id" name="backspace" />
@@ -1280,6 +1293,7 @@
<java-symbol type="id" name="keyguard_selector_view" />
<java-symbol type="id" name="keyguard_pattern_view" />
<java-symbol type="id" name="keyguard_password_view" />
+ <java-symbol type="id" name="keyguard_pin_view" />
<java-symbol type="id" name="keyguard_face_unlock_view" />
<java-symbol type="id" name="keyguard_sim_pin_view" />
<java-symbol type="id" name="keyguard_sim_puk_view" />
@@ -1304,12 +1318,15 @@
<java-symbol type="id" name="keyguard_users_grid" />
<java-symbol type="id" name="clock_text" />
<java-symbol type="id" name="clock_view" />
- <java-symbol type="id" name="kg_widget_region" />
- <java-symbol type="id" name="left_strip" />
- <java-symbol type="id" name="right_strip" />
<java-symbol type="id" name="keyguard_multi_user_selector" />
<java-symbol type="id" name="status_security_message" />
-
+ <java-symbol type="id" name="sliding_layout" />
+ <java-symbol type="id" name="keyguard_add_widget" />
+ <java-symbol type="id" name="keyguard_add_widget_view" />
+ <java-symbol type="id" name="sliding_layout" />
+ <java-symbol type="id" name="multi_pane_challenge" />
+ <java-symbol type="id" name="keyguard_user_selector" />
+ <java-symbol type="id" name="key_enter" />
<java-symbol type="integer" name="config_carDockRotation" />
<java-symbol type="integer" name="config_defaultUiModeType" />
<java-symbol type="integer" name="config_deskDockRotation" />
@@ -1318,6 +1335,7 @@
<java-symbol type="integer" name="config_lidOpenRotation" />
<java-symbol type="integer" name="config_longPressOnHomeBehavior" />
<java-symbol type="integer" name="kg_security_flip_duration" />
+ <java-symbol type="integer" name="kg_carousel_angle" />
<java-symbol type="layout" name="global_actions_item" />
<java-symbol type="layout" name="global_actions_silent_mode" />
<java-symbol type="layout" name="keyguard_screen_glogin_unlock" />
@@ -1334,6 +1352,7 @@
<java-symbol type="layout" name="keyguard_selector_view" />
<java-symbol type="layout" name="keyguard_pattern_view" />
<java-symbol type="layout" name="keyguard_password_view" />
+ <java-symbol type="layout" name="keyguard_pin_view" />
<java-symbol type="layout" name="keyguard_face_unlock_view" />
<java-symbol type="layout" name="keyguard_sim_pin_view" />
<java-symbol type="layout" name="keyguard_sim_puk_view" />
@@ -1403,6 +1422,9 @@
<java-symbol type="style" name="Animation.LockScreen" />
<java-symbol type="style" name="Theme.Dialog.RecentApplications" />
<java-symbol type="style" name="Theme.ExpandedMenu" />
+ <java-symbol type="style" name="Widget.Button.NumPadKey" />
+ <java-symbol type="style" name="TextAppearance.NumPadKey" />
+ <java-symbol type="style" name="TextAppearance.NumPadKey.Klondike" />
<java-symbol type="string" name="kg_emergency_call_label" />
<java-symbol type="string" name="kg_forgot_pattern_button_text" />
<java-symbol type="string" name="kg_wrong_pattern" />
diff --git a/docs/downloads/training/Animations.zip b/docs/downloads/training/Animations.zip
new file mode 100644
index 000000000000..5063dd1a5c84
--- /dev/null
+++ b/docs/downloads/training/Animations.zip
Binary files differ
diff --git a/docs/html/distribute/googleplay/publish/preparing.jd b/docs/html/distribute/googleplay/publish/preparing.jd
index 463343d2d7e5..a3538a95c0ee 100644
--- a/docs/html/distribute/googleplay/publish/preparing.jd
+++ b/docs/html/distribute/googleplay/publish/preparing.jd
@@ -15,7 +15,7 @@ page.title=Publishing Checklist for Google Play
<li><a href="#inapp-billing">9. Consider In-app Billing</a></li>
<li><a href="#pricing">10. Set prices for your apps</a></li>
<li><a href="#localize">11. Start localization early</a></li>
-<li><a href="#localize">12. Prepare promotional graphics</a></li>
+<li><a href="#graphics">12. Prepare promotional graphics</a></li>
<li><a href="#apk">13. Build the release-ready APK</a></li>
<li><a href="#product-page">14. Complete the product details</a></li>
<li><a href="#badges">15. Use Google Play badges</a></li>
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_10/land_back.png b/docs/html/distribute/promote/device-art-resources/nexus_10/land_back.png
new file mode 100644
index 000000000000..71bb52d29e26
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_10/land_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_10/land_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_10/land_fore.png
new file mode 100644
index 000000000000..d29f818b906e
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_10/land_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_10/land_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_10/land_shadow.png
new file mode 100644
index 000000000000..af1a2490d38b
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_10/land_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_10/port_back.png b/docs/html/distribute/promote/device-art-resources/nexus_10/port_back.png
new file mode 100644
index 000000000000..501690bde142
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_10/port_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_10/port_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_10/port_fore.png
new file mode 100644
index 000000000000..689a72a376a5
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_10/port_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_10/port_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_10/port_shadow.png
new file mode 100644
index 000000000000..b2265ef90bfb
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_10/port_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_10/thumb.png b/docs/html/distribute/promote/device-art-resources/nexus_10/thumb.png
new file mode 100644
index 000000000000..8d8dee97e01c
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_10/thumb.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_4/land_back.png b/docs/html/distribute/promote/device-art-resources/nexus_4/land_back.png
new file mode 100644
index 000000000000..57a011a7e67b
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_4/land_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_4/land_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_4/land_fore.png
new file mode 100644
index 000000000000..72c9654681ae
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_4/land_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_4/land_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_4/land_shadow.png
new file mode 100644
index 000000000000..d80a5fd89637
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_4/land_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_4/port_back.png b/docs/html/distribute/promote/device-art-resources/nexus_4/port_back.png
new file mode 100644
index 000000000000..e64fae465079
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_4/port_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_4/port_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_4/port_fore.png
new file mode 100644
index 000000000000..c9fb0629030e
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_4/port_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_4/port_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_4/port_shadow.png
new file mode 100644
index 000000000000..b2064a358b48
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_4/port_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_4/thumb.png b/docs/html/distribute/promote/device-art-resources/nexus_4/thumb.png
new file mode 100644
index 000000000000..2988dc951244
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_4/thumb.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png b/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png
index 2999f3563061..697fb7dfede2 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png
index cefdd35199bb..735262f696e4 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png
index 8f7aec726fdf..cfb7952d4b7d 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png b/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png
index b2908a86a4a3..5bb815a57f3d 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png
index 7f4b0b41e641..1be3b210a77d 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png
index c10bd533cb5f..7e8aff2bfa7e 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png b/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png
index 8b5cc5ab3005..b5db82e3f462 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art.jd b/docs/html/distribute/promote/device-art.jd
index 93f772a5cdd3..55b846ecd9a3 100644
--- a/docs/html/distribute/promote/device-art.jd
+++ b/docs/html/distribute/promote/device-art.jd
@@ -18,7 +18,9 @@ feature image or screenshots for your Google Play app listing.</p>
<p>Drag a screenshot from your desktop onto a device to the right.</p>
</div>
<div class="layout-content-col span-10">
- <ul id="device-list"></ul>
+ <ul class="device-list primary"></ul>
+ <a href="#" id="archive-expando">Older devices</a>
+ <ul class="device-list archive"></ul>
</div>
</div>
@@ -57,12 +59,12 @@ feature image or screenshots for your Google Play app listing.</p>
text-transform: uppercase;
}
- #device-list {
+ .device-list {
padding: 0;
margin: 0;
}
- #device-list li {
+ .device-list li {
display: inline-block;
vertical-align: bottom;
margin: 0;
@@ -70,11 +72,11 @@ feature image or screenshots for your Google Play app listing.</p>
text-align: center;
}
- #device-list li .thumb-container {
+ .device-list li .thumb-container {
display: inline-block;
}
- #device-list li .thumb-container img {
+ .device-list li .thumb-container img {
margin-bottom: 8px;
opacity: 0.6;
@@ -83,7 +85,7 @@ feature image or screenshots for your Google Play app listing.</p>
transition: transform 0.2s, opacity 0.2s;
}
- #device-list li.drag-hover .thumb-container img {
+ .device-list li.drag-hover .thumb-container img {
opacity: 1;
-webkit-transform: scale(1.1);
@@ -91,16 +93,35 @@ feature image or screenshots for your Google Play app listing.</p>
transform: scale(1.1);
}
- #device-list li .device-details {
+ .device-list li .device-details {
font-size: 13px;
line-height: 16px;
color: #888;
}
- #device-list li .device-url {
+ .device-list li .device-url {
font-weight: bold;
}
+ #archive-expando {
+ display: block;
+ font-size: 13px;
+ font-weight: bold;
+ color: #333;
+ text-transform: uppercase;
+ margin-top: 16px;
+ padding-top: 16px;
+ padding-left: 28px;
+ border-top: 1px solid transparent;
+ background: transparent url({@docRoot}assets/images/styles/disclosure_down.png)
+ no-repeat scroll 0 8px;
+ }
+
+ #archive-expando.expanded {
+ background-image: url({@docRoot}assets/images/styles/disclosure_up.png);
+ border-top: 1px solid #ccc;
+ }
+
#output {
color: #f44;
font-style: italic;
@@ -117,7 +138,7 @@ feature image or screenshots for your Google Play app listing.</p>
// Global constants
var MSG_INVALID_INPUT_IMAGE = 'Invalid screenshot provided. Screenshots must be PNG files '
- + 'matching the target device\'s screen resolution in either portrait or landscape.';
+ + 'matching the target device\'s screen aspect ratio in either portrait or landscape.';
var MSG_NO_INPUT_IMAGE = 'Drag a screenshot (in PNG format) from your desktop onto a '
+ 'target device above.'
var MSG_GENERATING_IMAGE = 'Generating device art&hellip;';
@@ -127,17 +148,44 @@ feature image or screenshots for your Google Play app listing.</p>
// Device manifest.
var DEVICES = [
{
+ id: 'nexus_4',
+ title: 'Nexus 4',
+ url: 'http://www.google.com/nexus/4/',
+ physicalSize: 4.7,
+ physicalHeight: 5.23,
+ density: 'XHDPI',
+ landRes: ['shadow', 'back', 'fore'],
+ landOffset: [349,214],
+ portRes: ['shadow', 'back', 'fore'],
+ portOffset: [213,350],
+ portSize: [768,1280]
+ },
+ {
id: 'nexus_7',
title: 'Nexus 7',
- url: 'http://www.android.com/devices/detail/nexus-7',
+ url: 'http://www.google.com/nexus/7/',
physicalSize: 7,
physicalHeight: 7.81,
- density: 213,
+ density: '213dpi',
landRes: ['shadow', 'back', 'fore'],
- landOffset: [363,260],
+ landOffset: [315,270],
portRes: ['shadow', 'back', 'fore'],
- portOffset: [265,341],
- portSize: [800,1280],
+ portOffset: [264,311],
+ portSize: [800,1280]
+ },
+ {
+ id: 'nexus_10',
+ title: 'Nexus 10',
+ url: 'http://www.google.com/nexus/10/',
+ physicalSize: 10,
+ physicalHeight: 7,
+ actualResolution: [1600,2560],
+ density: 'XHDPI',
+ landRes: ['shadow', 'back', 'fore'],
+ landOffset: [227,217],
+ portRes: ['shadow', 'back', 'fore'],
+ portOffset: [217,223],
+ portSize: [800,1280]
},
{
id: 'xoom',
@@ -145,12 +193,13 @@ feature image or screenshots for your Google Play app listing.</p>
url: 'http://www.google.com/phone/detail/motorola-xoom',
physicalSize: 10,
physicalHeight: 6.61,
- density: 160,
+ density: 'MDPI',
landRes: ['shadow', 'back', 'fore'],
landOffset: [218,191],
portRes: ['shadow', 'back', 'fore'],
portOffset: [199,200],
portSize: [800,1280],
+ archived: true
},
{
id: 'galaxy_nexus',
@@ -158,12 +207,13 @@ feature image or screenshots for your Google Play app listing.</p>
url: 'http://www.android.com/devices/detail/galaxy-nexus',
physicalSize: 4.65,
physicalHeight: 5.33,
- density: 320,
+ density: 'XHDPI',
landRes: ['shadow', 'back', 'fore'],
landOffset: [371,199],
portRes: ['shadow', 'back', 'fore'],
portOffset: [216,353],
portSize: [720,1280],
+ archived: true
},
{
id: 'nexus_s',
@@ -171,12 +221,13 @@ feature image or screenshots for your Google Play app listing.</p>
url: 'http://www.google.com/phone/detail/nexus-s',
physicalSize: 4.0,
physicalHeight: 4.88,
- density: 240,
+ density: 'HDPI',
landRes: ['shadow', 'back', 'fore'],
landOffset: [247,135],
portRes: ['shadow', 'back', 'fore'],
portOffset: [134,247],
portSize: [480,800],
+ archived: true
}
];
@@ -250,13 +301,23 @@ feature image or screenshots for your Google Play app listing.</p>
$('#output').html(MSG_NO_INPUT_IMAGE);
$('#frame-customizations').hide();
+ $('.device-list.archive').hide();
$('#output-shadow, #output-glare').click(function() {
- createFrame(g_currentDevice, g_currentImage);
+ createFrame();
});
// Build device list.
$.each(DEVICES, function() {
+ var resolution = this.actualResolution || this.portSize;
+ var scaleFactorText = '';
+ if (resolution[0] != this.portSize[0]) {
+ scaleFactorText = '<br>' + (100 * (this.portSize[0] / resolution[0])).toFixed(0) +
+ '% size output';
+ } else {
+ scaleFactorText = '<br>&nbsp;';
+ }
+
$('<li>')
.append($('<div>')
.addClass('thumb-container')
@@ -269,14 +330,26 @@ feature image or screenshots for your Google Play app listing.</p>
.html((this.url
? ('<a class="device-url" href="' + this.url + '">' + this.title + '</a>')
: this.title) +
- '<br>' + this.physicalSize + '" @ ' + this.density + 'dpi' +
- '<br>' + this.portSize[0] + 'x' + this.portSize[1]))
+ '<br>' + this.physicalSize + '" @ ' + this.density +
+ '<br>' + (resolution[0] + 'x' + resolution[1]) + scaleFactorText))
.data('deviceId', this.id)
- .appendTo('#device-list');
+ .appendTo(this.archived ? '.device-list.archive' : '.device-list.primary');
+ });
+
+ // Set up "older devices" expando.
+ $('#archive-expando').click(function() {
+ if ($(this).hasClass('expanded')) {
+ $(this).removeClass('expanded');
+ $('.device-list.archive').hide();
+ } else {
+ $(this).addClass('expanded');
+ $('.device-list.archive').show();
+ }
+ return false;
});
// Set up drag and drop.
- $('#device-list li')
+ $('.device-list li')
.live('dragover', function(evt) {
$(this).addClass('drag-hover');
evt.dataTransfer.dropEffect = 'link';
@@ -335,27 +408,19 @@ feature image or screenshots for your Google Play app listing.</p>
*/
function createFrame() {
var port;
- if (g_currentImage.naturalWidth == g_currentDevice.portSize[0] &&
- g_currentImage.naturalHeight == g_currentDevice.portSize[1]) {
- if (!g_currentDevice.portRes) {
- alert('No portrait frame is currently available for this device.');
- $('#output').html(MSG_NO_INPUT_IMAGE);
- return;
- }
+
+ var aspect1 = g_currentImage.naturalWidth / g_currentImage.naturalHeight;
+ var aspect2 = g_currentDevice.portSize[0] / g_currentDevice.portSize[1];
+
+ if (aspect1 == aspect2) {
port = true;
- } else if (g_currentImage.naturalWidth == g_currentDevice.portSize[1] &&
- g_currentImage.naturalHeight == g_currentDevice.portSize[0]) {
- if (!g_currentDevice.landRes) {
- alert('No landscape frame is currently available for this device.');
- $('#output').html(MSG_NO_INPUT_IMAGE);
- return;
- }
+ } else if (aspect1 == 1 / aspect2) {
port = false;
} else {
- alert('Screenshots for ' + g_currentDevice.title + ' must be ' +
- g_currentDevice.portSize[0] + 'x' + g_currentDevice.portSize[1] +
- ' or ' +
- g_currentDevice.portSize[1] + 'x' + g_currentDevice.portSize[0] + '.');
+ alert('The screenshot must have an aspect ratio of ' +
+ aspect2.toFixed(3) + ' or ' + (1 / aspect2).toFixed(3) +
+ ' (ideally ' + g_currentDevice.portSize[0] + 'x' + g_currentDevice.portSize[1] +
+ ' or ' + g_currentDevice.portSize[1] + 'x' + g_currentDevice.portSize[0] + ').');
$('#output').html(MSG_INVALID_INPUT_IMAGE);
return;
}
@@ -378,6 +443,9 @@ feature image or screenshots for your Google Play app listing.</p>
var width = resourceImages['back'].naturalWidth;
var height = resourceImages['back'].naturalHeight;
var offset = port ? g_currentDevice.portOffset : g_currentDevice.landOffset;
+ var size = port
+ ? g_currentDevice.portSize
+ : [g_currentDevice.portSize[1], g_currentDevice.portSize[0]];
var canvas = document.createElement('canvas');
canvas.width = width;
@@ -388,7 +456,9 @@ feature image or screenshots for your Google Play app listing.</p>
ctx.drawImage(resourceImages['shadow'], 0, 0);
}
ctx.drawImage(resourceImages['back'], 0, 0);
- ctx.drawImage(g_currentImage, offset[0], offset[1]);
+ ctx.fillStyle = '#000';
+ ctx.fillRect(offset[0], offset[1], size[0], size[1]);
+ ctx.drawImage(g_currentImage, offset[0], offset[1], size[0], size[1]);
if (resourceImages['fore'] && $('#output-glare').is(':checked')) {
ctx.drawImage(resourceImages['fore'], 0, 0);
}
diff --git a/docs/html/training/animation/anim_card_flip.mp4 b/docs/html/training/animation/anim_card_flip.mp4
new file mode 100755
index 000000000000..e885f987c413
--- /dev/null
+++ b/docs/html/training/animation/anim_card_flip.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_card_flip.ogv b/docs/html/training/animation/anim_card_flip.ogv
new file mode 100755
index 000000000000..33cd86c23e09
--- /dev/null
+++ b/docs/html/training/animation/anim_card_flip.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_card_flip.webm b/docs/html/training/animation/anim_card_flip.webm
new file mode 100755
index 000000000000..a670d7831c8a
--- /dev/null
+++ b/docs/html/training/animation/anim_card_flip.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_crossfade.mp4 b/docs/html/training/animation/anim_crossfade.mp4
new file mode 100644
index 000000000000..ced7cc9e7c53
--- /dev/null
+++ b/docs/html/training/animation/anim_crossfade.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_crossfade.ogv b/docs/html/training/animation/anim_crossfade.ogv
new file mode 100644
index 000000000000..7ec417a802b8
--- /dev/null
+++ b/docs/html/training/animation/anim_crossfade.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_crossfade.webm b/docs/html/training/animation/anim_crossfade.webm
new file mode 100644
index 000000000000..21e722888797
--- /dev/null
+++ b/docs/html/training/animation/anim_crossfade.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_layout_changes.mp4 b/docs/html/training/animation/anim_layout_changes.mp4
new file mode 100644
index 000000000000..0709601f432f
--- /dev/null
+++ b/docs/html/training/animation/anim_layout_changes.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_layout_changes.ogv b/docs/html/training/animation/anim_layout_changes.ogv
new file mode 100644
index 000000000000..75f5250107f6
--- /dev/null
+++ b/docs/html/training/animation/anim_layout_changes.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_layout_changes.webm b/docs/html/training/animation/anim_layout_changes.webm
new file mode 100644
index 000000000000..a99a5666ba75
--- /dev/null
+++ b/docs/html/training/animation/anim_layout_changes.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.mp4 b/docs/html/training/animation/anim_screenslide.mp4
new file mode 100755
index 000000000000..3e6502642e64
--- /dev/null
+++ b/docs/html/training/animation/anim_screenslide.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.ogv b/docs/html/training/animation/anim_screenslide.ogv
new file mode 100755
index 000000000000..c45ebd44db53
--- /dev/null
+++ b/docs/html/training/animation/anim_screenslide.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.webm b/docs/html/training/animation/anim_screenslide.webm
new file mode 100755
index 000000000000..c72adbcb1384
--- /dev/null
+++ b/docs/html/training/animation/anim_screenslide.webm
Binary files differ
diff --git a/docs/html/training/animation/anim_zoom.mp4 b/docs/html/training/animation/anim_zoom.mp4
new file mode 100644
index 000000000000..4326c35b3033
--- /dev/null
+++ b/docs/html/training/animation/anim_zoom.mp4
Binary files differ
diff --git a/docs/html/training/animation/anim_zoom.ogv b/docs/html/training/animation/anim_zoom.ogv
new file mode 100644
index 000000000000..e5793f3a7665
--- /dev/null
+++ b/docs/html/training/animation/anim_zoom.ogv
Binary files differ
diff --git a/docs/html/training/animation/anim_zoom.webm b/docs/html/training/animation/anim_zoom.webm
new file mode 100644
index 000000000000..b3b7566c5e8c
--- /dev/null
+++ b/docs/html/training/animation/anim_zoom.webm
Binary files differ
diff --git a/docs/html/training/animation/cardflip.jd b/docs/html/training/animation/cardflip.jd
new file mode 100644
index 000000000000..ab3eb3af8c0f
--- /dev/null
+++ b/docs/html/training/animation/cardflip.jd
@@ -0,0 +1,365 @@
+page.title=Displaying Card Flip Animations
+trainingnavtop=true
+
+@jd:body
+ <div id="tb-wrapper">
+ <div id="tb">
+ <h2>
+ This lesson teaches you to
+ </h2>
+ <ol>
+ <li>
+ <a href="#animators">Create the Animators</a>
+ </li>
+ <li>
+ <a href="#views">Create the Views</a>
+ </li>
+ <li>
+ <a href="#fragment">Create the Fragment</a>
+ </li>
+ <li>
+ <a href="#animate">Animate the Card Flip</a>
+ </li>
+ </ol>
+ </div>
+ </div>
+ <p> This lesson shows you how to do a card flip
+ animation with custom fragment animations.
+ Card flips animate between views of content by showing an animation that emulates
+ a card flipping over.
+ </p>
+ <p>Here's what a card flip looks like:
+ </p>
+
+ <div class="framed-galaxynexus-land-span-8">
+ <video class="play-on-hover" autoplay>
+ <source src="anim_card_flip.mp4" type="video/mp4">
+ <source src="anim_card_flip.webm" type="video/webm">
+ <source src="anim_card_flip.ogv" type="video/ogg">
+ </video>
+ </div>
+ <div class="figure-caption">
+ Card flip animation
+ <div class="video-instructions">&nbsp;</div>
+ </div>
+
+ <p>
+ If you want to jump ahead and see a full working example,
+ <a href="{@docRoot}shareables/training/Animations.zip">download</a> and
+ run the sample app and select the Card Flip example. See the following
+ files for the code implementation:
+ </p>
+ <ul>
+ <li>
+ <code>src/CardFlipActivity.java</code>
+ </li>
+ <li>
+ <code>animator/card_flip_right_in.xml</code>
+ </li>
+ <li>
+ <code>animator/card_flip_right_out.xml</code>
+ </li>
+ <li>
+ <code>animator/card_flip_right_in.xml</code>
+ </li>
+ <li>
+ <code>animator/card_flip_left_out.xml</code>
+ </li>
+ <li>
+ <code>layout/fragment_card_back.xml</code>
+ </li>
+ <li>
+ <code>layout/fragment_card_front.xml</code>
+ </li>
+ </ul>
+
+ <h2 id="animate">
+ Create the Animators
+ </h2>
+ <p>
+ Create the animations for the card flips. You'll need two animators for when the front
+ of the card animates out and to the left and in and from the left. You'll also need two animators
+ for when the back of the card animates in and from the right and out and to the right.
+ </p>
+ <h4>
+ card_flip_left_in.xml
+ </h4>
+<pre>
+&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;!-- Before rotating, immediately set the alpha to 0. --&gt;
+ &lt;objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:duration="0" /&gt;
+
+ &lt;!-- Rotate. --&gt;
+ &lt;objectAnimator
+ android:valueFrom="-180"
+ android:valueTo="0"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" /&gt;
+
+ &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 1. --&gt;
+ &lt;objectAnimator
+ android:valueFrom="0.0"
+ android:valueTo="1.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" /&gt;
+&lt;/set&gt;
+</pre>
+ <h4>
+ card_flip_left_out.xml
+ </h4>
+ <pre>
+&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;!-- Rotate. --&gt;
+ &lt;objectAnimator
+ android:valueFrom="0"
+ android:valueTo="180"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" /&gt;
+
+ &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 0. --&gt;
+ &lt;objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" /&gt;
+&lt;/set&gt;
+ </pre>
+ <h4>
+ card_flip_right_in.xml
+ </h4>
+ <pre>
+&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;!-- Before rotating, immediately set the alpha to 0. --&gt;
+ &lt;objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:duration="0" /&gt;
+
+ &lt;!-- Rotate. --&gt;
+ &lt;objectAnimator
+ android:valueFrom="180"
+ android:valueTo="0"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" /&gt;
+
+ &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 1. --&gt;
+ &lt;objectAnimator
+ android:valueFrom="0.0"
+ android:valueTo="1.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" /&gt;
+</set>
+
+</pre>
+ <h4>
+ card_flip_right_out.xml
+ </h4>
+ <pre>
+&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;!-- Rotate. --&gt;
+ &lt;objectAnimator
+ android:valueFrom="0"
+ android:valueTo="-180"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" /&gt;
+
+ &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 0. --&gt;
+ &lt;objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" /&gt;
+&lt;/set&gt;
+</pre>
+ <h2 id="views">
+ Create the Views
+ </h2>
+ <p>
+ Each side of the "card" is a separate layout that can contain any content you want,
+ such as two screens of text, two images, or any combination of views to flip between. You'll then
+ use the two layouts in the fragments that you'll later animate. The following layouts
+ create one side of a card that shows text:
+ </p>
+
+ <pre>
+&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="#a6c"
+ android:padding="16dp"
+ android:gravity="bottom"&gt;
+
+ &lt;TextView android:id="@android:id/text1"
+ style="?android:textAppearanceLarge"
+ android:textStyle="bold"
+ android:textColor="#fff"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/card_back_title" /&gt;
+
+ &lt;TextView style="?android:textAppearanceSmall"
+ android:textAllCaps="true"
+ android:textColor="#80ffffff"
+ android:textStyle="bold"
+ android:lineSpacingMultiplier="1.2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/card_back_description" /&gt;
+
+&lt;/LinearLayout&gt;
+</pre>
+<p>
+and the other side of the card that displays an {@link android.widget.ImageView}:
+</p>
+<pre>
+&lt;ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@drawable/image1"
+ android:scaleType="centerCrop"
+ android:contentDescription="@string/description_image_1" /&gt;
+</pre>
+ <h2 id="fragment">
+ Create the Fragment
+ </h2>
+ <p>
+ Create fragment classes for the front and back of the card. These classes return the layouts
+ that you created previously in the {@link android.app.Fragment#onCreateView onCreateView()} method
+ of each fragment. You can then create instances of this fragment in the parent activity
+ where you want to show the card. The following example shows nested fragment classes inside
+ of the parent activity that uses them:
+ </p>
+ <pre>
+public class CardFlipActivity extends Activity {
+ ...
+ /**
+ * A fragment representing the front of the card.
+ */
+ public class CardFrontFragment extends Fragment {
+ &#64;Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_card_front, container, false);
+ }
+ }
+
+ /**
+ * A fragment representing the back of the card.
+ */
+ public class CardBackFragment extends Fragment {
+ &#64;Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_card_back, container, false);
+ }
+ }
+}
+</pre>
+ <h2 id="animate">
+ Animate the Card Flip
+ </h2>
+
+ <p> Now, you'll need to display the fragments inside of a parent activity.
+ To do this, first create the layout for your activity. The following example creates a
+ {@link android.widget.FrameLayout} that you
+ can add fragments to at runtime:</p>
+
+ <pre>
+&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" /&gt;
+</pre>
+
+ <p>In the activity code, set the content view to be the layout that you just created. It's also
+ good idea to show a default fragment when the activity is created, so the following example
+ activity shows you how to display the front of the card by default:
+ </p>
+
+
+
+<pre>
+public class CardFlipActivity extends Activity {
+
+ &#64;Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_activity_card_flip);
+
+ if (savedInstanceState == null) {
+ getFragmentManager()
+ .beginTransaction()
+ .add(R.id.container, new CardFrontFragment())
+ .commit();
+ }
+ }
+ ...
+}
+</pre>
+ <p>
+ Now that you have the front of the card showing, you can show the back of the card
+ with the flip animation at an appropriate time. Create a method to show the other
+ side of the card that does the following things:
+ </p>
+ <ul>
+ <li>Sets the custom animations that you created earlier for the fragment transitions.
+ </li>
+ <li>Replaces the currently displayed fragment with a new fragment and animates this event
+ with the custom animations that you created.
+ </li>
+ <li>Adds the previously displayed fragment to the fragment back stack
+ so when the user presses the <em>Back</em> button, the card flips back over.
+ </li>
+ </ul>
+ <pre>
+private void flipCard() {
+ if (mShowingBack) {
+ getFragmentManager().popBackStack();
+ return;
+ }
+
+ // Flip to the back.
+
+ mShowingBack = true;
+
+ // Create and commit a new fragment transaction that adds the fragment for the back of
+ // the card, uses custom animations, and is part of the fragment manager's back stack.
+
+ getFragmentManager()
+ .beginTransaction()
+
+ // Replace the default fragment animations with animator resources representing
+ // rotations when switching to the back of the card, as well as animator
+ // resources representing rotations when flipping back to the front (e.g. when
+ // the system Back button is pressed).
+ .setCustomAnimations(
+ R.animator.card_flip_right_in, R.animator.card_flip_right_out,
+ R.animator.card_flip_left_in, R.animator.card_flip_left_out)
+
+ // Replace any fragments currently in the container view with a fragment
+ // representing the next page (indicated by the just-incremented currentPage
+ // variable).
+ .replace(R.id.container, new CardBackFragment())
+
+ // Add this transaction to the back stack, allowing users to press Back
+ // to get to the front of the card.
+ .addToBackStack(null)
+
+ // Commit the transaction.
+ .commit();
+}
+</pre> \ No newline at end of file
diff --git a/docs/html/training/animation/crossfade.jd b/docs/html/training/animation/crossfade.jd
new file mode 100644
index 000000000000..99e879bbc003
--- /dev/null
+++ b/docs/html/training/animation/crossfade.jd
@@ -0,0 +1,208 @@
+page.title=Crossfading Two Views
+trainingnavtop=true
+
+
+@jd:body
+
+ <div id="tb-wrapper">
+ <div id="tb">
+ <h2>
+ This lesson teaches you to:
+ </h2>
+ <ol>
+ <li>
+ <a href="#views">Create the Views</a>
+ </li>
+ <li>
+ <a href="#setup">Set up the Animation</a>
+ </li>
+ <li>
+ <a href="#animate">Crossfade the Views</a>
+ </li>
+ </ol>
+ </div>
+ </div>
+
+ <p>
+ Crossfade animations (also know as dissolve) gradually fade out one UI component while simultaneously fading in
+ another. This animation is useful for situations where you want to switch content or views
+ in your app. Crossfades are very subtle and short but offer a fluid transition from one screen to the
+ next. When you don't use them, however, transitions often feel abrupt or hurried.
+ </p>
+ <p>Here's an example of a crossfade from a progress indicator to some text content.
+ </p>
+
+<div class="framed-galaxynexus-land-span-8">
+ <video class="play-on-hover" autoplay>
+ <source src="anim_crossfade.mp4" type="video/mp4">
+ <source src="anim_crossfade.webm" type="video/webm">
+ <source src="anim_crossfade.ogv" type="video/ogg">
+ </video>
+</div>
+<div class="figure-caption">
+Crossfade animation
+ <div class="video-instructions">&nbsp;</div>
+</div>
+
+ <p>
+ If you want to jump ahead and see a full working example,
+ <a href="{@docRoot}shareables/training/Animations.zip">download</a>
+ and run the sample app and select the Crossfade example.
+ See the following files for the code implementation:
+ </p>
+ <ul>
+ <li>
+ <code>src/CrossfadeActivity.java</code>
+ </li>
+ <li>
+ <code>layout/activity_crossfade.xml</code>
+ </li>
+ <li>
+ <code>menu/activity_crossfade.xml</code>
+ </li>
+ </ul>
+ <h2 id="views">
+ Create the Views
+ </h2>
+ <p>
+ Create the two views that you want to crossfade. The following example creates a progress
+ indicator and a scrollable text view:
+ </p>
+ <pre>
+&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"&gt;
+
+ &lt;ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"&gt;
+
+ &lt;TextView style="?android:textAppearanceMedium"
+ android:lineSpacingMultiplier="1.2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/lorem_ipsum"
+ android:padding="16dp" /&gt;
+
+ &lt;/ScrollView&gt;
+
+ &lt;ProgressBar android:id="@+id/loading_spinner"
+ style="?android:progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" /&gt;
+
+&lt;/FrameLayout&gt;
+</pre>
+ <h2 id="setup">
+ Set up the Animation
+ </h2>
+ <p>
+ To set up the animation:
+ </p>
+ <ol>
+ <li>Create member variables for the views that you want to crossfade. You need
+ these references later when modifying the views during the animation.
+ </li>
+ <li>For the view that is being faded in, set its visibility to {@link
+ android.view.View#GONE}. This prevents the view from taking up layout space and omits it
+ from layout calculations, speeding up processing.
+ </li>
+ <li>Cache the <code>{@link android.R.integer#config_shortAnimTime}</code>
+ system property in a member variable. This property defines a standard
+ "short" duration for the animation. This duration is ideal for subtle animations or
+ animations that occur very frequently. {@link android.R.integer#config_longAnimTime} and
+ {@link android.R.integer#config_mediumAnimTime} are also available if you wish to use them.
+ </li>
+ </ol>
+ <p>
+ Here's an example using the layout from the previous code snippet as the activity content
+ view:
+ </p>
+ <pre>
+public class CrossfadeActivity extends Activity {
+
+ private View mContentView;
+ private View mLoadingView;
+ private int mShortAnimationDuration;
+
+ ...
+
+ &#64;Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_crossfade);
+
+ mContentView = findViewById(R.id.content);
+ mLoadingView = findViewById(R.id.loading_spinner);
+
+ // Initially hide the content view.
+ mContentView.setVisibility(View.GONE);
+
+ // Retrieve and cache the system's default "short" animation time.
+ mShortAnimationDuration = getResources().getInteger(
+ android.R.integer.config_shortAnimTime);
+ }
+
+</pre>
+ <h2 id="animate">
+ Crossfade the Views
+ </h2>
+ <p>
+ Now that the views are properly set up, crossfade them by doing the following:
+ </p>
+ <ol>
+ <li>For the view that is fading in, set the alpha value to <code>0</code> and the visibility
+ to {@link android.view.View#VISIBLE}. (Remember that it was initially set to {@link
+ android.view.View#GONE}.) This makes the view visible but completely transparent.
+ </li>
+ <li>For the view that is fading in, animate its alpha value from <code>0</code> to
+ <code>1</code>. At the same time, for the view that is fading out, animate the alpha value
+ from <code>1</code> to <code>0</code>.
+ </li>
+ <li>Using {@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()}
+ in an {@link android.animation.Animator.AnimatorListener}, set the visibility of the view
+ that was fading out to {@link android.view.View#GONE}. Even though the alpha value is <code>0</code>,
+ setting the view's visibility to {@link android.view.View#GONE} prevents the view from taking
+ up layout space and omits it from layout calculations, speeding up processing.
+ </li>
+ </ol>
+ <p>
+ The following method shows an example of how to do this:
+ </p>
+ <pre>
+private View mContentView;
+private View mLoadingView;
+private int mShortAnimationDuration;
+
+...
+
+private void crossfade() {
+
+ // Set the content view to 0% opacity but visible, so that it is visible
+ // (but fully transparent) during the animation.
+ mContentView.setAlpha(0f);
+ mContentView.setVisibility(View.VISIBLE);
+
+ // Animate the content view to 100% opacity, and clear any animation
+ // listener set on the view.
+ mContentView.animate()
+ .alpha(1f)
+ .setDuration(mShortAnimationDuration)
+ .setListener(null);
+
+ // Animate the loading view to 0% opacity. After the animation ends,
+ // set its visibility to GONE as an optimization step (it won't
+ // participate in layout passes, etc.)
+ mHideView.animate()
+ .alpha(0f)
+ .setDuration(mShortAnimationDuration)
+ .setListener(new AnimatorListenerAdapter() {
+ &#64;Override
+ public void onAnimationEnd(Animator animation) {
+ mHideView.setVisibility(View.GONE);
+ }
+ });
+}
+</pre> \ No newline at end of file
diff --git a/docs/html/training/animation/index.jd b/docs/html/training/animation/index.jd
new file mode 100644
index 000000000000..9cc7e6c405c0
--- /dev/null
+++ b/docs/html/training/animation/index.jd
@@ -0,0 +1,86 @@
+page.title=Adding Animations
+trainingnavtop=true
+startpage=true
+
+@jd:body
+ <div id="tb-wrapper">
+ <div id="tb">
+ <h2>
+ Dependencies and prerequisites
+ </h2>
+ <ul>
+ <li>Android 4.0 or later
+ </li>
+ <li>Experience building an Android <a href="{@docRoot}guide/topics/ui/index.html">User
+ Interface</a>
+ </li>
+ </ul>
+ <h2>
+ You should also read
+ </h2>
+ <ul>
+ <li>
+ <a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>
+ </li>
+ </ul>
+ <h2>
+ Try it out
+ </h2>
+ <div class="download-box">
+ <a href="{@docRoot}shareables/training/Animations.zip" class=
+ "button">Download the sample app</a>
+ <p class="filename">
+ Animations.zip
+ </p>
+ </div>
+ </div>
+ </div>
+ <p>
+ Animations can add subtle visual cues that notify users about what's going on in your app and
+ improve their mental model of your app's interface. Animations are especially useful when the
+ screen changes state, such as when content loads or new actions become available. Animations
+ can also add a polished look to your app, which gives your app a higher quality feel.
+ </p>
+ <p>
+ Keep in mind though, that overusing animations or using them at the wrong time can be
+ detrimental, such as when they cause delays. This training class shows you how to
+ implement some common types of animations that can increase usability and add flair without
+ annoying your users.
+ </p>
+
+ <h2>
+ Lessons
+ </h2>
+ <dl>
+ <dt>
+ <b><a href="crossfade.html">Crossfading Two Views</a></b>
+ </dt>
+ <dd>
+ Learn how to crossfade between two overlapping views. This lesson shows you how to crossfade a progress
+ indicator to a view that contains text content.
+ </dd>
+ <dt>
+ <b><a href="screen-slide.html">Using ViewPager for Screen Slides</a></b>
+ </dt>
+ <dd>
+ Learn how to animate between horizontally adjacent screens with a sliding transition.
+ </dd>
+ <dt>
+ <b><a href="cardflip.html">Displaying Card Flip Animations</a></b>
+ </dt>
+ <dd>
+ Learn how to animate between two views with a flipping motion.
+ </dd>
+ <dt>
+ <b><a href="zoom.html">Zooming a View</a></b>
+ </dt>
+ <dd>
+ Learn how to enlarge views with a touch-to-zoom animation.
+ </dd>
+ <dt>
+ <b><a href="layout.html">Animating Layout Changes</a></b>
+ </dt>
+ <dd>
+ Learn how to enable built-in animations when adding, removing, or updating child views in a layout.
+ </dd>
+ </dl> \ No newline at end of file
diff --git a/docs/html/training/animation/layout.jd b/docs/html/training/animation/layout.jd
new file mode 100644
index 000000000000..b8e00777dcbb
--- /dev/null
+++ b/docs/html/training/animation/layout.jd
@@ -0,0 +1,77 @@
+page.title=Animating Layout Changes
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to:</h2>
+ <ol>
+ <li><a href="#views">Create the Layout</a></li>
+ <li><a href="#add">Add, Update, or Remove Items from the Layout</a></li>
+ </ol>
+
+</div>
+</div>
+
+ <p>A layout animation is a pre-loaded animation that the system runs each time you make a change
+ to the layout configuration. All you need to do is set an attribute in the layout to tell the
+ Android system to animate these layout changes, and system-default animations are carried out for you.
+ </p>
+<p class="note"><strong>Tip</strong>: If you want to supply custom layout animations,
+create a {@link android.animation.LayoutTransition} object and supply it to
+the layout with the {@link android.view.ViewGroup#setLayoutTransition setLayoutTransition()}
+method.
+</p>
+ Here's what a default layout animation looks like when adding items to a list:
+</p>
+
+ <div class="framed-galaxynexus-land-span-8">
+ <video class="play-on-hover" autoplay>
+ <source src="anim_layout_changes.mp4" type="video/mp4">
+ <source src="anim_layout_changes.webm" type="video/webm">
+ <source src="anim_layout_changes.ogv" type="video/ogg">
+ </video>
+ </div>
+ <div class="figure-caption">
+ Layout animation
+ <div class="video-instructions">&nbsp;</div>
+ </div>
+
+<p>If you want to jump ahead and see a full working example,
+<a href="{@docRoot}shareables/training/Animations.zip">download</a> and
+run the sample app and select the Crossfade example. See the following files for the
+code implementation:</p>
+<ol>
+ <li><code>src/LayoutChangesActivity.java</code></li>
+ <li><code>layout/activity_layout_changes.xml</code></li>
+ <li><code>menu/activity_layout_changes.xml</code></li>
+</ol>
+
+<h2 id="views">Create the Layout</h2>
+<p>In your activity's layout XML file, set the <code>android:animateLayoutChanges</code>
+ attribute to <code>true</code> for the layout that you want to enable animations for.
+ For instance:</p>
+
+<pre>
+&lt;LinearLayout android:id="@+id/container"
+ android:animateLayoutChanges="true"
+ ...
+/&gt;
+</pre>
+
+<h2 id="activity">Add, Update, or Remove Items from the Layout</h2>
+<p>
+Now, all you need to do is add, remove, or update items in the layout
+and the items are animated automatically:
+</p>
+<pre>
+private ViewGroup mContainerView;
+...
+private void addItem() {
+ View newView;
+ ...
+ mContainerView.addView(newView, 0);
+}
+</pre>
diff --git a/docs/html/training/animation/screen-slide.jd b/docs/html/training/animation/screen-slide.jd
new file mode 100755
index 000000000000..8a7af672e916
--- /dev/null
+++ b/docs/html/training/animation/screen-slide.jd
@@ -0,0 +1,185 @@
+page.title=Using ViewPager for Screen Slides
+trainingnavtop=true
+
+@jd:body
+
+ <div id="tb-wrapper">
+ <div id="tb">
+ <h2>This lesson teaches you to</h2>
+ <ol>
+ <li><a href="#views">Create the Views</a></li>
+ <li><a href="#fragment">Create the Fragment</a></li>
+ <li><a href="#viewpager">Animate the Screen Slide</a></li>
+ </ol>
+ </div>
+ </div>
+ <p>
+ Screen slides are transitions between one entire screen to another and are common with UIs
+ like setup wizards or slideshows. This lesson shows you how to do screen slides with
+ a {@link android.support.v4.view.ViewPager} provided by the <a href=
+ "{@docRoot}/tools/extras/support-library.html">support library</a>.
+ {@link android.support.v4.view.ViewPager}s can animate screen slides
+ automatically. Here's what a screen slide looks like that transitions from
+ one screen of content to the next:
+ </p>
+
+ <div class="framed-galaxynexus-land-span-8">
+ <video class="play-on-hover" autoplay>
+ <source src="anim_screenslide.mp4" type="video/mp4">
+ <source src="anim_screenslide.webm" type="video/webm">
+ <source src="anim_screenslide.ogv" type="video/ogg">
+ </video>
+ </div>
+
+ <div class="figure-caption">
+ Screen slide animation
+ <div class="video-instructions">&nbsp;</div>
+ </div>
+
+<p>If you want to jump ahead and see a full working example,
+<a href="{@docRoot}shareables/training/Animations.zip">download</a>
+and run the sample app and select the Screen Slide example. See the
+following files for the code implementation:</p>
+<ul>
+ <li><code>src/ScreenSlidePageFragment.java</code></li>
+ <li><code>src/ScreenSlideActivity.java</code></li>
+ <li><code>layout/activity_screen_slide.xml</code></li>
+ <li><code>layout/fragment_screen_slide_page.xml</code></li>
+</ul>
+
+<h2 id="views">Create the Views</h2>
+ <p>Create a layout file that you'll later use for the content of a fragment. The following example
+ contains a text view to display some text:
+
+<pre>
+&lt;com.example.android.animationsdemo.ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"&gt;
+
+ &lt;TextView style="?android:textAppearanceMedium"
+ android:padding="16dp"
+ android:lineSpacingMultiplier="1.2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/lorem_ipsum" /&gt;
+
+&lt;/com.example.android.animationsdemo.ScrollView&gt;
+</pre>
+
+<h2 id="fragment">Create the Fragment</h2>
+<p>Create a {@link android.support.v4.app.Fragment} class that returns the layout
+that you just created in the {@link android.app.Fragment#onCreateView onCreateView()}
+ method. You can then create instances of this fragment in the parent activity whenever you need a new page to
+ display to the user:</p>
+
+
+<pre>
+public class ScreenSlidePageFragment extends Fragment {
+
+ &#64;Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ ViewGroup rootView = (ViewGroup) inflater.inflate(
+ R.layout.fragment_screen_slide_page, container, false);
+
+ return rootView;
+ }
+}
+</pre>
+
+<h2 id="viewpager">Screen Slides with ViewPager</h2>
+
+<p>{@link android.support.v4.view.ViewPager}s have built-in swipe gestures to transition
+ through pages, and they display screen slide animations by default, so you don't need to create any. {@link android.support.v4.view.ViewPager}s use
+{@link android.support.v4.view.PagerAdapter}s as a supply for new pages to display, so the {@link android.support.v4.view.PagerAdapter} will use the
+fragment class that you created earlier.
+ </p>
+
+<p>To begin, create a layout that contains a {@link android.support.v4.view.ViewPager}:</p>
+
+<pre>
+&lt;android.support.v4.view.ViewPager
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" /&gt;
+</pre>
+
+<p>Create an activity that does the following things:
+</p>
+
+<ul>
+ <li>Sets the content view to be the layout with the {@link android.support.v4.view.ViewPager}.</li>
+ <li>Create a class that extends the {@link android.support.v13.app.FragmentStatePagerAdapter} abstract class. Implement
+ the {@link android.support.v4.app.FragmentStatePagerAdapter#getItem getItem()} method to supply
+ instances of <code>ScreenSlidePageFragment</code> as new pages. The pager adapter also requires that you implement the
+ {@link android.support.v4.view.PagerAdapter#getCount getCount()} method, which returns the amount of pages the adapter will create (five in the example).
+ <li>Hooks up the {@link android.support.v4.view.PagerAdapter} to the {@link android.support.v4.view.ViewPager}</code>.</li>
+ <li>Handle's the device's back button by moving backwards in the virtual stack of fragments.
+ If the user is already on the first page, go back on the activity back stack.</li>
+</ul>
+
+<pre>
+public class ScreenSlidePagerActivity extends FragmentActivity {
+ /**
+ * The number of pages (wizard steps) to show in this demo.
+ */
+ private static final int NUM_PAGES = 5;
+
+ /**
+ * The pager widget, which handles animation and allows swiping horizontally to access previous
+ * and next wizard steps.
+ */
+ private ViewPager mPager;
+
+ /**
+ * The pager adapter, which provides the pages to the view pager widget.
+ */
+ private PagerAdapter mPagerAdapter;
+
+ &#64;Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_screen_slide_pager);
+
+ // Instantiate a ViewPager and a PagerAdapter.
+ mPager = (ViewPager) findViewById(R.id.pager);
+ mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager());
+ mPager.setAdapter(mPagerAdapter);
+ }
+
+ &#64;Override
+ public void onBackPressed() {
+ if (mPager.getCurrentItem() == 0) {
+ // If the user is currently looking at the first step, allow the system to handle the
+ // Back button. This calls finish() on this activity and pops the back stack.
+ super.onBackPressed();
+ } else {
+ // Otherwise, select the previous step.
+ mPager.setCurrentItem(mPager.getCurrentItem() - 1);
+ }
+ }
+
+ /**
+ * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
+ * sequence.
+ */
+ private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
+ public ScreenSlidePagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ &#64;Override
+ public Fragment getItem(int position) {
+ return new ScreenSlidePageFragment();
+ }
+
+ &#64;Override
+ public int getCount() {
+ return NUM_PAGES;
+ }
+ }
+}
+</pre> \ No newline at end of file
diff --git a/docs/html/training/animation/zoom.jd b/docs/html/training/animation/zoom.jd
new file mode 100644
index 000000000000..5dc2b6c25b92
--- /dev/null
+++ b/docs/html/training/animation/zoom.jd
@@ -0,0 +1,320 @@
+page.title=Zooming a View
+trainingnavtop=true
+
+@jd:body
+
+ <div id="tb-wrapper">
+ <div id="tb">
+ <h2>
+ This lesson teaches you to:
+ </h2>
+ <ol>
+ <li>
+ <a href="#views">Create the Views</a>
+ </li>
+ <li>
+ <a href="#setup">Set up the Zoom Animation</a>
+ </li>
+ <li>
+ <a href="#animate">Zoom the View</a>
+ </li>
+ </ol>
+ </div>
+ </div>
+ <p>
+ This lesson demonstrates how to do a touch-to-zoom animation, which is useful for apps such as photo
+ galleries to animate a view from a thumbnail to a full-size image that fills the screen.
+ </p>
+ <p>Here's what a touch-to-zoom animation looks like that
+ expands an image thumbnail to fill the screen:
+ </p>
+
+ <div class="framed-galaxynexus-land-span-8">
+ <video class="play-on-hover" autoplay>
+ <source src="anim_zoom.mp4" type="video/mp4">
+ <source src="anim_zoom.webm" type="video/webm">
+ <source src="anim_zoom.ogv" type="video/ogg">
+ </video>
+ </div>
+ <div class="figure-caption">
+ Zoom animation
+ <div class="video-instructions">&nbsp;</div>
+ </div>
+
+ <p>
+ If you want to jump ahead and see a full working example,
+ <a href="{@docRoot}shareables/training/Animations.zip">download</a> and
+ run the sample app and select the
+ Zoom example. See the following files for the code implementation:
+ </p>
+ <ul>
+ <li>
+ <code>src/TouchHighlightImageButton.java</code> (a simple helper class that shows a blue
+ touch highlight when the image button is pressed)
+ </li>
+ <li>
+ <code>src/ZoomActivity.java</code>
+ </li>
+ <li>
+ <code>layout/activity_zoom.xml</code>
+ </li>
+ </ul>
+ <h2 id="views">
+ Create the Views
+ </h2>
+ <p>
+ Create a layout file that contains the small and large version of the content that you want
+ to zoom. The following example creates an {@link android.widget.ImageButton} for clickable image thumbnail
+ and an {@link android.widget.ImageView} that displays the enlarged view of the image:
+ </p>
+ <pre>
+&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"&gt;
+
+ &lt;LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="16dp"&gt;
+
+ &lt;ImageButton
+ android:id="@+id/thumb_button_1"
+ android:layout_width="100dp"
+ android:layout_height="75dp"
+ android:layout_marginRight="1dp"
+ android:src="@drawable/thumb1"
+ android:scaleType="centerCrop"
+ android:contentDescription="@string/description_image_1" /&gt;
+
+ &lt;/LinearLayout&gt;
+
+ &lt;!-- This initially-hidden ImageView will hold the expanded/zoomed version of
+ the images above. Without transformations applied, it takes up the entire
+ screen. To achieve the "zoom" animation, this view's bounds are animated
+ from the bounds of the thumbnail button above, to its final laid-out
+ bounds.
+ --&gt;
+
+ &lt;ImageView
+ android:id="@+id/expanded_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"
+ android:contentDescription="@string/description_zoom_touch_close" /&gt;
+
+&lt;/FrameLayout&gt;
+</pre>
+ <h2 id="setup">
+ Set up the Zoom Animation
+ </h2>
+ <p>
+ Once you apply your layout, set up the event handlers that trigger the zoom animation.
+ The following example adds a {@link android.view.View.OnClickListener} to the {@link
+ android.widget.ImageButton} to execute the zoom animation when the user
+ clicks the image button:
+ </p>
+ <pre>
+public class ZoomActivity extends FragmentActivity {
+ // Hold a reference to the current animator,
+ // so that it can be canceled mid-way.
+ private Animator mCurrentAnimator;
+
+ // The system "short" animation time duration, in milliseconds. This
+ // duration is ideal for subtle animations or animations that occur
+ // very frequently.
+ private int mShortAnimationDuration;
+
+ &#64;Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_zoom);
+
+ // Hook up clicks on the thumbnail views.
+
+ final View thumb1View = findViewById(R.id.thumb_button_1);
+ thumb1View.setOnClickListener(new View.OnClickListener() {
+ &#64;Override
+ public void onClick(View view) {
+ zoomImageFromThumb(thumb1View, R.drawable.image1);
+ }
+ });
+
+ // Retrieve and cache the system's default "short" animation time.
+ mShortAnimationDuration = getResources().getInteger(
+ android.R.integer.config_shortAnimTime);
+ }
+ ...
+}
+</pre>
+ <h2 id="animate">
+ Zoom the View
+ </h2>
+ <p>
+ You'll now need to animate from the normal sized view to the zoomed view
+ when appropriate. In general, you need to animate from the bounds of the normal-sized view to the
+ bounds of the larger-sized view. The following method shows you how to implement a zoom animation that
+ zooms from an image thumbnail to an enlarged view by doing the following things:
+ </p>
+ <ol>
+ <li>Assign the high-res image to the hidden "zoomed-in" (enlarged) {@link
+ android.widget.ImageView}. The following example loads a large image resource on the UI
+ thread for simplicity. You will want to do this loading in a separate thread to prevent
+ blocking on the UI thread and then set the bitmap on the UI thread. Ideally, the bitmap
+ should not be larger than the screen size.
+ </li>
+ <li>Calculate the starting and ending bounds for the {@link android.widget.ImageView}.
+ </li>
+ <li>Animate each of the four positioning and sizing properties <code>{@link
+ android.view.View#X}</code>, <code>{@link android.view.View#Y}</code>, ({@link
+ android.view.View#SCALE_X}, and <code>{@link android.view.View#SCALE_Y}</code>)
+ simultaneously, from the starting bounds to the ending bounds. These four animations are
+ added to an {@link android.animation.AnimatorSet} so that they can be started at the same
+ time.
+ </li>
+ <li>Zoom back out by running a similar animation but in reverse when the user touches the
+ screen when the image is zoomed in. You can do this by adding a {@link
+ android.view.View.OnClickListener} to the {@link android.widget.ImageView}. When clicked, the
+ {@link android.widget.ImageView} minimizes back down to the size of the image thumbnail and
+ sets its visibility to {@link android.view.View#GONE} to hide it.
+ </li>
+ </ol>
+ <pre>
+private void zoomImageFromThumb(final View thumbView, int imageResId) {
+ // If there's an animation in progress, cancel it
+ // immediately and proceed with this one.
+ if (mCurrentAnimator != null) {
+ mCurrentAnimator.cancel();
+ }
+
+ // Load the high-resolution "zoomed-in" image.
+ final ImageView expandedImageView = (ImageView) findViewById(
+ R.id.expanded_image);
+ expandedImageView.setImageResource(imageResId);
+
+ // Calculate the starting and ending bounds for the zoomed-in image.
+ // This step involves lots of math. Yay, math.
+ final Rect startBounds = new Rect();
+ final Rect finalBounds = new Rect();
+ final Point globalOffset = new Point();
+
+ // The start bounds are the global visible rectangle of the thumbnail,
+ // and the final bounds are the global visible rectangle of the container
+ // view. Also set the container view's offset as the origin for the
+ // bounds, since that's the origin for the positioning animation
+ // properties (X, Y).
+ thumbView.getGlobalVisibleRect(startBounds);
+ findViewById(R.id.container)
+ .getGlobalVisibleRect(finalBounds, globalOffset);
+ startBounds.offset(-globalOffset.x, -globalOffset.y);
+ finalBounds.offset(-globalOffset.x, -globalOffset.y);
+
+ // Adjust the start bounds to be the same aspect ratio as the final
+ // bounds using the "center crop" technique. This prevents undesirable
+ // stretching during the animation. Also calculate the start scaling
+ // factor (the end scaling factor is always 1.0).
+ float startScale;
+ if ((float) finalBounds.width() / finalBounds.height()
+ &gt; (float) startBounds.width() / startBounds.height()) {
+ // Extend start bounds horizontally
+ startScale = (float) startBounds.height() / finalBounds.height();
+ float startWidth = startScale * finalBounds.width();
+ float deltaWidth = (startWidth - startBounds.width()) / 2;
+ startBounds.left -= deltaWidth;
+ startBounds.right += deltaWidth;
+ } else {
+ // Extend start bounds vertically
+ startScale = (float) startBounds.width() / finalBounds.width();
+ float startHeight = startScale * finalBounds.height();
+ float deltaHeight = (startHeight - startBounds.height()) / 2;
+ startBounds.top -= deltaHeight;
+ startBounds.bottom += deltaHeight;
+ }
+
+ // Hide the thumbnail and show the zoomed-in view. When the animation
+ // begins, it will position the zoomed-in view in the place of the
+ // thumbnail.
+ thumbView.setAlpha(0f);
+ expandedImageView.setVisibility(View.VISIBLE);
+
+ // Set the pivot point for SCALE_X and SCALE_Y transformations
+ // to the top-left corner of the zoomed-in view (the default
+ // is the center of the view).
+ expandedImageView.setPivotX(0f);
+ expandedImageView.setPivotY(0f);
+
+ // Construct and run the parallel animation of the four translation and
+ // scale properties (X, Y, SCALE_X, and SCALE_Y).
+ AnimatorSet set = new AnimatorSet();
+ set
+ .play(ObjectAnimator.ofFloat(expandedImageView, View.X,
+ startBounds.left, finalBounds.left))
+ .with(ObjectAnimator.ofFloat(expandedImageView, View.Y,
+ startBounds.top, finalBounds.top))
+ .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X,
+ startScale, 1f)).with(ObjectAnimator.ofFloat(expandedImageView,
+ View.SCALE_Y, startScale, 1f));
+ set.setDuration(mShortAnimationDuration);
+ set.setInterpolator(new DecelerateInterpolator());
+ set.addListener(new AnimatorListenerAdapter() {
+ &#64;Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentAnimator = null;
+ }
+
+ &#64;Override
+ public void onAnimationCancel(Animator animation) {
+ mCurrentAnimator = null;
+ }
+ });
+ set.start();
+ mCurrentAnimator = set;
+
+ // Upon clicking the zoomed-in image, it should zoom back down
+ // to the original bounds and show the thumbnail instead of
+ // the expanded image.
+ final float startScaleFinal = startScale;
+ expandedImageView.setOnClickListener(new View.OnClickListener() {
+ &#64;Override
+ public void onClick(View view) {
+ if (mCurrentAnimator != null) {
+ mCurrentAnimator.cancel();
+ }
+
+ // Animate the four positioning/sizing properties in parallel,
+ // back to their original values.
+ AnimatorSet set = new AnimatorSet();
+ set.play(ObjectAnimator
+ .ofFloat(expandedImageView, View.X, startBounds.left))
+ .with(ObjectAnimator
+ .ofFloat(expandedImageView,
+ View.Y,startBounds.top))
+ .with(ObjectAnimator
+ .ofFloat(expandedImageView,
+ View.SCALE_X, startScaleFinal))
+ .with(ObjectAnimator
+ .ofFloat(expandedImageView,
+ View.SCALE_Y, startScaleFinal));
+ set.setDuration(mShortAnimationDuration);
+ set.setInterpolator(new DecelerateInterpolator());
+ set.addListener(new AnimatorListenerAdapter() {
+ &#64;Override
+ public void onAnimationEnd(Animator animation) {
+ thumbView.setAlpha(1f);
+ expandedImageView.setVisibility(View.GONE);
+ mCurrentAnimator = null;
+ }
+
+ &#64;Override
+ public void onAnimationCancel(Animator animation) {
+ thumbView.setAlpha(1f);
+ expandedImageView.setVisibility(View.GONE);
+ mCurrentAnimator = null;
+ }
+ });
+ set.start();
+ mCurrentAnimator = set;
+ }
+ });
+}
+</pre> \ No newline at end of file
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 4ad13533bfc2..4a5b0fadc5dd 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -287,6 +287,34 @@
</li>
</ul>
</li>
+
+ <li class="nav-section">
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>training/animation/index.html">
+ <span class="en">Adding Animations</span>
+ </a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/animation/crossfade.html">
+ <span class="en">Crossfading Two Views</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/animation/screen-slide.html">
+ <span class="en">Using ViewPager for Screen Slide</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/animation/cardflip.html">
+ <span class="en">Displaying Card Flip Animations</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/animation/zoom.html">
+ <span class="en">Zooming a View</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/animation/layout.html">
+ <span class="en">Animating Layout Changes</span>
+ </a>
+ </li>
+ </ul>
+ </li>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>training/managing-audio/index.html">
diff --git a/location/java/android/location/Geofence.java b/location/java/android/location/Geofence.java
index b3e4a88982d4..e0e11be44279 100644
--- a/location/java/android/location/Geofence.java
+++ b/location/java/android/location/Geofence.java
@@ -36,8 +36,8 @@ public final class Geofence implements Parcelable {
/**
* Create a horizontal, circular geofence.
*
- * @param latitude latitude in degrees
- * @param longitude longitude in degrees
+ * @param latitude latitude in degrees, between -90 and +90 inclusive
+ * @param longitude longitude in degrees, between -180 and +180 inclusive
* @param radius radius in meters
* @return a new geofence
* @throws IllegalArgumentException if any parameters are out of range
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index 737e17f378dc..8e0061d8dd9e 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -33,8 +33,8 @@ import com.android.internal.location.ProviderProperties;
* Criteria} class allows providers to be selected based on
* user-specified criteria.
*
- * @deprecated Use the {@link Criteria} class to request location instead of
- * enumerating providers.
+ * @deprecated Use the {@link LocationRequest} class to request location
+ * instead of enumerating providers.
*/
@Deprecated
public class LocationProvider {
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index aa0d8c3b1aa0..315196e16330 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -429,10 +429,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// Devices for which the volume is fixed and VolumePanel slider should be disabled
final int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_AUX_DIGITAL |
AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
+ AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET |
AudioSystem.DEVICE_OUT_ALL_USB;
private final boolean mMonitorOrientation;
+ private boolean mDockAudioMediaEnabled = true;
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
@@ -630,6 +633,27 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
+ private void readDockAudioSettings(ContentResolver cr)
+ {
+ mDockAudioMediaEnabled = Settings.Global.getInt(
+ cr, Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, 0) == 1;
+
+ if (mDockAudioMediaEnabled) {
+ mBecomingNoisyIntentDevices |= AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
+ } else {
+ mBecomingNoisyIntentDevices &= ~AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
+ }
+
+ sendMsg(mAudioHandler,
+ MSG_SET_FORCE_USE,
+ SENDMSG_QUEUE,
+ AudioSystem.FOR_DOCK,
+ mDockAudioMediaEnabled ?
+ AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE,
+ null,
+ 0);
+ }
+
private void readPersistedSettings() {
final ContentResolver cr = mContentResolver;
@@ -693,6 +717,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
mRingerModeAffectedStreams,
UserHandle.USER_CURRENT);
+
+ readDockAudioSettings(cr);
}
mMuteAffectedStreams = System.getIntForUser(cr,
@@ -3408,6 +3434,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
super(new Handler());
mContentResolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
+ mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.DOCK_AUDIO_MEDIA_ENABLED), false, this);
}
@Override
@@ -3443,6 +3471,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mRingerModeAffectedStreams = ringerModeAffectedStreams;
setRingerModeInt(getRingerMode(), false);
}
+ readDockAudioSettings(mContentResolver);
}
}
}
@@ -3722,7 +3751,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
config = AudioSystem.FORCE_BT_CAR_DOCK;
break;
case Intent.EXTRA_DOCK_STATE_LE_DESK:
- config = AudioSystem.FORCE_ANALOG_DOCK;
+ synchronized (mSettingsLock) {
+ if (mDockAudioMediaEnabled) {
+ config = AudioSystem.FORCE_ANALOG_DOCK;
+ } else {
+ config = AudioSystem.FORCE_NONE;
+ }
+ }
break;
case Intent.EXTRA_DOCK_STATE_HE_DESK:
config = AudioSystem.FORCE_DIGITAL_DOCK;
@@ -3731,6 +3766,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
default:
config = AudioSystem.FORCE_NONE;
}
+
AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
@@ -3877,7 +3913,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
} else if (action.equalsIgnoreCase(Intent.ACTION_CONFIGURATION_CHANGED)) {
handleConfigurationChanged(context);
} else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
- // attempt to stop music playabck for background user
+ // attempt to stop music playback for background user
sendMsg(mAudioHandler,
MSG_BROADCAST_AUDIO_BECOMING_NOISY,
SENDMSG_REPLACE,
@@ -3885,6 +3921,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
0,
null,
0);
+ // the current audio focus owner is no longer valid
+ discardAudioFocusOwner();
+
// load volume settings for new user
readAudioSettings(true /*userSwitch*/);
// preserve STREAM_MUSIC volume from one user to the next.
@@ -3929,6 +3968,32 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
};
+ /**
+ * Discard the current audio focus owner.
+ * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
+ * focus), remove it from the stack, and clear the remote control display.
+ */
+ private void discardAudioFocusOwner() {
+ synchronized(mAudioFocusLock) {
+ if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
+ // notify the current focus owner it lost focus after removing it from stack
+ FocusStackEntry focusOwner = mFocusStack.pop();
+ try {
+ focusOwner.mFocusDispatcher.dispatchAudioFocusChange(
+ AudioManager.AUDIOFOCUS_LOSS, focusOwner.mClientId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failure to signal loss of audio focus due to "+ e);
+ e.printStackTrace();
+ }
+ focusOwner.unlinkToDeath();
+ // clear RCD
+ synchronized(mRCStack) {
+ clearRemoteControlDisplay_syncAfRcs();
+ }
+ }
+ }
+ }
+
private void notifyTopOfAudioFocusStack() {
// notify the top of the stack it gained focus
if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
@@ -4000,7 +4065,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
while(stackIterator.hasNext()) {
FocusStackEntry fse = stackIterator.next();
- pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
+ pw.println(" source:" + fse.mSourceRef
+ + " -- pack: " + fse.mPackageName
+ + " -- client: " + fse.mClientId
+ " -- duration: " + fse.mFocusChangeType
+ " -- uid: " + fse.mCallingUid
+ " -- stream: " + fse.mStreamType);
@@ -4682,6 +4749,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
while(stackIterator.hasNext()) {
RemoteControlStackEntry rcse = stackIterator.next();
pw.println(" pi: " + rcse.mMediaIntent +
+ " -- pack: " + rcse.mCallingPackageName +
" -- ercvr: " + rcse.mReceiverComponent +
" -- client: " + rcse.mRcClient +
" -- uid: " + rcse.mCallingUid +
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index e08d4941d092..2a5a16e60a61 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -32,7 +32,6 @@ import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
-import android.view.DisplayInfo;
import java.util.ArrayList;
import java.util.HashMap;
@@ -53,7 +52,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class MediaRouter {
private static final String TAG = "MediaRouter";
- static class Static {
+ static class Static implements DisplayManager.DisplayListener {
final Resources mResources;
final IAudioService mAudioService;
final DisplayManager mDisplayService;
@@ -105,6 +104,8 @@ public class MediaRouter {
mDefaultAudioVideo = new RouteInfo(mSystemCategory);
mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
+ mDefaultAudioVideo.mPresentationDisplay = choosePresentationDisplayForRoute(
+ mDefaultAudioVideo, getAllPresentationDisplays());
addRouteStatic(mDefaultAudioVideo);
// This will select the active wifi display route if there is one.
@@ -115,6 +116,8 @@ public class MediaRouter {
appContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
+ mDisplayService.registerDisplayListener(this, mHandler);
+
AudioRoutesInfo newAudioRoutes = null;
try {
newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
@@ -191,6 +194,39 @@ public class MediaRouter {
}
}
}
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ updatePresentationDisplays(displayId);
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ updatePresentationDisplays(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ updatePresentationDisplays(displayId);
+ }
+
+ public Display[] getAllPresentationDisplays() {
+ return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
+ }
+
+ private void updatePresentationDisplays(int changedDisplayId) {
+ final Display[] displays = getAllPresentationDisplays();
+ final int count = mRoutes.size();
+ for (int i = 0; i < count; i++) {
+ final RouteInfo info = mRoutes.get(i);
+ Display display = choosePresentationDisplayForRoute(info, displays);
+ if (display != info.mPresentationDisplay
+ || (display != null && display.getDisplayId() == changedDisplayId)) {
+ info.mPresentationDisplay = display;
+ dispatchRoutePresentationDisplayChanged(info);
+ }
+ }
+ }
}
static Static sStatic;
@@ -218,6 +254,9 @@ public class MediaRouter {
* While remote routing is active the application may use a
* {@link android.app.Presentation Presentation} to replace the mirrored view
* on the external display with different content.</p>
+ *
+ * @see RouteInfo#getPresentationDisplay()
+ * @see android.app.Presentation
*/
public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
@@ -674,6 +713,14 @@ public class MediaRouter {
}
}
+ static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
+ for (CallbackInfo cbi : sStatic.mCallbacks) {
+ if ((cbi.type & info.mSupportedTypes) != 0) {
+ cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
+ }
+ }
+ }
+
static void systemVolumeChanged(int newValue) {
final RouteInfo selectedRoute = sStatic.mSelectedRoute;
if (selectedRoute == null) return;
@@ -755,6 +802,9 @@ public class MediaRouter {
newRoute.mEnabled = available;
newRoute.mName = display.getFriendlyDisplayName();
+
+ newRoute.mPresentationDisplay = choosePresentationDisplayForRoute(newRoute,
+ sStatic.getAllPresentationDisplays());
return newRoute;
}
@@ -830,6 +880,27 @@ public class MediaRouter {
return null;
}
+ private static Display choosePresentationDisplayForRoute(RouteInfo route, Display[] displays) {
+ if ((route.mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
+ if (route.mDeviceAddress != null) {
+ // Find the indicated Wifi display by its address.
+ for (Display display : displays) {
+ if (display.getType() == Display.TYPE_WIFI
+ && route.mDeviceAddress.equals(display.getAddress())) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ if (route == sStatic.mDefaultAudioVideo && displays.length > 0) {
+ // Choose the first presentation display from the list.
+ return displays[0];
+ }
+ }
+ return null;
+ }
+
/**
* Information about a media route.
*/
@@ -848,6 +919,7 @@ public class MediaRouter {
int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
int mPlaybackStream = AudioManager.STREAM_MUSIC;
VolumeCallbackInfo mVcb;
+ Display mPresentationDisplay;
String mDeviceAddress;
boolean mEnabled = true;
@@ -1119,6 +1191,38 @@ public class MediaRouter {
}
/**
+ * Gets the {@link Display} that should be used by the application to show
+ * a {@link android.app.Presentation} on an external display when this route is selected.
+ * Depending on the route, this may only be valid if the route is currently
+ * selected.
+ * <p>
+ * The preferred presentation display may change independently of the route
+ * being selected or unselected. For example, the presentation display
+ * of the default system route may change when an external HDMI display is connected
+ * or disconnected even though the route itself has not changed.
+ * </p><p>
+ * This method may return null if there is no external display associated with
+ * the route or if the display is not ready to show UI yet.
+ * </p><p>
+ * The application should listen for changes to the presentation display
+ * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
+ * show or dismiss its {@link android.app.Presentation} accordingly when the display
+ * becomes available or is removed.
+ * </p><p>
+ * This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes.
+ * </p>
+ *
+ * @return The preferred presentation display to use when this route is
+ * selected or null if none.
+ *
+ * @see #ROUTE_TYPE_LIVE_VIDEO
+ * @see android.app.Presentation
+ */
+ public Display getPresentationDisplay() {
+ return mPresentationDisplay;
+ }
+
+ /**
* @return true if this route is enabled and may be selected
*/
public boolean isEnabled() {
@@ -1159,9 +1263,11 @@ public class MediaRouter {
@Override
public String toString() {
String supportedTypes = typesToString(getSupportedTypes());
- return getClass().getSimpleName() + "{ name=" + getName() + ", status=" + getStatus() +
- " category=" + getCategory() +
- " supportedTypes=" + supportedTypes + "}";
+ return getClass().getSimpleName() + "{ name=" + getName() +
+ ", status=" + getStatus() +
+ ", category=" + getCategory() +
+ ", supportedTypes=" + supportedTypes +
+ ", presentationDisplay=" + mPresentationDisplay + "}";
}
}
@@ -1856,6 +1962,21 @@ public class MediaRouter {
* @param info The route with altered volume
*/
public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info);
+
+ /**
+ * Called when a route's presentation display changes.
+ * <p>
+ * This method is called whenever the route's presentation display becomes
+ * available, is removes or has changes to some of its properties (such as its size).
+ * </p>
+ *
+ * @param router the MediaRouter reporting the event
+ * @param info The route whose presentation display changed
+ *
+ * @see RouteInfo#getPresentationDisplay()
+ */
+ public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
+ }
}
/**
diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml
index 10b90644fcc4..779428e34a0c 100644
--- a/packages/FusedLocation/AndroidManifest.xml
+++ b/packages/FusedLocation/AndroidManifest.xml
@@ -18,16 +18,14 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.location.fused"
- coreApp="true"
- android:sharedUserId="android.uid.system">
+ coreApp="true">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" />
<application
- android:label="@string/app_label"
- android:process="system">
+ android:label="@string/app_label">
<uses-library android:name="com.android.location.provider" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 02b5326b8c31..cfe70dc862c0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -214,7 +214,7 @@
<activity android:name=".Somnambulator"
android:label="@string/start_dreams"
- android:icon="@mipmap/ic_daydreams"
+ android:icon="@mipmap/ic_launcher_dreams"
android:theme="@android:style/Theme.Wallpaper.NoTitleBar"
android:exported="true"
android:excludeFromRecents="true"
diff --git a/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png
deleted file mode 100644
index 56cbac146bbd..000000000000
--- a/packages/SystemUI/res/mipmap-hdpi/ic_dreams.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png b/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png
index a335d6d73698..37185f31496a 100644
--- a/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png
+++ b/packages/SystemUI/res/mipmap-hdpi/ic_launcher_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png
deleted file mode 100644
index ea3d9914c7f4..000000000000
--- a/packages/SystemUI/res/mipmap-mdpi/ic_dreams.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png b/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png
index ef2e27b62271..1993b0d75048 100644
--- a/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png
+++ b/packages/SystemUI/res/mipmap-mdpi/ic_launcher_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png b/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png
deleted file mode 100644
index ddc7f664528b..000000000000
--- a/packages/SystemUI/res/mipmap-xhdpi/ic_dreams.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png b/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png
index 7b42cb4ffd7d..c92b68106044 100644
--- a/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png
+++ b/packages/SystemUI/res/mipmap-xhdpi/ic_launcher_dreams.png
Binary files differ
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index e5cc356dc267..29ba624b4b43 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skerm is in landskapsoriëntasie gesluit."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skerm is in portretoriëntasie gesluit."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Dagdroom"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Vliegtuigmodus"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Laai, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 57ad48a98352..da77625199fc 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -26,7 +26,7 @@
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"ከዝርዝር አስወግድ"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"የትግበራ መረጃ"</string>
<string name="status_bar_no_recent_apps" msgid="6576392951053994640">"ምንም የቅርብ ጊዜ ትግበራዎች የሉም"</string>
- <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"የቅርብ ጊዜ መተግበሪያዎችን አሰናብት"</string>
+ <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"የቅርብ ጊዜ መተግበሪያዎችን ሰርዝ"</string>
<plurals name="status_bar_accessibility_recent_apps">
<item quantity="one" msgid="5854176083865845541">"1 የቅርብ ጊዜ መተግበሪያ"</item>
<item quantity="other" msgid="1040784359794890744">"%d የቅርብ ጊዜ መተግበሪያዎች"</item>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index c40e26d233f8..baf6322b4cfd 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"تم تأمين الشاشة في الاتجاه الأفقي."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"تم تأمين الشاشة في الاتجاه العمودي."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="7219575858348719790">"حلم اليقظة"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"وضع الطائرة"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"جارٍ الشحن، <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index a4b4b73c537c..c66e9120f540 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -176,8 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Экран заблакiраваны ў альбомнай арыентацыі."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Экран заблакiраваны ў партрэтнай арыентацыі."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Мроi"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Рэжым палёту"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Зарадка, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 8876a3808d4b..e3f7dc1f2ae8 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екранът е заключен в хоризонтална ориентация."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екранът е заключен във вертикална ориентация."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Мечта"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Самолетен режим"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Зарежда се, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 8d5cf366fcc9..8c2dd8e23edf 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -176,7 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla està bloquejada en orientació horitzontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla està bloquejada en orientació vertical."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="7219575858348719790">"Somnis despert"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Estalvi de pantalla"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mode d\'avió"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"S\'està carregant, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index ddb6265ad3c2..c34ca3e116b7 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skærmen er nu låst i liggende retning."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skærmen er nu låst i stående retning."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Dagdrømme"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Flytilstand"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Oplader, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index fc18361f32e2..a5a5e36d28ff 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -185,7 +185,7 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> Geräte)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth aus"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Helligkeit"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatisch drehen"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Autom. drehen"</string>
<string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Drehung gesperrt"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Eingabemethode"</string>
<string name="quick_settings_location_label" msgid="3292451598267467545">"Verwendeter Standort"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 8abf3a838c5b..d5af0cd88052 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -176,8 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla está bloqueada en modo horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla está bloqueada en modo vertical."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Activar Daydreams"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo de avión"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 79f0bb0cc66f..e8bc08d1e408 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla está bloqueada en modo horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla está bloqueada en modo vertical."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Suspender"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo avión"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index e547478c9a56..a12ccf01b4d7 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -176,8 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"L\'écran est verrouillé en mode paysage."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"L\'écran est verrouillé en mode portrait."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Écran de veille"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mode avion"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"En charge (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index baa7b22161e5..26151dd219b6 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Layar dikunci dalam orientasi lanskap."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Layar dikunci dalam orientasi potret."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Lamunan"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mode pesawat"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Mengisi baterai, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index c427ac43ef8f..b64d73e0d30d 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -176,8 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"画面は横向きにロックされています。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"画面は縦向きにロックされています。"</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"スクリーンセーバー"</string>
<string name="ethernet_label" msgid="7967563676324087464">"イーサネット"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"機内モード"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"充電中: <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index deafa45edc95..3f7c5205f491 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"화면이 가로 방향으로 잠겨 있습니다."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"화면이 세로 방향으로 잠겨 있습니다."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"화면 보호기"</string>
<string name="ethernet_label" msgid="7967563676324087464">"이더넷"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"비행기 모드"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"충전 중(<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 7ce5b6231526..85053772726f 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skrin dikunci dalam orientasi landskap."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skrin dikunci dalam orientasi potret."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Lamun"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mod kapal terbang"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Mengecas, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index abd7f8408c3f..e6076329ad81 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skjermen er låst i liggende retning."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skjermen er låst i stående retning."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Dagdrøm"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Flymodus"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Lader: <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 68f08ddcd789..0764aeacf17b 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -176,8 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"A tela está bloqueada na orientação paisagem."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"A tela está bloqueada na orientação retrato."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo para avião"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Carregando, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 085f6076f6d7..46185591cb77 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ecranul este blocat în orientarea de tip peisaj."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ecranul este blocat în orientarea de tip portret."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mod Avion"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Se încarcă, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 2596f8899e86..28fe0d253c61 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -176,12 +176,13 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Выбрана только альбомная ориентация экрана."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Выбрана только книжная ориентация экрана."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Заставка"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Режим полета"</string>
- <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Зарядка (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
- <string name="quick_settings_battery_charged_label" msgid="8865413079414246081">"Заряжено"</string>
+ <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <!-- String.format failed for translation -->
+ <!-- no translation found for quick_settings_battery_charged_label (8865413079414246081) -->
+ <skip />
<string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g>)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth выкл."</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index ea70c1074661..c5143dd6239c 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -176,8 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Obrazovka je uzamknutá v orientácii na šírku."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Obrazovka je uzamknutá v orientácii na výšku."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Šetrič obrazovky"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Režim V lietadle"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Nabíjanie, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index fb8812a84719..48f0893a6da3 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Zaslon je zaklenjen v ležeči usmerjenosti."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Zaslon je zaklenjen v pokončni usmerjenosti."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Sanjarjenje"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Način za letalo"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Polnjenje, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 2d6f5f24904b..dadebaead020 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวนอน"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวตั้ง"</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
<string name="ethernet_label" msgid="7967563676324087464">"อีเทอร์เน็ต"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"โหมดใช้งานบนเครื่องบิน"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"กำลังชาร์จ, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 52b3758700f4..48dca2d8daf2 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Naka-lock ang screen sa pahigang oryentasyon."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Naka-lock ang screen sa patayong oryentasyon."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Airplane mode"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Nagcha-charge, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 08111ef015db..5d642860f037 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran yatay yönde kilitlendi."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran dikey yönde kilitlendi."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Hafif uyku"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Uçak modu"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Şarj oluyor, <xliff:g id="PERCENT">%%</xliff:g><xliff:g id="NUMBER">%d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 71a2bca20af8..7750e7706492 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -174,7 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Екран заблоковано в альбомній орієнтації."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Екран заблоковано в книжковій орієнтації."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <string name="start_dreams" msgid="7219575858348719790">"Заставка \"Видіння\""</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Заставка"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Режим польоту"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Заряджається, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 87ee67b4a3cb..fc22203dd3c9 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -174,8 +174,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Màn hình hiện bị khóa theo hướng ngang."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Màn hình hiện bị khóa theo hướng dọc."</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Mơ màng"</string>
<string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Chế độ trên máy bay"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Đang sạc, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 45be2e9eb08c..33d1f6d6d375 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -176,8 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"屏幕锁定为横向模式。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"屏幕锁定为纵向模式。"</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"果冻豆大乱舞"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"互动屏保"</string>
<string name="ethernet_label" msgid="7967563676324087464">"以太网"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"飞行模式"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"正在充电:<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index d64ca20e0e03..91248da217f2 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -176,8 +176,7 @@
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"螢幕已鎖定為橫向模式。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"螢幕已鎖定為垂直模式。"</string>
<string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
- <!-- no translation found for start_dreams (7219575858348719790) -->
- <skip />
+ <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
<string name="ethernet_label" msgid="7967563676324087464">"乙太網路"</string>
<string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"飛航模式"</string>
<string name="quick_settings_battery_charging_label" msgid="490074774465309209">"充電中 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 0a7dd7c76bf0..9da883a7f4c7 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -401,6 +401,7 @@ public class ImageWallpaper extends WallpaperService {
Throwable exception = null;
try {
mWallpaperManager.forgetLoadedWallpaper(); // force reload
+ mBackground = null;
mBackground = mWallpaperManager.getBitmap();
} catch (RuntimeException e) {
exception = e;
diff --git a/packages/SystemUI/src/com/android/systemui/Somnambulator.java b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
index 1f00bc1f3e60..0dd6d9283223 100644
--- a/packages/SystemUI/src/com/android/systemui/Somnambulator.java
+++ b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
@@ -43,7 +43,7 @@ public class Somnambulator extends Activity {
| Intent.FLAG_ACTIVITY_NEW_TASK);
Intent resultIntent = new Intent();
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
- Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_daydreams));
+ Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher_dreams));
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
resultIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.string.start_dreams));
setResult(RESULT_OK, resultIntent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index eef54460d8af..983328d438ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -55,6 +55,9 @@ public class NavigationBarView extends LinearLayout {
final static boolean NAVBAR_ALWAYS_AT_RIGHT = true;
+ // slippery nav bar when everything is disabled, e.g. during setup
+ final static boolean SLIPPERY_WHEN_DISABLED= true;
+
final static boolean ANIMATE_HIDE_TRANSITION = false; // turned off because it introduces unsightly delay when videos goes to full screen
protected IStatusBarService mBarService;
@@ -237,7 +240,9 @@ public class NavigationBarView extends LinearLayout {
final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0);
final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0);
- setSlippery(disableHome && disableRecent && disableBack);
+ if (SLIPPERY_WHEN_DISABLED) {
+ setSlippery(disableHome && disableRecent && disableBack && disableSearch);
+ }
if (!mScreenOn && mCurrentView != null) {
ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index fefd4fb9831b..17256cfa2893 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -412,7 +412,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mForceStatusBar;
boolean mForceStatusBarFromKeyguard;
boolean mHideLockScreen;
- boolean mDismissKeyguard;
+
+ // States of keyguard dismiss.
+ private static final int DISMISS_KEYGUARD_NONE = 0; // Keyguard not being dismissed.
+ private static final int DISMISS_KEYGUARD_START = 1; // Keyguard needs to be dismissed.
+ private static final int DISMISS_KEYGUARD_CONTINUE = 2; // Keyguard has been dismissed.
+ int mDismissKeyguard = DISMISS_KEYGUARD_NONE;
+
+ /** The window that is currently dismissing the keyguard. Dismissing the keyguard must only
+ * be done once per window. */
+ private WindowState mWinDismissingKeyguard;
+
boolean mShowingLockscreen;
boolean mShowingDream;
boolean mDreamingLockscreen;
@@ -2921,6 +2931,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
/** {@inheritDoc} */
+ @Override
public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
mTopFullscreenOpaqueWindowState = null;
mForceStatusBar = false;
@@ -2928,12 +2939,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mHideLockScreen = false;
mAllowLockscreenWhenOn = false;
- mDismissKeyguard = false;
+ mDismissKeyguard = DISMISS_KEYGUARD_NONE;
mShowingLockscreen = false;
mShowingDream = false;
}
/** {@inheritDoc} */
+ @Override
public void applyPostLayoutPolicyLw(WindowState win,
WindowManager.LayoutParams attrs) {
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw="
@@ -2971,9 +2983,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mHideLockScreen = true;
mForceStatusBarFromKeyguard = false;
}
- if ((attrs.flags & FLAG_DISMISS_KEYGUARD) != 0) {
+ if ((attrs.flags & FLAG_DISMISS_KEYGUARD) != 0
+ && mDismissKeyguard == DISMISS_KEYGUARD_NONE) {
if (DEBUG_LAYOUT) Log.v(TAG, "Setting mDismissKeyguard to true by win " + win);
- mDismissKeyguard = true;
+ mDismissKeyguard = mWinDismissingKeyguard == win ?
+ DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START;
+ mWinDismissingKeyguard = win;
mForceStatusBarFromKeyguard = false;
}
if ((attrs.flags & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
@@ -2984,6 +2999,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
/** {@inheritDoc} */
+ @Override
public int finishPostLayoutPolicyLw() {
int changes = 0;
boolean topIsFullscreen = false;
@@ -3023,7 +3039,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mStatusBar.hideLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
- mHandler.post(new Runnable() { public void run() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
try {
IStatusBarService statusbar = getStatusBarService();
if (statusbar != null) {
@@ -3051,7 +3069,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mKeyguard != null) {
if (localLOGV) Log.v(TAG, "finishPostLayoutPolicyLw: mHideKeyguard="
+ mHideLockScreen);
- if (mDismissKeyguard && !mKeyguardMediator.isSecure()) {
+ if (mDismissKeyguard != DISMISS_KEYGUARD_NONE && !mKeyguardMediator.isSecure()) {
if (mKeyguard.hideLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT
| FINISH_LAYOUT_REDO_CONFIG
@@ -3059,6 +3077,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
if (mKeyguardMediator.isShowing()) {
mHandler.post(new Runnable() {
+ @Override
public void run() {
mKeyguardMediator.keyguardDone(false, false);
}
@@ -3071,7 +3090,25 @@ public class PhoneWindowManager implements WindowManagerPolicy {
| FINISH_LAYOUT_REDO_WALLPAPER;
}
mKeyguardMediator.setHidden(true);
+ } else if (mDismissKeyguard != DISMISS_KEYGUARD_NONE) {
+ // This is the case of keyguard isSecure() and not mHideLockScreen.
+ if (mDismissKeyguard == DISMISS_KEYGUARD_START) {
+ // Only launch the next keyguard unlock window once per window.
+ if (mKeyguard.showLw(true)) {
+ changes |= FINISH_LAYOUT_REDO_LAYOUT
+ | FINISH_LAYOUT_REDO_CONFIG
+ | FINISH_LAYOUT_REDO_WALLPAPER;
+ }
+ mKeyguardMediator.setHidden(false);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mKeyguardMediator.dismiss();
+ }
+ });
+ }
} else {
+ mWinDismissingKeyguard = null;
if (mKeyguard.showLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT
| FINISH_LAYOUT_REDO_CONFIG
@@ -4549,6 +4586,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(" mForceStatusBarFromKeyguard=");
pw.println(mForceStatusBarFromKeyguard);
pw.print(prefix); pw.print("mDismissKeyguard="); pw.print(mDismissKeyguard);
+ pw.print(" mWinDismissingKeyguard="); pw.print(mWinDismissingKeyguard);
pw.print(" mHomePressed="); pw.println(mHomePressed);
pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.print(mAllowLockscreenWhenOn);
pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
new file mode 100644
index 000000000000..b38a9ed2934c
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+
+import com.android.internal.policy.impl.keyguard.KeyguardActivityLauncher.CameraWidgetInfo;
+
+public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnClickListener {
+ private static final String TAG = CameraWidgetFrame.class.getSimpleName();
+ private static final boolean DEBUG = KeyguardHostView.DEBUG;
+ private static final int WIDGET_ANIMATION_DURATION = 250;
+ private static final int WIDGET_WAIT_DURATION = 650;
+
+ interface Callbacks {
+ void onLaunchingCamera();
+ void onCameraLaunched();
+ }
+
+ private final Handler mHandler = new Handler();
+ private final KeyguardActivityLauncher mActivityLauncher;
+ private final Callbacks mCallbacks;
+
+ private View mWidgetView;
+ private long mLaunchCameraStart;
+ private boolean mRendered;
+ private boolean mActive;
+ private boolean mChallengeActive;
+ private boolean mTransitioning;
+ private boolean mDown;
+
+ private final Runnable mLaunchCameraRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLaunchCameraStart = SystemClock.uptimeMillis();
+ mActivityLauncher.launchCamera();
+ }};
+
+ private final Runnable mRenderRunnable = new Runnable() {
+ @Override
+ public void run() {
+ render();
+ }};
+
+ private final Runnable mTransitionToCameraRunnable = new Runnable() {
+ @Override
+ public void run() {
+ transitionToCamera();
+ }};
+
+ private CameraWidgetFrame(Context context, Callbacks callbacks,
+ KeyguardActivityLauncher activityLauncher) {
+ super(context);
+
+ mCallbacks = callbacks;
+ mActivityLauncher = activityLauncher;
+ }
+
+ public static CameraWidgetFrame create(Context context, Callbacks callbacks,
+ KeyguardActivityLauncher launcher) {
+ if (context == null || callbacks == null || launcher == null)
+ return null;
+
+ CameraWidgetInfo widgetInfo = launcher.getCameraWidgetInfo();
+ if (widgetInfo == null)
+ return null;
+ View widgetView = widgetInfo.layoutId > 0 ?
+ inflateWidgetView(context, widgetInfo) :
+ inflateGenericWidgetView(context);
+ if (widgetView == null)
+ return null;
+
+ ImageView preview = new ImageView(context);
+ preview.setLayoutParams(new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT));
+ preview.setScaleType(ScaleType.FIT_CENTER);
+ CameraWidgetFrame cameraWidgetFrame = new CameraWidgetFrame(context, callbacks, launcher);
+ cameraWidgetFrame.addView(preview);
+ cameraWidgetFrame.mWidgetView = widgetView;
+ preview.setOnClickListener(cameraWidgetFrame);
+ return cameraWidgetFrame;
+ }
+
+ private static View inflateWidgetView(Context context, CameraWidgetInfo widgetInfo) {
+ if (DEBUG) Log.d(TAG, "inflateWidgetView: " + widgetInfo.contextPackage);
+ View widgetView = null;
+ Exception exception = null;
+ try {
+ Context cameraContext = context.createPackageContext(
+ widgetInfo.contextPackage, Context.CONTEXT_RESTRICTED);
+ LayoutInflater cameraInflater = (LayoutInflater)
+ cameraContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ cameraInflater = cameraInflater.cloneInContext(cameraContext);
+ widgetView = cameraInflater.inflate(widgetInfo.layoutId, null, false);
+ } catch (NameNotFoundException e) {
+ exception = e;
+ } catch (RuntimeException e) {
+ exception = e;
+ }
+ if (exception != null) {
+ Log.w(TAG, "Error creating camera widget view", exception);
+ }
+ return widgetView;
+ }
+
+ private static View inflateGenericWidgetView(Context context) {
+ if (DEBUG) Log.d(TAG, "inflateGenericWidgetView");
+ ImageView iv = new ImageView(context);
+ iv.setImageResource(com.android.internal.R.drawable.ic_lockscreen_camera);
+ iv.setScaleType(ScaleType.CENTER);
+ iv.setBackgroundColor(Color.argb(127, 0, 0, 0));
+ return iv;
+ }
+
+ public void render() {
+ if (mRendered) return;
+
+ try {
+ int width = getRootView().getWidth();
+ int height = getRootView().getHeight();
+ if (DEBUG) Log.d(TAG, String.format("render [%sx%s] %s",
+ width, height, Integer.toHexString(hashCode())));
+ if (width == 0 || height == 0) {
+ return;
+ }
+ Bitmap offscreen = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(offscreen);
+ mWidgetView.measure(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ mWidgetView.layout(0, 0, width, height);
+ mWidgetView.draw(c);
+ ((ImageView)getChildAt(0)).setImageBitmap(offscreen);
+ mRendered = true;
+ } catch (Throwable t) {
+ Log.w(TAG, "Error rendering camera widget", t);
+ removeAllViews();
+ View genericView = inflateGenericWidgetView(mContext);
+ addView(genericView);
+ }
+ }
+
+ private void transitionToCamera() {
+ if (mTransitioning || mChallengeActive || mDown) return;
+
+ mTransitioning = true;
+
+ final View child = getChildAt(0);
+ final View root = getRootView();
+
+ final int startWidth = child.getWidth();
+ final int startHeight = child.getHeight();
+
+ final int finishWidth = root.getWidth();
+ final int finishHeight = root.getHeight();
+
+ final float scaleX = (float) finishWidth / startWidth;
+ final float scaleY = (float) finishHeight / startHeight;
+ final float scale = Math.round( Math.max(scaleX, scaleY) * 100) / 100f;
+
+ final int[] loc = new int[2];
+ root.getLocationInWindow(loc);
+ final int finishCenter = loc[1] + finishHeight / 2;
+
+ child.getLocationInWindow(loc);
+ final int startCenter = loc[1] + startHeight / 2;
+
+ if (DEBUG) Log.d(TAG, String.format("Transitioning to camera. " +
+ "(start=%sx%s, finish=%sx%s, scale=%s,%s, startCenter=%s, finishCenter=%s)",
+ startWidth, startHeight,
+ finishWidth, finishHeight,
+ scaleX, scaleY,
+ startCenter, finishCenter));
+
+ animate()
+ .scaleX(scale)
+ .scaleY(scale)
+ .translationY(finishCenter - startCenter)
+ .setDuration(WIDGET_ANIMATION_DURATION)
+ .withEndAction(mLaunchCameraRunnable)
+ .start();
+
+ mCallbacks.onLaunchingCamera();
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (DEBUG) Log.d(TAG, "clicked");
+ if (mTransitioning) return;
+ if (mActive && !mChallengeActive) {
+ cancelTransitionToCamera();
+ transitionToCamera();
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ if (DEBUG) Log.d(TAG, "onWindowFocusChanged: " + hasWindowFocus);
+ if (!hasWindowFocus) {
+ mTransitioning = false;
+ if (mLaunchCameraStart > 0) {
+ long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart;
+ if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime));
+ mLaunchCameraStart = 0;
+ onCameraLaunched();
+ }
+ }
+ }
+
+ @Override
+ public void onActive(boolean isActive) {
+ mActive = isActive;
+ if (mActive) {
+ rescheduleTransitionToCamera();
+ } else {
+ reset();
+ }
+ }
+
+ @Override
+ public boolean onUserInteraction(int action) {
+ if (mTransitioning) return true;
+ if (DEBUG) Log.d(TAG, "onUserInteraction " + action);
+ mDown = action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE;
+ if (mActive && !mChallengeActive) {
+ rescheduleTransitionToCamera();
+ }
+ return false;
+ }
+
+ @Override
+ protected void onFocusLost() {
+ Log.d(TAG, "onFocusLost");
+ cancelTransitionToCamera();
+ super.onFocusLost();
+ }
+
+ @Override
+ public void onChallengeActive(boolean challengeActive) {
+ if (DEBUG) Log.d(TAG, "onChallengeActive: " + challengeActive);
+ mChallengeActive = challengeActive;
+ if (mTransitioning) return;
+ if (mActive) {
+ if (mChallengeActive) {
+ cancelTransitionToCamera();
+ } else {
+ rescheduleTransitionToCamera();
+ }
+ }
+ }
+
+ private void rescheduleTransitionToCamera() {
+ if (DEBUG) Log.d(TAG, "rescheduleTransitionToCamera at " + SystemClock.uptimeMillis());
+ mHandler.removeCallbacks(mTransitionToCameraRunnable);
+ mHandler.postDelayed(mTransitionToCameraRunnable, WIDGET_WAIT_DURATION);
+ }
+
+ private void cancelTransitionToCamera() {
+ if (DEBUG) Log.d(TAG, "cancelTransitionToCamera at " + SystemClock.uptimeMillis());
+ mHandler.removeCallbacks(mTransitionToCameraRunnable);
+ }
+
+ private void onCameraLaunched() {
+ mCallbacks.onCameraLaunched();
+ reset();
+ }
+
+ private void reset() {
+ if (DEBUG) Log.d(TAG, "reset");
+ mLaunchCameraStart = 0;
+ mTransitioning = false;
+ mChallengeActive = false;
+ mDown = false;
+ cancelTransitionToCamera();
+ animate().cancel();
+ setScaleX(1);
+ setScaleY(1);
+ setTranslationY(0);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mHandler.post(mRenderRunnable);
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java
new file mode 100644
index 000000000000..605a7380775c
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+/**
+ * Interface implemented by ViewGroup-derived layouts that implement
+ * special logic for presenting security challenges to the user.
+ */
+public interface ChallengeLayout {
+ /**
+ * @return true if the security challenge area of this layout is currently visible
+ */
+ boolean isChallengeShowing();
+
+ /**
+ * @return true if the challenge area significantly overlaps other content
+ */
+ boolean isChallengeOverlapping();
+
+ /**
+ * Show or hide the challenge layout.
+ *
+ * If you want to show the challenge layout in bouncer mode where applicable,
+ * use {@link #showBouncer()} instead.
+ *
+ * @param b true to show, false to hide
+ */
+ void showChallenge(boolean b);
+
+ /**
+ * Show the bouncer challenge. This may block access to other child views.
+ */
+ void showBouncer();
+
+ /**
+ * Hide the bouncer challenge if it is currently showing.
+ * This may restore previously blocked access to other child views.
+ */
+ void hideBouncer();
+
+ /**
+ * Returns true if the challenge is currently in bouncer mode,
+ * potentially blocking access to other child views.
+ */
+ boolean isBouncing();
+
+ /**
+ * Set a listener that will respond to changes in bouncer state.
+ *
+ * @param listener listener to register
+ */
+ void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener);
+
+ /**
+ * Listener interface that reports changes in bouncer state.
+ * The bouncer is
+ */
+ public interface OnBouncerStateChangedListener {
+ /**
+ * Called when the bouncer state changes.
+ * The bouncer is activated when the user must pass a security challenge
+ * to proceed with the requested action.
+ *
+ * <p>This differs from simply showing or hiding the security challenge
+ * as the bouncer will prevent interaction with other elements of the UI.
+ * If the user attempts to escape from the bouncer, it will be dismissed,
+ * this method will be called with false as the parameter, and the action
+ * should be canceled. If the security component reports a successful
+ * authentication and the containing code calls hideBouncer() as a result,
+ * this method will also be called with a false parameter. It is up to the
+ * caller of hideBouncer to be ready for this.</p>
+ *
+ * @param bouncerActive true if the bouncer is now active,
+ * false if the bouncer was dismissed.
+ */
+ public void onBouncerStateChanged(boolean bouncerActive);
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CheckLongPressHelper.java b/policy/src/com/android/internal/policy/impl/keyguard/CheckLongPressHelper.java
new file mode 100644
index 000000000000..4825e23ec9c9
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/CheckLongPressHelper.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+public class CheckLongPressHelper {
+ private View mView;
+ private boolean mHasPerformedLongPress;
+ private CheckForLongPress mPendingCheckForLongPress;
+ private float mDownX, mDownY;
+ private int mLongPressTimeout;
+ private int mScaledTouchSlop;
+
+ class CheckForLongPress implements Runnable {
+ public void run() {
+ if ((mView.getParent() != null) && mView.hasWindowFocus()
+ && !mHasPerformedLongPress) {
+ if (mView.performLongClick()) {
+ mView.setPressed(false);
+ mHasPerformedLongPress = true;
+ }
+ }
+ }
+ }
+
+ public CheckLongPressHelper(View v) {
+ mScaledTouchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();
+ mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
+ mView = v;
+ }
+
+ public void postCheckForLongPress(MotionEvent ev) {
+ mDownX = ev.getX();
+ mDownY = ev.getY();
+ mHasPerformedLongPress = false;
+
+ if (mPendingCheckForLongPress == null) {
+ mPendingCheckForLongPress = new CheckForLongPress();
+ }
+ mView.postDelayed(mPendingCheckForLongPress, mLongPressTimeout);
+ }
+
+ public void onMove(MotionEvent ev) {
+ float x = ev.getX();
+ float y = ev.getY();
+ boolean xMoved = Math.abs(mDownX - x) > mScaledTouchSlop;
+ boolean yMoved = Math.abs(mDownY - y) > mScaledTouchSlop;
+
+ if (xMoved || yMoved) {
+ cancelLongPress();
+ }
+ }
+
+ public void cancelLongPress() {
+ mHasPerformedLongPress = false;
+ if (mPendingCheckForLongPress != null) {
+ mView.removeCallbacks(mPendingCheckForLongPress);
+ mPendingCheckForLongPress = null;
+ }
+ }
+
+ public boolean hasPerformedLongPress() {
+ return mHasPerformedLongPress;
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
index 203ba3c6a071..cab4ed96cf14 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
@@ -23,7 +23,6 @@ import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.view.View;
-import android.view.View.OnClickListener;
import android.widget.Button;
import com.android.internal.telephony.IccCardConstants.State;
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
index 3fe16cf0f4ea..cae598cc7d99 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
@@ -147,10 +147,18 @@ public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
public boolean stop() {
if (DEBUG) Log.d(TAG, "stop()");
if (mHandler.getLooper() != Looper.myLooper()) {
- Log.e(TAG, "stop() called off of the UI thread");
+ Log.e(TAG, "stop() called from non-UI thread");
}
boolean mWasRunning = mIsRunning;
+ try {
+ if (mService != null) {
+ mService.makeInvisible();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Caught exception making Face Unlock invisible: " + e.toString());
+ }
+
stopUi();
if (mBoundToService) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java
new file mode 100644
index 000000000000..9c21830cf710
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
+
+/**
+ * Base class for PIN and password unlock screens.
+ */
+public abstract class KeyguardAbsKeyInputView extends LinearLayout
+ implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+ protected KeyguardSecurityCallback mCallback;
+ protected TextView mPasswordEntry;
+ protected LockPatternUtils mLockPatternUtils;
+ protected SecurityMessageDisplay mSecurityMessageDisplay;
+ protected boolean mEnableHaptics;
+
+ // To avoid accidental lockout due to events while the device in in the pocket, ignore
+ // any passwords with length less than or equal to this length.
+ protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
+
+ public KeyguardAbsKeyInputView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ }
+
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ if (hasWindowFocus) {
+ reset();
+ }
+ }
+
+ public void reset() {
+ // start fresh
+ mPasswordEntry.setText("");
+ mPasswordEntry.requestFocus();
+
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
+ if (deadline != 0) {
+ handleAttemptLockout(deadline);
+ } else {
+ resetState();
+ }
+ }
+
+ protected abstract void resetState();
+
+ @Override
+ protected void onFinishInflate() {
+ mLockPatternUtils = new LockPatternUtils(mContext);
+
+ mPasswordEntry = (TextView) findViewById(R.id.passwordEntry);
+ mPasswordEntry.setOnEditorActionListener(this);
+ mPasswordEntry.addTextChangedListener(this);
+
+ // Poke the wakelock any time the text is selected or modified
+ mPasswordEntry.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ mCallback.userActivity(0); // TODO: customize timeout for text?
+ }
+ });
+
+ mPasswordEntry.addTextChangedListener(new TextWatcher() {
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ public void afterTextChanged(Editable s) {
+ if (mCallback != null) {
+ mCallback.userActivity(0);
+ }
+ }
+ });
+ mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
+ }
+
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ // send focus to the password field
+ return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
+ }
+
+ protected void verifyPasswordAndUnlock() {
+ String entry = mPasswordEntry.getText().toString();
+ if (mLockPatternUtils.checkPassword(entry)) {
+ mCallback.reportSuccessfulUnlockAttempt();
+ mCallback.dismiss(true);
+ } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
+ // to avoid accidental lockout, only count attempts that are long enough to be a
+ // real password. This may require some tweaking.
+ mCallback.reportFailedUnlockAttempt();
+ if (0 == (mCallback.getFailedAttempts()
+ % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
+ handleAttemptLockout(deadline);
+ }
+ mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pin, true);
+ }
+ mPasswordEntry.setText("");
+ }
+
+ // Prevent user from using the PIN/Password entry until scheduled deadline.
+ protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ mPasswordEntry.setEnabled(false);
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ int secondsRemaining = (int) (millisUntilFinished / 1000);
+ mSecurityMessageDisplay.setMessage(
+ R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining);
+ }
+
+ @Override
+ public void onFinish() {
+ resetState();
+ }
+ }.start();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ mCallback.userActivity(0);
+ return false;
+ }
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ // Check if this was the result of hitting the enter key
+ if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT) {
+ verifyPasswordAndUnlock();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+
+ }
+
+ @Override
+ public void onResume() {
+ reset();
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ if (mCallback != null) {
+ mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS);
+ }
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+
+ // Cause a VIRTUAL_KEY vibration
+ public void doHapticKeyClick() {
+ if (mEnableHaptics) {
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+ | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ }
+ }
+}
+
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
index ebca4acd0eac..9c877550d4e0 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAccountView.java
@@ -84,9 +84,6 @@ public class KeyguardAccountView extends LinearLayout implements KeyguardSecurit
protected void onFinishInflate() {
super.onFinishInflate();
- // We always set a dummy NavigationManager to avoid null checks
- mSecurityMessageDisplay = new KeyguardNavigationManager(null);
-
mLogin = (EditText) findViewById(R.id.login);
mLogin.setFilters(new InputFilter[] { new LoginFilter.UsernameFilterGeneric() } );
mLogin.addTextChangedListener(this);
@@ -96,6 +93,8 @@ public class KeyguardAccountView extends LinearLayout implements KeyguardSecurit
mOk = (Button) findViewById(R.id.ok);
mOk.setOnClickListener(this);
+
+ mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
reset();
}
@@ -317,9 +316,7 @@ public class KeyguardAccountView extends LinearLayout implements KeyguardSecurit
}
@Override
- public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
- mSecurityMessageDisplay = display;
- reset();
+ public void showUsabilityHint() {
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java
new file mode 100644
index 000000000000..4c500696cc4d
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardActivityLauncher.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import java.util.List;
+
+public abstract class KeyguardActivityLauncher {
+ private static final String TAG = KeyguardActivityLauncher.class.getSimpleName();
+ private static final boolean DEBUG = KeyguardHostView.DEBUG;
+ private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
+ private static final Intent SECURE_CAMERA_INTENT =
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+ .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ private static final Intent INSECURE_CAMERA_INTENT =
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+
+ abstract Context getContext();
+
+ abstract KeyguardSecurityCallback getCallback();
+
+ abstract LockPatternUtils getLockPatternUtils();
+
+ public static class CameraWidgetInfo {
+ public String contextPackage;
+ public int layoutId;
+ }
+
+ public CameraWidgetInfo getCameraWidgetInfo() {
+ CameraWidgetInfo info = new CameraWidgetInfo();
+ Intent intent = getCameraIntent();
+ PackageManager packageManager = getContext().getPackageManager();
+ final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
+ intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
+ if (appList.size() == 0) {
+ if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Nothing found");
+ return null;
+ }
+ ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
+ PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
+ getLockPatternUtils().getCurrentUser());
+ if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): resolved: " + resolved);
+ if (wouldLaunchResolverActivity(resolved, appList)) {
+ if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Would launch resolver");
+ return info;
+ }
+ if (resolved == null || resolved.activityInfo == null) {
+ return null;
+ }
+ if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
+ if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no metadata found");
+ return info;
+ }
+ int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
+ if (layoutId == 0) {
+ if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no layout specified");
+ return info;
+ }
+ info.contextPackage = resolved.activityInfo.packageName;
+ info.layoutId = layoutId;
+ return info;
+ }
+
+ public void launchCamera() {
+ LockPatternUtils lockPatternUtils = getLockPatternUtils();
+ if (lockPatternUtils.isSecure()) {
+ // Launch the secure version of the camera
+ if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) {
+ // TODO: Show disambiguation dialog instead.
+ // For now, we'll treat this like launching any other app from secure keyguard.
+ // When they do, user sees the system's ResolverActivity which lets them choose
+ // which secure camera to use.
+ launchActivity(SECURE_CAMERA_INTENT, false, false);
+ } else {
+ launchActivity(SECURE_CAMERA_INTENT, true, false);
+ }
+ } else {
+ // Launch the normal camera
+ launchActivity(INSECURE_CAMERA_INTENT, false, false);
+ }
+ }
+
+ /**
+ * Launches the said intent for the current foreground user.
+ * @param intent
+ * @param showsWhileLocked true if the activity can be run on top of keyguard.
+ * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
+ */
+ public void launchActivity(final Intent intent, boolean showsWhileLocked, boolean animate) {
+ final Context context = getContext();
+ final Bundle animation = animate ? null :
+ ActivityOptions.makeCustomAnimation(context, com.android.internal.R.anim.fade_in,
+ com.android.internal.R.anim.fade_out).toBundle();
+ LockPatternUtils lockPatternUtils = getLockPatternUtils();
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ boolean isSecure = lockPatternUtils.isSecure();
+ if (!isSecure || showsWhileLocked) {
+ if (!isSecure) try {
+ ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+ } catch (RemoteException e) {
+ Log.w(TAG, "can't dismiss keyguard on launch");
+ }
+ try {
+ context.startActivityAsUser(intent, animation,
+ new UserHandle(UserHandle.USER_CURRENT));
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Activity not found for intent + " + intent.getAction());
+ }
+ } else {
+ // Create a runnable to start the activity and ask the user to enter their
+ // credentials.
+ KeyguardSecurityCallback callback = getCallback();
+ callback.setOnDismissRunnable(new Runnable() {
+ @Override
+ public void run() {
+ context.startActivityAsUser(intent, animation,
+ new UserHandle(UserHandle.USER_CURRENT));
+ }
+ });
+ callback.dismiss(false);
+ }
+ }
+
+ private Intent getCameraIntent() {
+ return getLockPatternUtils().isSecure() ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
+ }
+
+ private boolean wouldLaunchResolverActivity(Intent intent) {
+ PackageManager packageManager = getContext().getPackageManager();
+ ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
+ PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
+ List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
+ intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
+ return wouldLaunchResolverActivity(resolved, appList);
+ }
+
+ private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
+ // If the list contains the above resolved activity, then it can't be
+ // ResolverActivity itself.
+ for (int i = 0; i < appList.size(); i++) {
+ ResolveInfo tmp = appList.get(i);
+ if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
+ && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java
new file mode 100644
index 000000000000..29124c41681b
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardCircleFramedDrawable.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+import android.util.Log;
+
+class KeyguardCircleFramedDrawable extends Drawable {
+
+ private final Bitmap mBitmap;
+ private final int mSize;
+ private final Paint mPaint;
+ private final float mShadowRadius;
+ private final float mStrokeWidth;
+ private final int mFrameColor;
+ private final int mHighlightColor;
+ private final int mFrameShadowColor;
+
+ private float mScale;
+ private Path mFramePath;
+ private Rect mSrcRect;
+ private RectF mDstRect;
+ private RectF mFrameRect;
+ private boolean mPressed;
+
+ public KeyguardCircleFramedDrawable(Bitmap bitmap, int size,
+ int frameColor, float strokeWidth,
+ int frameShadowColor, float shadowRadius,
+ int highlightColor) {
+ super();
+ mSize = size;
+ mShadowRadius = shadowRadius;
+ mFrameColor = frameColor;
+ mFrameShadowColor = frameShadowColor;
+ mStrokeWidth = strokeWidth;
+ mHighlightColor = highlightColor;
+
+ mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(mBitmap);
+
+ final int width = bitmap.getWidth();
+ final int height = bitmap.getHeight();
+ final int square = Math.min(width, height);
+
+ final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, square, square);
+ final RectF circleRect = new RectF(0f, 0f, mSize, mSize);
+ circleRect.inset(mStrokeWidth / 2f, mStrokeWidth / 2f);
+ circleRect.inset(mShadowRadius, mShadowRadius);
+
+ final Path fillPath = new Path();
+ fillPath.addArc(circleRect, 0f, 360f);
+
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+ // opaque circle matte
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setColor(Color.BLACK);
+ mPaint.setStyle(Paint.Style.FILL);
+ canvas.drawPath(fillPath, mPaint);
+
+ // mask in the icon where the bitmap is opaque
+ mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
+ canvas.drawBitmap(bitmap, cropRect, circleRect, mPaint);
+
+ // prepare paint for frame drawing
+ mPaint.setXfermode(null);
+
+ mScale = 1f;
+
+ mSrcRect = new Rect(0, 0, mSize, mSize);
+ mDstRect = new RectF(0, 0, mSize, mSize);
+ mFrameRect = new RectF(mDstRect);
+ mFramePath = new Path();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ // clear background
+ final float outside = Math.min(canvas.getWidth(), canvas.getHeight());
+ final float inside = mScale * outside;
+ final float pad = (outside - inside) / 2f;
+
+ mDstRect.set(pad, pad, outside - pad, outside - pad);
+ canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
+
+ mFrameRect.set(mDstRect);
+ mFrameRect.inset(mStrokeWidth / 2f, mStrokeWidth / 2f);
+ mFrameRect.inset(mShadowRadius, mShadowRadius);
+
+ mFramePath.reset();
+ mFramePath.addArc(mFrameRect, 0f, 360f);
+
+ // white frame
+ if (mPressed) {
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(Color.argb((int) (0.33f * 255),
+ Color.red(mHighlightColor),
+ Color.green(mHighlightColor),
+ Color.blue(mHighlightColor)));
+ canvas.drawPath(mFramePath, mPaint);
+ }
+ mPaint.setStrokeWidth(mStrokeWidth);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setColor(mPressed ? mHighlightColor : mFrameColor);
+ mPaint.setShadowLayer(mShadowRadius, 0f, 0f, mFrameShadowColor);
+ canvas.drawPath(mFramePath, mPaint);
+ }
+
+ public void setScale(float scale) {
+ Log.i("KFD", "scale: " + scale);
+ mScale = scale;
+ }
+
+ public float getScale() {
+ return mScale;
+ }
+
+ public void setPressed(boolean pressed) {
+ mPressed = pressed;
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
index 04ab8713eaa6..a88456849a45 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
@@ -35,10 +35,12 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu
private KeyguardSecurityCallback mKeyguardSecurityCallback;
private LockPatternUtils mLockPatternUtils;
private BiometricSensorUnlock mBiometricUnlock;
- private SecurityMessageDisplay mSecurityMessageDisplay;
private View mFaceUnlockAreaView;
private ImageButton mCancelButton;
+ private boolean mIsShowing = false;
+ private final Object mIsShowingLock = new Object();
+
public KeyguardFaceUnlockView(Context context) {
this(context, null);
}
@@ -83,7 +85,7 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu
public void onPause() {
if (DEBUG) Log.d(TAG, "onPause()");
if (mBiometricUnlock != null) {
- mBiometricUnlock.stopAndShowBackup();
+ mBiometricUnlock.stop();
}
KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback);
}
@@ -112,6 +114,7 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu
}
private void initializeBiometricUnlockView() {
+ if (DEBUG) Log.d(TAG, "initializeBiometricUnlockView()");
mFaceUnlockAreaView = findViewById(R.id.face_unlock_area_view);
if (mFaceUnlockAreaView != null) {
mBiometricUnlock = new FaceUnlock(mContext);
@@ -129,9 +132,9 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu
}
/**
- * Starts the biometric unlock if it should be started based on a number of factors including
- * the mSuppressBiometricUnlock flag. If it should not be started, it hides the biometric
- * unlock area.
+ * Starts the biometric unlock if it should be started based on a number of factors. If it
+ * should not be started, it either goes to the back up, or remains showing to prepare for
+ * it being started later.
*/
private void maybeStartBiometricUnlock() {
if (DEBUG) Log.d(TAG, "maybeStartBiometricUnlock()");
@@ -142,12 +145,25 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu
LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
PowerManager powerManager = (PowerManager) mContext.getSystemService(
Context.POWER_SERVICE);
+
+ boolean isShowing;
+ synchronized(mIsShowingLock) {
+ isShowing = mIsShowing;
+ }
+
+ // Don't start it if the screen is off or if it's not showing, but keep this view up
+ // because we want it here and ready for when the screen turns on or when it does start
+ // showing.
+ if (!powerManager.isScreenOn() || !isShowing) {
+ mBiometricUnlock.stop(); // It shouldn't be running but calling this can't hurt.
+ return;
+ }
+
// TODO: Some of these conditions are handled in KeyguardSecurityModel and may not be
// necessary here.
if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
&& !monitor.getMaxBiometricUnlockAttemptsReached()
- && !backupIsTimedOut
- && powerManager.isScreenOn()) {
+ && !backupIsTimedOut) {
mBiometricUnlock.start();
} else {
mBiometricUnlock.stopAndShowBackup();
@@ -161,7 +177,9 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu
public void onPhoneStateChanged(int phoneState) {
if (DEBUG) Log.d(TAG, "onPhoneStateChanged(" + phoneState + ")");
if (phoneState == TelephonyManager.CALL_STATE_RINGING) {
- mBiometricUnlock.stopAndShowBackup();
+ if (mBiometricUnlock != null) {
+ mBiometricUnlock.stopAndShowBackup();
+ }
}
}
@@ -174,10 +192,28 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu
// No longer required; static value set by KeyguardViewMediator
// mLockPatternUtils.setCurrentUser(userId);
}
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")");
+ boolean wasShowing = false;
+ synchronized(mIsShowingLock) {
+ wasShowing = mIsShowing;
+ mIsShowing = showing;
+ }
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(
+ Context.POWER_SERVICE);
+ if (mBiometricUnlock != null) {
+ if (!showing && wasShowing) {
+ mBiometricUnlock.stop();
+ } else if (showing && powerManager.isScreenOn() && !wasShowing) {
+ maybeStartBiometricUnlock();
+ }
+ }
+ }
};
@Override
- public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
- mSecurityMessageDisplay = display;
+ public void showUsabilityHint() {
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index b86e5b8564ca..ec89da25840a 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -35,7 +35,9 @@ import android.graphics.Rect;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
@@ -47,7 +49,6 @@ import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.widget.RemoteViews.OnClickHandler;
import android.widget.TextView;
-import android.widget.ViewFlipper;
import com.android.internal.R;
import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -60,7 +61,7 @@ public class KeyguardHostView extends KeyguardViewBase {
private static final String TAG = "KeyguardViewHost";
// Use this to debug all of keyguard
- public static boolean DEBUG;
+ public static boolean DEBUG = KeyguardViewMediator.DEBUG;
// also referenced in SecuritySettings.java
static final int APPWIDGET_HOST_ID = 0x4B455947;
@@ -71,9 +72,8 @@ public class KeyguardHostView extends KeyguardViewBase {
private static final int TRANSPORT_VISIBLE = 2;
private AppWidgetHost mAppWidgetHost;
- private KeyguardWidgetRegion mAppWidgetRegion;
private KeyguardWidgetPager mAppWidgetContainer;
- private ViewFlipper mSecurityViewContainer;
+ private KeyguardSecurityViewFlipper mSecurityViewContainer;
private KeyguardSelectorView mKeyguardSelectorView;
private KeyguardTransportControlView mTransportControl;
private boolean mEnableMenuKey;
@@ -87,6 +87,7 @@ public class KeyguardHostView extends KeyguardViewBase {
private LockPatternUtils mLockPatternUtils;
private KeyguardSecurityModel mSecurityModel;
+ private KeyguardViewStateManager mViewStateManager;
private Rect mTempRect = new Rect();
private int mTransportState = TRANSPORT_GONE;
@@ -101,6 +102,7 @@ public class KeyguardHostView extends KeyguardViewBase {
void hideSecurityView(int duration);
void showSecurityView();
void showUnlockHint();
+ void userActivity();
}
public KeyguardHostView(Context context) {
@@ -151,21 +153,41 @@ public class KeyguardHostView extends KeyguardViewBase {
@Override
protected void onFinishInflate() {
- mAppWidgetRegion = (KeyguardWidgetRegion) findViewById(R.id.kg_widget_region);
- mAppWidgetRegion.setVisibility(VISIBLE);
- mAppWidgetRegion.setCallbacks(mWidgetCallbacks);
-
+ // Grab instances of and make any necessary changes to the main layouts. Create
+ // view state manager and wire up necessary listeners / callbacks.
mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
- mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper);
- mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
+ mAppWidgetContainer.setVisibility(VISIBLE);
+ mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
+ mAppWidgetContainer.setMinScale(0.5f);
addDefaultWidgets();
- updateSecurityViews();
- setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
+ addWidgetsFromSettings();
- if (KeyguardUpdateMonitor.getInstance(mContext).getIsFirstBoot()) {
- showPrimarySecurityScreen(false);
+ mViewStateManager = new KeyguardViewStateManager();
+ SlidingChallengeLayout slider =
+ (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
+ if (slider != null) {
+ slider.setOnChallengeScrolledListener(mViewStateManager);
}
+ mAppWidgetContainer.setViewStateManager(mViewStateManager);
+ mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
+
+ mViewStateManager.setPagedView(mAppWidgetContainer);
+ mViewStateManager.setChallengeLayout(slider != null ? slider :
+ (ChallengeLayout) findViewById(R.id.multi_pane_challenge));
+ mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
+ mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
+ mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
+
+ mViewStateManager.showUsabilityHints();
+
+ if (!(mContext instanceof Activity)) {
+ setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
+ }
+
+ showPrimarySecurityScreen(false);
+
+ updateSecurityViews();
}
private void updateSecurityViews() {
@@ -195,22 +217,9 @@ public class KeyguardHostView extends KeyguardViewBase {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mAppWidgetHost.startListening();
- // TODO: Re-enable when we have layouts that can support a better variety of widgets.
- // maybePopulateWidgets();
- disableStatusViewInteraction();
post(mSwitchPageRunnable);
}
- private void disableStatusViewInteraction() {
- // Disable all user interaction on status view. This is done to prevent falsing in the
- // pocket from triggering useractivity and prevents 3rd party replacement widgets
- // from responding to user interaction while in this position.
- View statusView = findViewById(R.id.keyguard_status_view);
- if (statusView instanceof KeyguardWidgetFrame) {
- ((KeyguardWidgetFrame) statusView).setDisableUserInteraction(true);
- }
- }
-
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
@@ -221,12 +230,12 @@ public class KeyguardHostView extends KeyguardViewBase {
return mAppWidgetHost;
}
- void addWidget(AppWidgetHostView view) {
- mAppWidgetContainer.addWidget(view);
+ void addWidget(AppWidgetHostView view, int pageIndex) {
+ mAppWidgetContainer.addWidget(view, pageIndex);
}
- private KeyguardWidgetRegion.Callbacks mWidgetCallbacks
- = new KeyguardWidgetRegion.Callbacks() {
+ private KeyguardWidgetPager.Callbacks mWidgetCallbacks
+ = new KeyguardWidgetPager.Callbacks() {
@Override
public void userActivity() {
if (mViewMediatorCallback != null) {
@@ -246,8 +255,8 @@ public class KeyguardHostView extends KeyguardViewBase {
public long getUserActivityTimeout() {
// Currently only considering user activity timeouts needed by widgets.
// Could also take into account longer timeouts for certain security views.
- if (mAppWidgetRegion != null) {
- return mAppWidgetRegion.getUserActivityTimeout();
+ if (mAppWidgetContainer != null) {
+ return mAppWidgetContainer.getUserActivityTimeout();
}
return -1;
}
@@ -317,13 +326,11 @@ public class KeyguardHostView extends KeyguardViewBase {
case Pattern:
messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
break;
-
- case Password: {
- final boolean isPin = mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
- messageId = isPin ? R.string.kg_too_many_failed_pin_attempts_dialog_message
- : R.string.kg_too_many_failed_password_attempts_dialog_message;
- }
+ case PIN:
+ messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
+ break;
+ case Password:
+ messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
break;
}
@@ -418,7 +425,8 @@ public class KeyguardHostView extends KeyguardViewBase {
void showPrimarySecurityScreen(boolean turningOff) {
SecurityMode securityMode = mSecurityModel.getSecurityMode();
if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
- if (!turningOff && KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
+ if (!turningOff && KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()
+ && !KeyguardUpdateMonitor.getInstance(mContext).getIsFirstBoot()) {
// If we're not turning off, then allow biometric alternate.
// We'll reload it when the device comes back on.
securityMode = mSecurityModel.getAlternateFor(securityMode);
@@ -465,6 +473,7 @@ public class KeyguardHostView extends KeyguardViewBase {
switch (mCurrentSecuritySelection) {
case Pattern:
case Password:
+ case PIN:
case Account:
case Biometric:
finish = true;
@@ -504,6 +513,8 @@ public class KeyguardHostView extends KeyguardViewBase {
if (mViewMediatorCallback != null) {
mViewMediatorCallback.keyguardDone(true);
}
+ } else {
+ mViewStateManager.showBouncer(true);
}
}
@@ -607,7 +618,6 @@ public class KeyguardHostView extends KeyguardViewBase {
break;
}
}
- boolean simPukFullScreen = getResources().getBoolean(R.bool.kg_sim_puk_account_full_screen);
int layoutId = getLayoutIdFor(securityMode);
if (view == null && layoutId != 0) {
final LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -615,24 +625,7 @@ public class KeyguardHostView extends KeyguardViewBase {
View v = inflater.inflate(layoutId, this, false);
mSecurityViewContainer.addView(v);
updateSecurityView(v);
-
- view = (KeyguardSecurityView) v;
- TextView navigationText = ((TextView) findViewById(R.id.keyguard_message_area));
-
- // Some devices can fit a navigation area, others cannot. On devices that cannot,
- // we display the security message in status area.
- if (navigationText != null) {
- view.setSecurityMessageDisplay(new KeyguardNavigationManager(navigationText));
- } else {
- view.setSecurityMessageDisplay(mKeyguardStatusViewManager);
- }
- }
-
- if (securityMode == SecurityMode.SimPin || securityMode == SecurityMode.SimPuk ||
- securityMode == SecurityMode.Account) {
- if (simPukFullScreen) {
- mAppWidgetRegion.setVisibility(View.GONE);
- }
+ view = (KeyguardSecurityView)v;
}
if (view instanceof KeyguardSelectorView) {
@@ -658,6 +651,15 @@ public class KeyguardHostView extends KeyguardViewBase {
KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
KeyguardSecurityView newView = getSecurityView(securityMode);
+ // Enter full screen mode if we're in SIM or Account screen
+ boolean fullScreenEnabled = getResources().getBoolean(
+ com.android.internal.R.bool.kg_sim_puk_account_full_screen);
+ boolean isSimOrAccount = securityMode == SecurityMode.SimPin
+ || securityMode == SecurityMode.SimPuk
+ || securityMode == SecurityMode.Account;
+ mAppWidgetContainer.setVisibility(
+ isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);
+
// Emulate Activity life cycle
if (oldView != null) {
oldView.onPause();
@@ -698,7 +700,7 @@ public class KeyguardHostView extends KeyguardViewBase {
@Override
public void onScreenTurnedOn() {
- if (DEBUG) Log.d(TAG, "screen on");
+ if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
showPrimarySecurityScreen(false);
getSecurityView(mCurrentSecuritySelection).onResume();
@@ -706,18 +708,24 @@ public class KeyguardHostView extends KeyguardViewBase {
// layout is blank but forcing a layout causes it to reappear (e.g. with with
// hierarchyviewer).
requestLayout();
+
+ if (mViewStateManager != null) {
+ mViewStateManager.showUsabilityHints();
+ }
}
@Override
public void onScreenTurnedOff() {
- if (DEBUG) Log.d(TAG, "screen off");
+ if (DEBUG) Log.d(TAG, "screen off, instance " + Integer.toHexString(hashCode()));
+ saveStickyWidgetIndex();
showPrimarySecurityScreen(true);
getSecurityView(mCurrentSecuritySelection).onPause();
}
@Override
public void show() {
- onScreenTurnedOn();
+ if (DEBUG) Log.d(TAG, "show()");
+ showPrimarySecurityScreen(false);
}
private boolean isSecure() {
@@ -726,6 +734,7 @@ public class KeyguardHostView extends KeyguardViewBase {
case Pattern:
return mLockPatternUtils.isLockPatternEnabled();
case Password:
+ case PIN:
return mLockPatternUtils.isLockPasswordEnabled();
case SimPin:
case SimPuk:
@@ -760,6 +769,7 @@ public class KeyguardHostView extends KeyguardViewBase {
mViewMediatorCallback.keyguardDone(true);
}
} else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
+ && securityMode != KeyguardSecurityModel.SecurityMode.PIN
&& securityMode != KeyguardSecurityModel.SecurityMode.Password) {
// can only verify unlock when in pattern/password mode
if (mViewMediatorCallback != null) {
@@ -776,6 +786,7 @@ public class KeyguardHostView extends KeyguardViewBase {
switch (securityMode) {
case None: return R.id.keyguard_selector_view;
case Pattern: return R.id.keyguard_pattern_view;
+ case PIN: return R.id.keyguard_pin_view;
case Password: return R.id.keyguard_password_view;
case Biometric: return R.id.keyguard_face_unlock_view;
case Account: return R.id.keyguard_account_view;
@@ -789,6 +800,7 @@ public class KeyguardHostView extends KeyguardViewBase {
switch (securityMode) {
case None: return R.layout.keyguard_selector_view;
case Pattern: return R.layout.keyguard_pattern_view;
+ case PIN: return R.layout.keyguard_pin_view;
case Password: return R.layout.keyguard_password_view;
case Biometric: return R.layout.keyguard_face_unlock_view;
case Account: return R.layout.keyguard_account_view;
@@ -799,23 +811,98 @@ public class KeyguardHostView extends KeyguardViewBase {
}
}
- private void addWidget(int appId) {
+ private void addWidget(int appId, int pageIndex) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId);
if (appWidgetInfo != null) {
AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
- addWidget(view);
+ addWidget(view, pageIndex);
} else {
- Log.w(TAG, "AppWidgetInfo was null; not adding widget id " + appId);
+ Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting");
+ mLockPatternUtils.removeAppWidget(appId);
}
}
+ private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
+ new CameraWidgetFrame.Callbacks() {
+ @Override
+ public void onLaunchingCamera() {
+ SlidingChallengeLayout slider = locateSlider();
+ if (slider != null) {
+ slider.showHandle(false);
+ }
+ }
+
+ @Override
+ public void onCameraLaunched() {
+ if (isCameraPage(mAppWidgetContainer.getCurrentPage())) {
+ mAppWidgetContainer.scrollLeft();
+ }
+ SlidingChallengeLayout slider = locateSlider();
+ if (slider != null) {
+ slider.showHandle(true);
+ slider.showChallenge(true);
+ }
+ }
+
+ private SlidingChallengeLayout locateSlider() {
+ return (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
+ }
+ };
+
+ private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
+ @Override
+ Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ LockPatternUtils getLockPatternUtils() {
+ return mLockPatternUtils;
+ }};
+
private void addDefaultWidgets() {
LayoutInflater inflater = LayoutInflater.from(mContext);
- inflater.inflate(R.layout.keyguard_status_view, mAppWidgetContainer, true);
inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
- inflateAndAddUserSelectorWidgetIfNecessary();
+ View addWidget = inflater.inflate(R.layout.keyguard_add_widget, null, true);
+ mAppWidgetContainer.addWidget(addWidget);
+ if (mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
+ View cameraWidget =
+ CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
+ if (cameraWidget != null) {
+ mAppWidgetContainer.addWidget(cameraWidget);
+ }
+ }
+
+ View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
+ addWidgetButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCallback.setOnDismissRunnable(new Runnable() {
+
+ @Override
+ public void run() {
+ Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ mContext.startActivityAsUser(intent,
+ new UserHandle(UserHandle.USER_CURRENT));
+ }
+ });
+ mCallback.dismiss(false);
+ }
+ });
+
+ enableUserSelectorIfNecessary();
initializeTransportControl();
}
@@ -856,13 +943,9 @@ public class KeyguardHostView extends KeyguardViewBase {
}
});
}
-
- mKeyguardStatusViewManager = ((KeyguardStatusView)
- findViewById(R.id.keyguard_status_view_face_palm)).getManager();
-
}
- private void maybePopulateWidgets() {
+ private void addWidgetsFromSettings() {
DevicePolicyManager dpm =
(DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
if (dpm != null) {
@@ -874,27 +957,29 @@ public class KeyguardHostView extends KeyguardViewBase {
}
}
- // Replace status widget if selected by user in Settings
- int statusWidgetId = mLockPatternUtils.getStatusWidget();
- if (statusWidgetId != -1) {
- addWidget(statusWidgetId);
- View newStatusWidget = mAppWidgetContainer.getChildAt(
- mAppWidgetContainer.getChildCount() - 1);
-
- int oldStatusWidgetPosition = getWidgetPosition(R.id.keyguard_status_view);
- mAppWidgetContainer.removeViewAt(oldStatusWidgetPosition);
-
- // Re-add new status widget at position of old one
- mAppWidgetContainer.removeView(newStatusWidget);
- newStatusWidget.setId(R.id.keyguard_status_view);
- mAppWidgetContainer.addView(newStatusWidget, oldStatusWidgetPosition);
+ View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
+ int addPageIndex = mAppWidgetContainer.indexOfChild(addWidget);
+ // This shouldn't happen, but just to be safe!
+ if (addPageIndex < 0) {
+ addPageIndex = 0;
}
// Add user-selected widget
- final int[] widgets = mLockPatternUtils.getUserDefinedWidgets();
- for (int i = 0; i < widgets.length; i++) {
- if (widgets[i] != -1) {
- addWidget(widgets[i]);
+ final int[] widgets = mLockPatternUtils.getAppWidgets();
+ if (widgets == null) {
+ Log.d(TAG, "Problem reading widgets");
+ } else {
+ for (int i = widgets.length -1; i >= 0; i--) {
+ if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
+ mAppWidgetContainer.addWidget(statusWidget, addPageIndex + 1);
+ } else {
+ // We add the widgets from left to right, starting after the first page after
+ // the add page. We count down, since the order will be persisted from right
+ // to left, starting after camera.
+ addWidget(widgets[i], addPageIndex + 1);
+ }
}
}
}
@@ -938,6 +1023,8 @@ public class KeyguardHostView extends KeyguardViewBase {
@Override
public Parcelable onSaveInstanceState() {
+ if (DEBUG) Log.d(TAG, "onSaveInstanceState");
+ saveStickyWidgetIndex();
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.transportState = mTransportState;
@@ -946,6 +1033,7 @@ public class KeyguardHostView extends KeyguardViewBase {
@Override
public void onRestoreInstanceState(Parcelable state) {
+ if (DEBUG) Log.d(TAG, "onRestoreInstanceState");
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
@@ -956,48 +1044,90 @@ public class KeyguardHostView extends KeyguardViewBase {
post(mSwitchPageRunnable);
}
- private void showAppropriateWidgetPage() {
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused"));
+ if (!hasWindowFocus) {
+ saveStickyWidgetIndex();
+ }
+ }
- // The following sets the priority for showing widgets. Transport should be shown if
- // music is playing, followed by the multi-user widget if enabled, followed by the
- // status widget.
- final int pageToShow;
- if (mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE) {
+ private void showAppropriateWidgetPage() {
+ boolean isMusicPlaying =
+ mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE;
+ if (isMusicPlaying) {
mTransportState = TRANSPORT_VISIBLE;
- pageToShow = mAppWidgetContainer.indexOfChild(mTransportControl);
- } else {
- UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- final View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
- final int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
- if (multiUserPosition != -1 && mUm.getUsers(true).size() > 1) {
- pageToShow = multiUserPosition;
- } else {
- final View statusView = findViewById(R.id.keyguard_status_view);
- pageToShow = mAppWidgetContainer.indexOfChild(statusView);
- }
- if (mTransportState == TRANSPORT_VISIBLE) {
- mTransportState = TRANSPORT_INVISIBLE;
- }
+ } else if (mTransportState == TRANSPORT_VISIBLE) {
+ mTransportState = TRANSPORT_INVISIBLE;
}
+ int pageToShow = getAppropriateWidgetPage(isMusicPlaying);
mAppWidgetContainer.setCurrentPage(pageToShow);
}
- private void inflateAndAddUserSelectorWidgetIfNecessary() {
- // if there are multiple users, we need to add the multi-user switcher widget to the
- // keyguard.
+ private boolean isCameraPage(int pageIndex) {
+ View v = mAppWidgetContainer.getChildAt(pageIndex);
+ return v != null && v instanceof CameraWidgetFrame;
+ }
+
+ private boolean isAddPage(int pageIndex) {
+ View v = mAppWidgetContainer.getChildAt(pageIndex);
+ return v != null && v.getId() == R.id.keyguard_add_widget;
+ }
+
+ private int getAppropriateWidgetPage(boolean isMusicPlaying) {
+ // assumes at least one widget (besides camera + add)
+
+ // if music playing, show transport
+ if (isMusicPlaying) {
+ if (DEBUG) Log.d(TAG, "Music playing, show transport");
+ return mAppWidgetContainer.indexOfChild(mTransportControl);
+ }
+
+ // if we have a valid sticky widget, show it
+ int stickyWidgetIndex = mLockPatternUtils.getStickyAppWidgetIndex();
+ if (stickyWidgetIndex > -1
+ && stickyWidgetIndex < mAppWidgetContainer.getChildCount()
+ && !isAddPage(stickyWidgetIndex)
+ && !isCameraPage(stickyWidgetIndex)) {
+ if (DEBUG) Log.d(TAG, "Valid sticky widget found, show page " + stickyWidgetIndex);
+ return stickyWidgetIndex;
+ }
+
+ // else show the right-most widget (except for camera)
+ int rightMost = mAppWidgetContainer.getChildCount() - 1;
+ if (isCameraPage(rightMost)) {
+ rightMost--;
+ }
+ if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost);
+ return rightMost;
+ }
+
+ private void saveStickyWidgetIndex() {
+ int stickyWidgetIndex = mAppWidgetContainer.getCurrentPage();
+ if (isAddPage(stickyWidgetIndex)) {
+ stickyWidgetIndex++;
+ }
+ if (isCameraPage(stickyWidgetIndex)) {
+ stickyWidgetIndex--;
+ }
+ if (stickyWidgetIndex < 0 || stickyWidgetIndex >= mAppWidgetContainer.getChildCount()) {
+ stickyWidgetIndex = -1;
+ }
+ if (DEBUG) Log.d(TAG, "saveStickyWidgetIndex: " + stickyWidgetIndex);
+ mLockPatternUtils.setStickyAppWidgetIndex(stickyWidgetIndex);
+ }
+
+ private void enableUserSelectorIfNecessary() {
+ // if there are multiple users, we need to enable to multi-user switcher
UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
List<UserInfo> users = mUm.getUsers(true);
if (users.size() > 1) {
- KeyguardWidgetFrame userSwitcher = (KeyguardWidgetFrame)
- LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
- mAppWidgetContainer, false);
-
- // add the switcher in the first position
- mAppWidgetContainer.addView(userSwitcher, getWidgetPosition(R.id.keyguard_status_view));
- KeyguardMultiUserSelectorView multiUser = (KeyguardMultiUserSelectorView)
- userSwitcher.getChildAt(0);
-
+ KeyguardMultiUserSelectorView multiUser =
+ (KeyguardMultiUserSelectorView) findViewById(R.id.keyguard_user_selector);
+ multiUser.setVisibility(View.VISIBLE);
+ multiUser.addUsers(mUm.getUsers(true));
UserSwitcherCallback callback = new UserSwitcherCallback() {
@Override
public void hideSecurityView(int duration) {
@@ -1012,10 +1142,16 @@ public class KeyguardHostView extends KeyguardViewBase {
@Override
public void showUnlockHint() {
if (mKeyguardSelectorView != null) {
- mKeyguardSelectorView.ping();
+ mKeyguardSelectorView.showUsabilityHint();
}
}
+ @Override
+ public void userActivity() {
+ if (mViewMediatorCallback != null) {
+ mViewMediatorCallback.userActivity();
+ }
+ }
};
multiUser.setCallback(callback);
}
@@ -1065,4 +1201,10 @@ public class KeyguardHostView extends KeyguardViewBase {
return false;
}
+ /**
+ * Dismisses the keyguard by going to the next screen or making it gone.
+ */
+ public void dismiss() {
+ showNextSecurityScreenOrFinish(false);
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java
new file mode 100644
index 000000000000..0fc54cdfbcaf
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardLinearLayout.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * A layout that arranges its children into a special type of grid.
+ */
+public class KeyguardLinearLayout extends LinearLayout {
+ int mTopChild = 0;
+
+ public KeyguardLinearLayout(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardLinearLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setTopChild(View child) {
+ int top = indexOfChild(child);
+ mTopChild = top;
+ invalidate();
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java
new file mode 100644
index 000000000000..ca78cf95a7eb
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+import libcore.util.MutableInt;
+
+import com.android.internal.R;
+
+/***
+ * Manages a number of views inside of the given layout. See below for a list of widgets.
+ */
+class KeyguardMessageArea extends TextView {
+ static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
+ static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
+
+ static final int SECURITY_MESSAGE_DURATION = 5000;
+ static final String SEPARATOR = " ";
+
+ // are we showing battery information?
+ boolean mShowingBatteryInfo = false;
+
+ // last known plugged in state
+ boolean mPluggedIn = false;
+
+ // last known battery level
+ int mBatteryLevel = 100;
+
+ KeyguardUpdateMonitor mUpdateMonitor;
+
+ // Timeout before we reset the message to show charging/owner info
+ long mTimeout = SECURITY_MESSAGE_DURATION;
+
+ // Shadowed text values
+ protected boolean mBatteryCharged;
+ protected boolean mBatteryIsLow;
+
+ private Handler mHandler;
+
+ CharSequence mMessage;
+ boolean mShowingMessage;
+ Runnable mClearMessageRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mMessage = null;
+ mShowingMessage = false;
+ update();
+ }
+ };
+
+ public static class Helper implements SecurityMessageDisplay {
+ KeyguardMessageArea mMessageArea;
+ Helper(View v) {
+ mMessageArea = (KeyguardMessageArea) v.findViewById(R.id.keyguard_message_area);
+ if (mMessageArea == null) {
+ throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
+ }
+ }
+
+ public void setMessage(CharSequence msg, boolean important) {
+ if (!TextUtils.isEmpty(msg) && important) {
+ mMessageArea.mMessage = msg;
+ mMessageArea.securityMessageChanged();
+ }
+ }
+
+ public void setMessage(int resId, boolean important) {
+ if (resId != 0 && important) {
+ mMessageArea.mMessage = mMessageArea.getContext().getResources().getText(resId);
+ mMessageArea.securityMessageChanged();
+ }
+ }
+
+ public void setMessage(int resId, boolean important, Object... formatArgs) {
+ if (resId != 0 && important) {
+ mMessageArea.mMessage = mMessageArea.getContext().getString(resId, formatArgs);
+ mMessageArea.securityMessageChanged();
+ }
+ }
+
+ @Override
+ public void setTimeout(int timeoutMs) {
+ mMessageArea.mTimeout = timeoutMs;
+ }
+ }
+
+ private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
+ mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
+ mPluggedIn = status.isPluggedIn();
+ mBatteryLevel = status.level;
+ mBatteryCharged = status.isCharged();
+ mBatteryIsLow = status.isBatteryLow();
+ update();
+ }
+ };
+
+ public KeyguardMessageArea(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardMessageArea(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // This is required to ensure marquee works
+ setSelected(true);
+
+ // Registering this callback immediately updates the battery state, among other things.
+ mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
+ mUpdateMonitor.registerCallback(mInfoCallback);
+ mHandler = new Handler(Looper.myLooper());
+
+ update();
+ }
+
+ public void securityMessageChanged() {
+ mShowingMessage = true;
+ update();
+ mHandler.removeCallbacks(mClearMessageRunnable);
+ if (mTimeout > 0) {
+ mHandler.postDelayed(mClearMessageRunnable, mTimeout);
+ }
+ announceForAccessibility(getText());
+ }
+
+ /**
+ * Update the status lines based on these rules:
+ * AlarmStatus: Alarm state always gets it's own line.
+ * Status1 is shared between help, battery status and generic unlock instructions,
+ * prioritized in that order.
+ * @param showStatusLines status lines are shown if true
+ */
+ void update() {
+ MutableInt icon = new MutableInt(0);
+ CharSequence status = concat(getChargeInfo(icon), getOwnerInfo(), getCurrentMessage());
+ setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
+ setText(status);
+ }
+
+
+ private CharSequence concat(Object... args) {
+ StringBuilder b = new StringBuilder();
+ for (int i = 0; i < args.length; i++) {
+ final Object arg = args[i];
+ if (arg instanceof CharSequence) {
+ b.append((CharSequence)args[i]);
+ b.append(SEPARATOR);
+ } else if (arg instanceof String) {
+ b.append((String)args[i]);
+ b.append(SEPARATOR);
+ }
+ }
+ return b.toString();
+ }
+
+
+ CharSequence getCurrentMessage() {
+ return mShowingMessage ? mMessage : null;
+ }
+
+ String getOwnerInfo() {
+ ContentResolver res = getContext().getContentResolver();
+ final boolean ownerInfoEnabled = Settings.Secure.getIntForUser(res,
+ Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
+ return ownerInfoEnabled && !mShowingMessage ?
+ Settings.Secure.getStringForUser(res, Settings.Secure.LOCK_SCREEN_OWNER_INFO,
+ UserHandle.USER_CURRENT) : null;
+ }
+
+ private CharSequence getChargeInfo(MutableInt icon) {
+ CharSequence string = null;
+ if (mShowingBatteryInfo && !mShowingMessage) {
+ // Battery status
+ if (mPluggedIn) {
+ // Charging, charged or waiting to charge.
+ string = getContext().getString(mBatteryCharged ?
+ com.android.internal.R.string.lockscreen_charged
+ :com.android.internal.R.string.lockscreen_plugged_in, mBatteryLevel);
+ icon.value = CHARGING_ICON;
+ } else if (mBatteryIsLow) {
+ // Battery is low
+ string = getContext().getString(
+ com.android.internal.R.string.lockscreen_low_battery);
+ icon.value = BATTERY_LOW_ICON;
+ }
+ }
+ return string;
+ }
+
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
index 3c972bc40d41..a21ebe38b2c7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserAvatar.java
@@ -18,15 +18,18 @@ package com.android.internal.policy.impl.keyguard;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Color;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -34,23 +37,38 @@ import android.widget.TextView;
import com.android.internal.R;
class KeyguardMultiUserAvatar extends FrameLayout {
+ private static final String TAG = KeyguardMultiUserAvatar.class.getSimpleName();
+ private static final boolean DEBUG = KeyguardHostView.DEBUG;
private ImageView mUserImage;
private TextView mUserName;
private UserInfo mUserInfo;
private static final float ACTIVE_ALPHA = 1.0f;
- private static final float INACTIVE_ALPHA = 0.5f;
- private static final float ACTIVE_SCALE = 1.2f;
- private static final float ACTIVE_TEXT_BACGROUND_ALPHA = 0.5f;
- private static final float INACTIVE_TEXT_BACGROUND_ALPHA = 0f;
- private static int mActiveTextColor;
- private static int mInactiveTextColor;
+ private static final float INACTIVE_ALPHA = 1.0f;
+ private static final float ACTIVE_SCALE = 1.5f;
+ private static final float ACTIVE_TEXT_ALPHA = 0f;
+ private static final float INACTIVE_TEXT_ALPHA = 0.5f;
+ private static final int SWITCH_ANIMATION_DURATION = 150;
+
+ private final float mActiveAlpha;
+ private final float mActiveScale;
+ private final float mActiveTextAlpha;
+ private final float mInactiveAlpha;
+ private final float mInactiveTextAlpha;
+ private final float mShadowRadius;
+ private final float mStroke;
+ private final float mIconSize;
+ private final int mFrameColor;
+ private final int mFrameShadowColor;
+ private final int mTextColor;
+ private final int mHighlightColor;
+
+ private boolean mTouched;
+
private boolean mActive;
private boolean mInit = true;
private KeyguardMultiUserSelectorView mUserSelector;
-
- boolean mPressedStateLocked = false;
- boolean mTempPressedStateHolder = false;
+ private KeyguardCircleFramedDrawable mFramed;
public static KeyguardMultiUserAvatar fromXml(int resId, Context context,
KeyguardMultiUserSelectorView userSelector, UserInfo info) {
@@ -73,8 +91,29 @@ class KeyguardMultiUserAvatar extends FrameLayout {
super(context, attrs, defStyle);
Resources res = mContext.getResources();
- mActiveTextColor = res.getColor(R.color.kg_multi_user_text_active);
- mInactiveTextColor = res.getColor(R.color.kg_multi_user_text_inactive);
+ mTextColor = res.getColor(R.color.keyguard_avatar_nick_color);
+ mIconSize = res.getDimension(R.dimen.keyguard_avatar_size);
+ mStroke = res.getDimension(R.dimen.keyguard_avatar_frame_stroke_width);
+ mShadowRadius = res.getDimension(R.dimen.keyguard_avatar_frame_shadow_radius);
+ mFrameColor = res.getColor(R.color.keyguard_avatar_frame_color);
+ mFrameShadowColor = res.getColor(R.color.keyguard_avatar_frame_shadow_color);
+ mHighlightColor = res.getColor(R.color.keyguard_avatar_frame_pressed_color);
+ mActiveTextAlpha = ACTIVE_TEXT_ALPHA;
+ mInactiveTextAlpha = INACTIVE_TEXT_ALPHA;
+ mActiveScale = ACTIVE_SCALE;
+ mActiveAlpha = ACTIVE_ALPHA;
+ mInactiveAlpha = INACTIVE_ALPHA;
+
+ mTouched = false;
+
+ setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+
+ protected String rewriteIconPath(String path) {
+ if (!this.getClass().getName().contains("internal")) {
+ return path.replace("system", "data");
+ }
+ return path;
}
public void init(UserInfo user, KeyguardMultiUserSelectorView userSelector) {
@@ -84,39 +123,52 @@ class KeyguardMultiUserAvatar extends FrameLayout {
mUserImage = (ImageView) findViewById(R.id.keyguard_user_avatar);
mUserName = (TextView) findViewById(R.id.keyguard_user_name);
- mUserImage.setImageDrawable(Drawable.createFromPath(mUserInfo.iconPath));
+ Bitmap icon = null;
+ try {
+ icon = BitmapFactory.decodeFile(rewriteIconPath(user.iconPath));
+ } catch (Exception e) {
+ if (DEBUG) Log.d(TAG, "failed to open profile icon " + user.iconPath, e);
+ }
+
+ if (icon == null) {
+ icon = BitmapFactory.decodeResource(mContext.getResources(),
+ com.android.internal.R.drawable.ic_contact_picture);
+ }
+
+ mFramed = new KeyguardCircleFramedDrawable(icon, (int) mIconSize, mFrameColor, mStroke,
+ mFrameShadowColor, mShadowRadius, mHighlightColor);
+ mUserImage.setImageDrawable(mFramed);
mUserName.setText(mUserInfo.name);
setOnClickListener(mUserSelector);
- setActive(false, false, 0, null);
mInit = false;
}
- public void setActive(boolean active, boolean animate, int duration, final Runnable onComplete) {
+ public void setActive(boolean active, boolean animate, final Runnable onComplete) {
if (mActive != active || mInit) {
mActive = active;
if (active) {
- KeyguardSubdivisionLayout parent = (KeyguardSubdivisionLayout) getParent();
- parent.setTopChild(parent.indexOfChild(this));
+ KeyguardLinearLayout parent = (KeyguardLinearLayout) getParent();
+ parent.setTopChild(this);
}
}
- updateVisualsForActive(mActive, animate, duration, true, onComplete);
+ updateVisualsForActive(mActive, animate, SWITCH_ANIMATION_DURATION, onComplete);
}
- void updateVisualsForActive(boolean active, boolean animate, int duration, boolean scale,
+ void updateVisualsForActive(boolean active, boolean animate, int duration,
final Runnable onComplete) {
- final float finalAlpha = active ? ACTIVE_ALPHA : INACTIVE_ALPHA;
- final float initAlpha = active ? INACTIVE_ALPHA : ACTIVE_ALPHA;
- final float finalScale = active && scale ? ACTIVE_SCALE : 1.0f;
- final float initScale = active ? 1.0f : ACTIVE_SCALE;
- final int finalTextBgAlpha = active ? (int) (ACTIVE_TEXT_BACGROUND_ALPHA * 255) :
- (int) (INACTIVE_TEXT_BACGROUND_ALPHA * 255);
- final int initTextBgAlpha = active ? (int) (INACTIVE_TEXT_BACGROUND_ALPHA * 255) :
- (int) (ACTIVE_TEXT_BACGROUND_ALPHA * 255);
- int textColor = active ? mActiveTextColor : mInactiveTextColor;
+ final float finalAlpha = active ? mActiveAlpha : mInactiveAlpha;
+ final float initAlpha = active ? mInactiveAlpha : mActiveAlpha;
+ final float finalScale = active ? 1f : 1f / mActiveScale;
+ final float initScale = mFramed.getScale();
+ final int finalTextAlpha = active ? (int) (mActiveTextAlpha * 255) :
+ (int) (mInactiveTextAlpha * 255);
+ final int initTextAlpha = active ? (int) (mInactiveTextAlpha * 255) :
+ (int) (mActiveTextAlpha * 255);
+ int textColor = mTextColor;
mUserName.setTextColor(textColor);
- if (animate) {
+ if (animate && mTouched) {
ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
va.addUpdateListener(new AnimatorUpdateListener() {
@Override
@@ -124,12 +176,11 @@ class KeyguardMultiUserAvatar extends FrameLayout {
float r = animation.getAnimatedFraction();
float scale = (1 - r) * initScale + r * finalScale;
float alpha = (1 - r) * initAlpha + r * finalAlpha;
- int textBgAlpha = (int) ((1 - r) * initTextBgAlpha + r * finalTextBgAlpha);
- setScaleX(scale);
- setScaleY(scale);
+ int textAlpha = (int) ((1 - r) * initTextAlpha + r * finalTextAlpha);
+ mFramed.setScale(scale);
mUserImage.setAlpha(alpha);
- mUserName.setBackgroundColor(Color.argb(textBgAlpha, 0, 0, 0));
- mUserSelector.invalidate();
+ mUserName.setTextColor(Color.argb(textAlpha, 255, 255, 255));
+ mUserImage.invalidate();
}
});
va.addListener(new AnimatorListenerAdapter() {
@@ -143,37 +194,23 @@ class KeyguardMultiUserAvatar extends FrameLayout {
va.setDuration(duration);
va.start();
} else {
- setScaleX(finalScale);
- setScaleY(finalScale);
+ mFramed.setScale(finalScale);
mUserImage.setAlpha(finalAlpha);
- mUserName.setBackgroundColor(Color.argb(finalTextBgAlpha, 0, 0, 0));
+ mUserName.setTextColor(Color.argb(finalTextAlpha, 255, 255, 255));
if (onComplete != null) {
post(onComplete);
}
}
- }
- public void lockPressedState() {
- mPressedStateLocked = true;
- }
-
- public void resetPressedState() {
- mPressedStateLocked = false;
- post(new Runnable() {
- @Override
- public void run() {
- KeyguardMultiUserAvatar.this.setPressed(mTempPressedStateHolder);
- }
- });
+ mTouched = true;
}
@Override
public void setPressed(boolean pressed) {
- if (!mPressedStateLocked) {
+ if (!pressed || isClickable()) {
super.setPressed(pressed);
- updateVisualsForActive(pressed || mActive, false, 0, mActive, null);
- } else {
- mTempPressedStateHolder = pressed;
+ mFramed.setPressed(pressed);
+ mUserImage.invalidate();
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
index 246c25587f83..728e87c2dd00 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMultiUserSelectorView.java
@@ -20,26 +20,26 @@ import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.RemoteException;
-import android.os.UserManager;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.MotionEvent;
import android.view.View;
-import android.view.WindowManagerGlobal;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.android.internal.R;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
public class KeyguardMultiUserSelectorView extends FrameLayout implements View.OnClickListener {
private static final String TAG = "KeyguardMultiUserSelectorView";
- private KeyguardSubdivisionLayout mUsersGrid;
+ private ViewGroup mUsersGrid;
private KeyguardMultiUserAvatar mActiveUserAvatar;
private KeyguardHostView.UserSwitcherCallback mCallback;
- private static final int SWITCH_ANIMATION_DURATION = 150;
private static final int FADE_OUT_ANIMATION_DURATION = 100;
public KeyguardMultiUserSelectorView(Context context) {
@@ -55,19 +55,18 @@ public class KeyguardMultiUserSelectorView extends FrameLayout implements View.O
}
protected void onFinishInflate () {
- init();
+ mUsersGrid = (ViewGroup) findViewById(R.id.keyguard_users_grid);
+ mUsersGrid.removeAllViews();
+ setClipChildren(false);
+ setClipToPadding(false);
+
}
public void setCallback(KeyguardHostView.UserSwitcherCallback callback) {
mCallback = callback;
}
- public void init() {
- mUsersGrid = (KeyguardSubdivisionLayout) findViewById(R.id.keyguard_users_grid);
- mUsersGrid.removeAllViews();
- setClipChildren(false);
- setClipToPadding(false);
-
+ public void addUsers(Collection<UserInfo> userList) {
UserInfo activeUser;
try {
activeUser = ActivityManagerNative.getDefault().getCurrentUser();
@@ -75,17 +74,18 @@ public class KeyguardMultiUserSelectorView extends FrameLayout implements View.O
activeUser = null;
}
- UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUm.getUsers(true));
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>(userList);
Collections.sort(users, mOrderAddedComparator);
for (UserInfo user: users) {
KeyguardMultiUserAvatar uv = createAndAddUser(user);
if (user.id == activeUser.id) {
mActiveUserAvatar = uv;
+ mActiveUserAvatar.setActive(true, false, null);
+ } else {
+ uv.setActive(false, false, null);
}
}
- mActiveUserAvatar.setActive(true, false, 0, null);
}
Comparator<UserInfo> mOrderAddedComparator = new Comparator<UserInfo>() {
@@ -103,27 +103,57 @@ public class KeyguardMultiUserSelectorView extends FrameLayout implements View.O
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if(event.getActionMasked() != MotionEvent.ACTION_CANCEL && mCallback != null) {
+ mCallback.userActivity();
+ }
+ return false;
+ }
+
+ private void setAllClickable(boolean clickable)
+ {
+ for(int i = 0; i < mUsersGrid.getChildCount(); i++) {
+ View v = mUsersGrid.getChildAt(i);
+ v.setClickable(clickable);
+ v.setPressed(false);
+ }
+ }
+
+ @Override
public void onClick(View v) {
if (!(v instanceof KeyguardMultiUserAvatar)) return;
final KeyguardMultiUserAvatar avatar = (KeyguardMultiUserAvatar) v;
- if (mActiveUserAvatar == avatar) {
- // If they click the currently active user, show the unlock hint
- mCallback.showUnlockHint();
- return;
- } else {
- // Reset the previously active user to appear inactive
- avatar.lockPressedState();
- mCallback.hideSecurityView(FADE_OUT_ANIMATION_DURATION);
- mActiveUserAvatar.setActive(false, true, SWITCH_ANIMATION_DURATION, new Runnable() {
- @Override
- public void run() {
- try {
- ActivityManagerNative.getDefault().switchUser(avatar.getUserInfo().id);
- } catch (RemoteException re) {
- Log.e(TAG, "Couldn't switch user " + re);
+ if (avatar.isClickable()) { // catch race conditions
+ if (mActiveUserAvatar == avatar) {
+ // If they click the currently active user, show the unlock hint
+ mCallback.showUnlockHint();
+ return;
+ } else {
+ // Reset the previously active user to appear inactive
+ mCallback.hideSecurityView(FADE_OUT_ANIMATION_DURATION);
+ setAllClickable(false);
+ mActiveUserAvatar.setActive(false, true, new Runnable() {
+ @Override
+ public void run() {
+ mActiveUserAvatar = avatar;
+ mActiveUserAvatar.setActive(true, true, new Runnable() {
+ @Override
+ public void run() {
+ if (this.getClass().getName().contains("internal")) {
+ try {
+ ActivityManagerNative.getDefault()
+ .switchUser(avatar.getUserInfo().id);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Couldn't switch user " + re);
+ }
+ } else {
+ setAllClickable(true);
+ }
+ }
+ });
}
- }
- });
+ });
+ }
}
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardNavigationManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardNavigationManager.java
deleted file mode 100644
index cec2668ff23e..000000000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardNavigationManager.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.policy.impl.keyguard;
-
-import android.widget.TextView;
-
-public class KeyguardNavigationManager implements SecurityMessageDisplay {
-
- private TextView mMessageArea;
-
- public KeyguardNavigationManager(TextView messageArea) {
- if (messageArea != null) {
- mMessageArea = messageArea;
- mMessageArea.setSelected(true); // Make marquee work
- }
- }
-
- public void setMessage(CharSequence msg, boolean important) {
- if (mMessageArea == null) return;
- mMessageArea.setText(msg);
- mMessageArea.announceForAccessibility(mMessageArea.getText());
- }
-
- public void setMessage(int resId, boolean important) {
- if (mMessageArea == null) return;
- if (resId != 0) {
- mMessageArea.setText(resId);
- mMessageArea.announceForAccessibility(mMessageArea.getText());
- } else {
- mMessageArea.setText("");
- }
- }
-
- public void setMessage(int resId, boolean important, Object... formatArgs) {
- if (mMessageArea == null) return;
- if (resId != 0) {
- mMessageArea.setText(mMessageArea.getContext().getString(resId, formatArgs));
- mMessageArea.announceForAccessibility(mMessageArea.getText());
- } else {
- mMessageArea.setText("");
- }
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java
new file mode 100644
index 000000000000..bea9aec13ed5
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.text.method.DigitsKeyListener;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.R;
+
+/**
+ * Displays a PIN pad for unlocking.
+ */
+public class KeyguardPINView extends KeyguardAbsKeyInputView
+ implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+
+ public KeyguardPINView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardPINView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ protected void resetState() {
+ mSecurityMessageDisplay.setMessage(R.string.kg_pin_instructions, false);
+ mPasswordEntry.setEnabled(true);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ final View ok = findViewById(R.id.key_enter);
+ if (ok != null) {
+ ok.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ doHapticKeyClick();
+ verifyPasswordAndUnlock();
+ }
+ });
+ }
+
+ // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
+ // not a separate view
+ View pinDelete = findViewById(R.id.delete_button);
+ if (pinDelete != null) {
+ pinDelete.setVisibility(View.VISIBLE);
+ pinDelete.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ CharSequence str = mPasswordEntry.getText();
+ if (str.length() > 0) {
+ mPasswordEntry.setText(str.subSequence(0, str.length()-1));
+ }
+ doHapticKeyClick();
+ }
+ });
+ pinDelete.setOnLongClickListener(new View.OnLongClickListener() {
+ public boolean onLongClick(View v) {
+ mPasswordEntry.setText("");
+ doHapticKeyClick();
+ return true;
+ }
+ });
+ }
+
+ mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
+ mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
+ | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
+
+ mPasswordEntry.requestFocus();
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
index be2dc8ffb28e..b6334f0c83cc 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
@@ -16,70 +16,35 @@
package com.android.internal.policy.impl.keyguard;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
-import java.util.List;
-
import android.app.admin.DevicePolicyManager;
+import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Rect;
-
-import com.android.internal.widget.PasswordEntryKeyboardView;
-
-import android.os.CountDownTimer;
-import android.os.SystemClock;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.text.method.DigitsKeyListener;
import android.text.method.TextKeyListener;
-import android.view.KeyEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.inputmethod.EditorInfo;
+import android.util.AttributeSet;
+import android.view.View;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
+import com.android.internal.R;
import com.android.internal.widget.PasswordEntryKeyboardHelper;
+import com.android.internal.widget.PasswordEntryKeyboardView;
+
+import java.util.List;
/**
- * Displays a dialer-like interface or alphanumeric (latin-1) key entry for the user to enter
+ * Displays an alphanumeric (latin-1) key entry for the user to enter
* an unlock password
*/
-public class KeyguardPasswordView extends LinearLayout
+public class KeyguardPasswordView extends KeyguardAbsKeyInputView
implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
- /** Delay in ms between updates to the "too many attempts" count down. */
- private static final long LOCKOUT_INTERVAL = 1000;
-
- /**
- * Delay in ms between updates to the "too many attempts" count down used
- * when accessibility is turned on. Less annoying than the shorter default
- * {@link #LOCKOUT_INTERVAL}.
- */
- private static final long ACCESSIBILITY_LOCKOUT_INTERVAL = 10000;
-
- private KeyguardSecurityCallback mCallback;
- private EditText mPasswordEntry;
- private LockPatternUtils mLockPatternUtils;
- private PasswordEntryKeyboardView mKeyboardView;
- private PasswordEntryKeyboardHelper mKeyboardHelper;
- private boolean mIsAlpha;
- private SecurityMessageDisplay mSecurityMessageDisplay;
- // To avoid accidental lockout due to events while the device in in the pocket, ignore
- // any passwords with length less than or equal to this length.
- private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
-
- // Enable this if we want to hide the on-screen PIN keyboard when a physical one is showing
- private static final boolean ENABLE_HIDE_KEYBOARD = false;
+ InputMethodManager mImm;
public KeyguardPasswordView(Context context) {
super(context);
@@ -89,110 +54,40 @@ public class KeyguardPasswordView extends LinearLayout
super(context, attrs);
}
- public void setKeyguardCallback(KeyguardSecurityCallback callback) {
- mCallback = callback;
- }
-
- public void setLockPatternUtils(LockPatternUtils utils) {
- mLockPatternUtils = utils;
+ protected void resetState() {
+ mSecurityMessageDisplay.setMessage(R.string.kg_password_instructions, false);
+ mPasswordEntry.setEnabled(true);
}
@Override
- public void onWindowFocusChanged(boolean hasWindowFocus) {
- if (hasWindowFocus) {
- reset();
- }
+ public boolean needsInput() {
+ return true;
}
- public void reset() {
- // start fresh
- mPasswordEntry.setText("");
- mPasswordEntry.requestFocus();
-
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
- if (deadline != 0) {
- handleAttemptLockout(deadline);
- } else {
- resetState();
- }
+ @Override
+ public void onResume() {
+ super.onResume();
+ mImm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
- private void resetState() {
- mSecurityMessageDisplay.setMessage(
- mIsAlpha ? R.string.kg_password_instructions : R.string.kg_pin_instructions, false);
- mPasswordEntry.setEnabled(true);
- mKeyboardView.setEnabled(true);
+ @Override
+ public void onPause() {
+ super.onPause();
+ mImm.hideSoftInputFromWindow(getWindowToken(), 0);
}
@Override
protected void onFinishInflate() {
- // We always set a dummy NavigationManager to avoid null checks
- mSecurityMessageDisplay = new KeyguardNavigationManager(null);
-
- mLockPatternUtils = new LockPatternUtils(mContext); // TODO: use common one
-
- final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality();
- mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality
- || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality
- || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == quality;
-
- mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
- mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
- mPasswordEntry.setOnEditorActionListener(this);
- mPasswordEntry.addTextChangedListener(this);
-
- mKeyboardHelper = new PasswordEntryKeyboardHelper(mContext, mKeyboardView, this, false,
- new int[] {
- R.xml.kg_password_kbd_numeric,
- com.android.internal.R.xml.password_kbd_qwerty,
- com.android.internal.R.xml.password_kbd_qwerty_shifted,
- com.android.internal.R.xml.password_kbd_symbols,
- com.android.internal.R.xml.password_kbd_symbols_shift
- }
- );
- mKeyboardHelper.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
+ super.onFinishInflate();
boolean imeOrDeleteButtonVisible = false;
- if (mIsAlpha) {
- // We always use the system IME for alpha keyboard, so hide lockscreen's soft keyboard
- mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
- mKeyboardView.setVisibility(View.GONE);
- } else {
- mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
-
- // Use lockscreen's numeric keyboard if the physical keyboard isn't showing
- boolean hardKeyboardVisible = getResources().getConfiguration().hardKeyboardHidden
- == Configuration.HARDKEYBOARDHIDDEN_NO;
- mKeyboardView.setVisibility(
- (ENABLE_HIDE_KEYBOARD && hardKeyboardVisible) ? View.INVISIBLE : View.VISIBLE);
-
- // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
- // not a separate view
- View pinDelete = findViewById(R.id.delete_button);
- if (pinDelete != null) {
- pinDelete.setVisibility(View.VISIBLE);
- imeOrDeleteButtonVisible = true;
- pinDelete.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mKeyboardHelper.handleBackspace();
- }
- });
- }
- }
- mPasswordEntry.requestFocus();
+ mImm = (InputMethodManager) getContext().getSystemService(
+ Context.INPUT_METHOD_SERVICE);
- // This allows keyboards with overlapping qwerty/numeric keys to choose just numeric keys.
- if (mIsAlpha) {
- mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
- | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- } else {
- mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER
- | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
- }
+ mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
+ mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD);
// Poke the wakelock any time the text is selected or modified
mPasswordEntry.setOnClickListener(new OnClickListener() {
@@ -215,17 +110,17 @@ public class KeyguardPasswordView extends LinearLayout
}
});
+ mPasswordEntry.requestFocus();
+
// If there's more than one IME, enable the IME switcher button
View switchImeButton = findViewById(R.id.switch_ime_button);
- final InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
- Context.INPUT_METHOD_SERVICE);
- if (mIsAlpha && switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(imm, false)) {
+ if (switchImeButton != null && hasMultipleEnabledIMEsOrSubtypes(mImm, false)) {
switchImeButton.setVisibility(View.VISIBLE);
imeOrDeleteButtonVisible = true;
switchImeButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mCallback.userActivity(0); // Leave the screen on a bit longer
- imm.showInputMethodPicker();
+ mImm.showInputMethodPicker();
}
});
}
@@ -291,113 +186,6 @@ public class KeyguardPasswordView extends LinearLayout
}
@Override
- protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
- // send focus to the password field
- return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
- }
-
- private void verifyPasswordAndUnlock() {
- String entry = mPasswordEntry.getText().toString();
- if (mLockPatternUtils.checkPassword(entry)) {
- mCallback.reportSuccessfulUnlockAttempt();
- mCallback.dismiss(true);
- } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
- // to avoid accidental lockout, only count attempts that are long enough to be a
- // real password. This may require some tweaking.
- mCallback.reportFailedUnlockAttempt();
- if (0 == (mCallback.getFailedAttempts()
- % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
- handleAttemptLockout(deadline);
- }
- mSecurityMessageDisplay.setMessage(
- mIsAlpha ? R.string.kg_wrong_password : R.string.kg_wrong_pin, true);
- }
- mPasswordEntry.setText("");
- }
-
- // Prevent user from using the PIN/Password entry until scheduled deadline.
- private void handleAttemptLockout(long elapsedRealtimeDeadline) {
- mPasswordEntry.setEnabled(false);
- mKeyboardView.setEnabled(false);
- long elapsedRealtime = SystemClock.elapsedRealtime();
- final AccessibilityManager accessManager =
- (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- // Use a longer update interval when accessibility is turned on.
- final long interval = accessManager.isEnabled() ? ACCESSIBILITY_LOCKOUT_INTERVAL
- : LOCKOUT_INTERVAL;
- new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, interval) {
-
- @Override
- public void onTick(long millisUntilFinished) {
- int secondsRemaining = (int) (millisUntilFinished / 1000);
- mSecurityMessageDisplay.setMessage(
- R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining);
- }
-
- @Override
- public void onFinish() {
- resetState();
- }
- }.start();
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- mCallback.userActivity(0);
- return false;
- }
-
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- // Check if this was the result of hitting the enter key
- if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
- || actionId == EditorInfo.IME_ACTION_NEXT) {
- verifyPasswordAndUnlock();
- return true;
- }
- return false;
- }
-
- @Override
- public boolean needsInput() {
- return mIsAlpha;
- }
-
- @Override
- public void onPause() {
-
- }
-
- @Override
- public void onResume() {
- reset();
- }
-
- @Override
- public KeyguardSecurityCallback getCallback() {
- return mCallback;
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- if (mCallback != null) {
- mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS);
- }
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- }
-
- @Override
- public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
- mSecurityMessageDisplay = display;
- reset();
+ public void showUsabilityHint() {
}
}
-
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
index dcf40bf966d0..6b3446add9a1 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPatternView.java
@@ -110,10 +110,6 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
@Override
protected void onFinishInflate() {
super.onFinishInflate();
-
- // We always set a dummy NavigationManager to avoid null checks
- mSecurityMessageDisplay = new KeyguardNavigationManager(null);
-
mLockPatternUtils = mLockPatternUtils == null
? new LockPatternUtils(mContext) : mLockPatternUtils;
@@ -139,6 +135,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
setFocusableInTouchMode(true);
maybeEnableFallback(mContext);
+ mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
}
private void updateFooter(FooterMode mode) {
@@ -200,6 +197,10 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
}
+ @Override
+ public void showUsabilityHint() {
+ }
+
/** TODO: hook this up */
public void cleanUp() {
if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
@@ -372,12 +373,6 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
public KeyguardSecurityCallback getCallback() {
return mCallback;
}
-
- @Override
- public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
- mSecurityMessageDisplay = display;
- reset();
- }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java
new file mode 100644
index 000000000000..f6a90c5941bc
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java
@@ -0,0 +1,20 @@
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+public class KeyguardSecurityContainer extends FrameLayout {
+
+ public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardSecurityContainer(Context context) {
+ this(null, null, 0);
+ }
+
+ public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
index 59e2ca93ba30..7a69586a91fa 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
@@ -31,7 +31,8 @@ public class KeyguardSecurityModel {
Invalid, // NULL state
None, // No security enabled
Pattern, // Unlock by drawing a pattern.
- Password, // Unlock by entering a password or PIN
+ Password, // Unlock by entering an alphanumeric password
+ PIN, // Strictly numeric password
Biometric, // Unlock with a biometric key (e.g. finger print or face unlock)
Account, // Unlock by entering an account's login and password.
SimPin, // Unlock by entering a sim pin.
@@ -85,6 +86,9 @@ public class KeyguardSecurityModel {
final int security = mLockPatternUtils.getKeyguardStoredPasswordQuality();
switch (security) {
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+ mode = mLockPatternUtils.isLockPasswordEnabled() ?
+ SecurityMode.PIN : SecurityMode.None;
+ break;
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
@@ -117,7 +121,9 @@ public class KeyguardSecurityModel {
*/
SecurityMode getAlternateFor(SecurityMode mode) {
if (isBiometricUnlockEnabled() && !isBiometricUnlockSuppressed()
- && (mode == SecurityMode.Password || mode == SecurityMode.Pattern)) {
+ && (mode == SecurityMode.Password
+ || mode == SecurityMode.PIN
+ || mode == SecurityMode.Pattern)) {
return SecurityMode.Biometric;
}
return mode; // no alternate, return what was given
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
index 19bcae9466d2..26517435d8f0 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityView.java
@@ -61,5 +61,9 @@ public interface KeyguardSecurityView {
*/
KeyguardSecurityCallback getCallback();
- void setSecurityMessageDisplay(SecurityMessageDisplay display);
+ /**
+ * Instruct the view to show usability hints, if any.
+ *
+ */
+ void showUsabilityHint();
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
index c4e1607f4cdd..58cf56767b0c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
@@ -21,14 +21,17 @@ import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowManager;
import android.widget.ViewFlipper;
+import com.android.internal.widget.LockPatternUtils;
+
/**
* Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so
* we can emulate {@link WindowManager.LayoutParams#FLAG_SLIPPERY} within a view hierarchy.
*
*/
-public class KeyguardSecurityViewFlipper extends ViewFlipper {
+public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView {
private Rect mTempRect = new Rect();
public KeyguardSecurityViewFlipper(Context context) {
@@ -55,4 +58,71 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper {
return result;
}
+ KeyguardSecurityView getSecurityView() {
+ View child = getChildAt(getDisplayedChild());
+ if (child instanceof KeyguardSecurityView) {
+ return (KeyguardSecurityView) child;
+ }
+ return null;
+ }
+
+ @Override
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.setKeyguardCallback(callback);
+ }
+ }
+
+ @Override
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.setLockPatternUtils(utils);
+ }
+ }
+
+ @Override
+ public void reset() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.reset();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.onPause();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.onResume();
+ }
+ }
+
+ @Override
+ public boolean needsInput() {
+ KeyguardSecurityView ksv = getSecurityView();
+ return (ksv != null) ? ksv.needsInput() : false;
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ KeyguardSecurityView ksv = getSecurityView();
+ return (ksv != null) ? ksv.getCallback() : null;
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.showUsabilityHint();
+ }
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
index 1d26def19693..938e3bd63aac 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
@@ -16,18 +16,12 @@
package com.android.internal.policy.impl.keyguard;
import android.animation.ObjectAnimator;
-import android.app.ActivityManagerNative;
import android.app.SearchManager;
import android.app.admin.DevicePolicyManager;
-import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.MediaStore;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
@@ -41,8 +35,6 @@ import com.android.internal.widget.multiwaveview.GlowPadView;
import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
import com.android.internal.R;
-import java.util.List;
-
public class KeyguardSelectorView extends LinearLayout implements KeyguardSecurityView {
private static final boolean DEBUG = KeyguardHostView.DEBUG;
private static final String TAG = "SecuritySelectorView";
@@ -67,7 +59,7 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
.getAssistIntent(mContext, UserHandle.USER_CURRENT);
if (assistIntent != null) {
- launchActivity(assistIntent, false);
+ mActivityLauncher.launchActivity(assistIntent, false, true);
} else {
Log.w(TAG, "Failed to get intent for assist activity");
}
@@ -75,7 +67,7 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
break;
case com.android.internal.R.drawable.ic_lockscreen_camera:
- launchCamera();
+ mActivityLauncher.launchCamera();
mCallback.userActivity(0);
break;
@@ -119,47 +111,25 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
}
};
- public KeyguardSelectorView(Context context) {
- this(context, null);
- }
+ private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
- private boolean wouldLaunchResolverActivity(Intent intent) {
- PackageManager packageManager = mContext.getPackageManager();
- ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
- PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser());
- final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
- intent, PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser());
- // If the list contains the above resolved activity, then it can't be
- // ResolverActivity itself.
- for (int i = 0; i < appList.size(); i++) {
- ResolveInfo tmp = appList.get(i);
- if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
- && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
- return false;
- }
+ @Override
+ KeyguardSecurityCallback getCallback() {
+ return mCallback;
}
- return true;
- }
- protected void launchCamera() {
- if (mLockPatternUtils.isSecure()) {
- // Launch the secure version of the camera
- final Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
- intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-
- if (wouldLaunchResolverActivity(intent)) {
- // TODO: Show disambiguation dialog instead.
- // For now, we'll treat this like launching any other app from secure keyguard.
- // When they do, user sees the system's ResolverActivity which lets them choose
- // which secure camera to use.
- launchActivity(intent, false);
- } else {
- launchActivity(intent, true);
- }
- } else {
- // Launch the normal camera
- launchActivity(new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA), false);
+ @Override
+ LockPatternUtils getLockPatternUtils() {
+ return mLockPatternUtils;
}
+
+ @Override
+ Context getContext() {
+ return mContext;
+ }};
+
+ public KeyguardSelectorView(Context context) {
+ this(context, null);
}
public KeyguardSelectorView(Context context, AttributeSet attrs) {
@@ -183,7 +153,8 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
return mGlowPadView.getTargetPosition(resId) != -1;
}
- public void ping() {
+ @Override
+ public void showUsabilityHint() {
mGlowPadView.ping();
}
@@ -266,42 +237,6 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
mLockPatternUtils = utils;
}
- /**
- * Launches the said intent for the current foreground user.
- * @param intent
- * @param showsWhileLocked true if the activity can be run on top of keyguard.
- * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
- */
- private void launchActivity(final Intent intent, boolean showsWhileLocked) {
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_SINGLE_TOP
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- boolean isSecure = mLockPatternUtils.isSecure();
- if (!isSecure || showsWhileLocked) {
- if (!isSecure) try {
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
- } catch (RemoteException e) {
- Log.w(TAG, "can't dismiss keyguard on launch");
- }
- try {
- mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, "Activity not found for intent + " + intent.getAction());
- }
- } else {
- // Create a runnable to start the activity and ask the user to enter their
- // credentials.
- mCallback.setOnDismissRunnable(new Runnable() {
- @Override
- public void run() {
- mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
- }
- });
- mCallback.dismiss(false);
- }
- }
-
@Override
public void reset() {
mGlowPadView.reset(false);
@@ -326,8 +261,4 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
public KeyguardSecurityCallback getCallback() {
return mCallback;
}
-
- @Override
- public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
- }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
index 7878e461313a..fcf45ff8a880 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
@@ -76,9 +76,6 @@ public class KeyguardSimPinView extends LinearLayout
protected void onFinishInflate() {
super.onFinishInflate();
- // We always set a dummy NavigationManager to avoid null checks
- mSecurityMessageDisplay = new KeyguardNavigationManager(null);
-
mPinEntry = (EditText) findViewById(R.id.sim_pin_entry);
mPinEntry.setOnEditorActionListener(this);
mPinEntry.addTextChangedListener(this);
@@ -103,6 +100,9 @@ public class KeyguardSimPinView extends LinearLayout
}
});
}
+
+ mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
+ mSecurityMessageDisplay.setTimeout(0);
reset();
}
@@ -121,6 +121,10 @@ public class KeyguardSimPinView extends LinearLayout
mPinEntry.requestFocus();
}
+ @Override
+ public void showUsabilityHint() {
+ }
+
/** {@inheritDoc} */
public void cleanUp() {
// dismiss the dialog.
@@ -265,9 +269,4 @@ public class KeyguardSimPinView extends LinearLayout
public void afterTextChanged(Editable s) {
}
- @Override
- public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
- mSecurityMessageDisplay = display;
- reset();
- }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
index 562d8931e2a3..04658050d9b4 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
@@ -91,7 +91,8 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList
} else if (state == CONFIRM_PIN) {
if (confirmPin()) {
state = DONE;
- msg = R.string.lockscreen_sim_unlock_progress_dialog_message;
+ msg =
+ com.android.internal.R.string.lockscreen_sim_unlock_progress_dialog_message;
updateSim();
} else {
msg = R.string.kg_invalid_confirm_pin_hint;
@@ -129,10 +130,6 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList
@Override
protected void onFinishInflate() {
super.onFinishInflate();
-
- // We always set a dummy NavigationManager to avoid null checks
- mSecurityMessageDisplay = new KeyguardNavigationManager(null);
-
mSimPinEntry = (TextView) findViewById(R.id.sim_pin_entry);
mSimPinEntry.setOnEditorActionListener(this);
mSimPinEntry.addTextChangedListener(this);
@@ -149,6 +146,9 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList
});
mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
mKeyboardHelper.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
+
+ mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
+ mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default
reset();
}
@@ -169,6 +169,10 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList
reset();
}
+ @Override
+ public void showUsabilityHint() {
+ }
+
/** {@inheritDoc} */
public void cleanUp() {
// dismiss the dialog.
@@ -333,10 +337,4 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList
@Override
public void afterTextChanged(Editable s) {
}
-
- @Override
- public void setSecurityMessageDisplay(SecurityMessageDisplay display) {
- mSecurityMessageDisplay = display;
- reset();
- }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
index 00cd5b9a0048..f2cb5221d0a8 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
@@ -20,6 +20,8 @@ import android.content.Context;
import android.util.AttributeSet;
import android.widget.GridLayout;
+import com.android.internal.widget.LockPatternUtils;
+
public class KeyguardStatusView extends GridLayout {
@SuppressWarnings("unused")
private KeyguardStatusViewManager mStatusViewManager;
@@ -36,6 +38,10 @@ public class KeyguardStatusView extends GridLayout {
super(context, attrs, defStyle);
}
+ public int getAppWidgetId() {
+ return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
index 5b85064fdabd..b4bd6e9e7a6c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
@@ -16,15 +16,6 @@
package com.android.internal.policy.impl.keyguard;
-import com.android.internal.R;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.widget.DigitalClock;
-import com.android.internal.widget.LockPatternUtils;
-
-import java.util.Date;
-
-import libcore.util.MutableInt;
-
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
@@ -39,6 +30,10 @@ import android.util.Log;
import android.view.View;
import android.widget.TextView;
+import com.android.internal.R;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.widget.LockPatternUtils;
+
import java.util.Date;
import libcore.util.MutableInt;
@@ -78,7 +73,7 @@ class KeyguardStatusViewManager implements SecurityMessageDisplay {
// Whether to use the last line as a combined line to either display owner info / charging.
// If false, each item will be given a dedicated space.
private boolean mShareStatusRegion = false;
-
+
// last known battery level
private int mBatteryLevel = 100;
@@ -121,9 +116,9 @@ class KeyguardStatusViewManager implements SecurityMessageDisplay {
if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()");
mContainer = view;
Resources res = getContext().getResources();
- mDateFormatString =
+ mDateFormatString =
res.getText(com.android.internal.R.string.abbrev_wday_month_day_no_year);
- mShareStatusRegion = res.getBoolean(R.bool.kg_share_status_area);
+ mShareStatusRegion = res.getBoolean(com.android.internal.R.bool.kg_share_status_area);
mLockPatternUtils = new LockPatternUtils(view.getContext());
mUpdateMonitor = KeyguardUpdateMonitor.getInstance(view.getContext());
@@ -331,4 +326,8 @@ class KeyguardStatusViewManager implements SecurityMessageDisplay {
return mContainer.getContext();
}
+ @Override
+ public void setTimeout(int timeout_ms) {
+ }
+
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java
deleted file mode 100644
index 1cd796c8a994..000000000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSubdivisionLayout.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * A layout that arranges its children into a special type of grid.
- */
-public class KeyguardSubdivisionLayout extends ViewGroup {
- ArrayList<BiTree> mCells = new ArrayList<BiTree>();
- int mNumChildren = -1;
- int mWidth = -1;
- int mHeight = -1;
- int mTopChild = 0;
-
- public KeyguardSubdivisionLayout(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardSubdivisionLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardSubdivisionLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- setClipChildren(false);
- setClipToPadding(false);
- setChildrenDrawingOrderEnabled(true);
- }
-
- private class BiTree {
- Rect rect;
- BiTree left;
- BiTree right;
- int nodeDepth;
- ArrayList<BiTree> leafs;
-
- public BiTree(Rect r) {
- rect = r;
- }
-
- public BiTree() {
- }
-
- boolean isLeaf() {
- return (left == null) && (right == null);
- }
-
- int depth() {
- if (left != null && right != null) {
- return Math.max(left.depth(), right.depth()) + 1;
- } else if (left != null) {
- return left.depth() + 1;
- } else if (right != null) {
- return right.depth() + 1;
- } else {
- return 1;
- }
- }
-
- int numLeafs() {
- if (left != null && right != null) {
- return left.numLeafs() + right.numLeafs();
- } else if (left != null) {
- return left.numLeafs();
- } else if (right != null) {
- return right.numLeafs();
- } else {
- return 1;
- }
- }
-
- BiTree getNextNodeToBranch() {
- if (leafs == null) {
- leafs = new ArrayList<BiTree>();
- }
- leafs.clear();
- getLeafs(leafs, 1);
-
- // If the tree is complete, then we start a new level at the rightmost side.
- double r = log2(leafs.size());
- if (Math.ceil(r) == Math.floor(r)) {
- return leafs.get(leafs.size() - 1);
- }
-
- // Tree is not complete, find the first leaf who's depth is less than the depth of
- // the tree.
- int treeDepth = depth();
- for (int i = leafs.size() - 1; i >= 0; i--) {
- BiTree n = leafs.get(i);
- if (n.nodeDepth < treeDepth) {
- return n;
- }
- }
- return null;
- }
-
- // Gets leafs in left to right order
- void getLeafs(ArrayList<BiTree> leafs, int depth) {
- if (isLeaf()) {
- this.nodeDepth = depth;
- leafs.add(this);
- } else {
- if (left != null) {
- left.getLeafs(leafs, depth + 1);
- }
- if (right != null) {
- right.getLeafs(leafs, depth + 1);
- }
- }
- }
- }
-
- double log2(double d) {
- return Math.log(d) / Math.log(2);
- }
-
- private void addCell(BiTree tree) {
- BiTree branch = tree.getNextNodeToBranch();
- Rect r = branch.rect;
- branch.left = new BiTree();
- branch.right = new BiTree();
- int newDepth = tree.depth();
-
- // For each level of the tree, we alternate between horizontal and vertical division
- if (newDepth % 2 == 0) {
- // Divide the cell vertically
- branch.left.rect = new Rect(r.left, r.top, r.right, r.top + r.height() / 2);
- branch.right.rect = new Rect(r.left, r.top + r.height() / 2, r.right, r.bottom);
- } else {
- // Divide the cell horizontally
- branch.left.rect = new Rect(r.left, r.top, r.left + r.width() / 2, r.bottom);
- branch.right.rect = new Rect(r.left + r.width() / 2, r.top, r.right, r.bottom);
- }
- }
-
- private void constructGrid(int width, int height, int numChildren) {
- mCells.clear();
- BiTree root = new BiTree(new Rect(0, 0, width, height));
-
- // We add nodes systematically until the number of leafs matches the number of children
- while (root.numLeafs() < numChildren) {
- addCell(root);
- }
-
- // Spit out the final list of cells
- root.getLeafs(mCells, 1);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
- int childCount = getChildCount();
-
- if (mNumChildren != childCount || width != getMeasuredWidth() ||
- height != getMeasuredHeight()) {
- constructGrid(width, height, childCount);
- }
-
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- Rect rect = mCells.get(i).rect;
- child.measure(MeasureSpec.makeMeasureSpec(rect.width(), MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(rect.height(), MeasureSpec.EXACTLY));
- }
- setMeasuredDimension(width, height);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = getChildAt(i);
- Rect rect = mCells.get(i).rect;
- child.layout(rect.left, rect.top, rect.right, rect.bottom);
- }
- }
-
- public void setTopChild(int top) {
- mTopChild = top;
- invalidate();
- }
-
- protected int getChildDrawingOrder(int childCount, int i) {
- int ret = i;
- if (i == childCount - 1) {
- ret = mTopChild;
- } else if (i >= mTopChild){
- ret = i + 1;
- }
- return ret;
- }
-} \ No newline at end of file
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
index 6a3c7c1dfb33..89f220a0cf2d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
@@ -16,17 +16,15 @@
package com.android.internal.policy.impl.keyguard;
-import java.lang.ref.WeakReference;
-
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.AudioManager;
+import android.media.IRemoteControlDisplay;
import android.media.MediaMetadataRetriever;
import android.media.RemoteControlClient;
-import android.media.IRemoteControlDisplay;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -42,11 +40,12 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.R;
+
+import java.lang.ref.WeakReference;
/**
* This is the widget responsible for showing music controls in keyguard.
*/
@@ -264,7 +263,7 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight()));
+// int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight()));
// Log.v(TAG, "setting max bitmap size: " + dim + "x" + dim);
// mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim);
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index d8e1c1a94ed4..316825a241c8 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -80,6 +80,7 @@ public class KeyguardUpdateMonitor {
private static final int MSG_DPM_STATE_CHANGED = 309;
private static final int MSG_USER_SWITCHED = 310;
private static final int MSG_USER_REMOVED = 311;
+ private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312;
private static KeyguardUpdateMonitor sInstance;
@@ -147,6 +148,10 @@ public class KeyguardUpdateMonitor {
case MSG_USER_REMOVED:
handleUserRemoved(msg.arg1);
break;
+ case MSG_KEYGUARD_VISIBILITY_CHANGED:
+ handleKeyguardVisibilityChanged(msg.arg1);
+ break;
+
}
}
};
@@ -557,6 +562,19 @@ public class KeyguardUpdateMonitor {
}
}
+ /**
+ * Handle {@link #MSG_KEYGUARD_VISIBILITY_CHANGED}
+ */
+ private void handleKeyguardVisibilityChanged(int showing) {
+ if (DEBUG) Log.d(TAG, "handleKeyguardVisibilityChanged(" + showing + ")");
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onKeyguardVisibilityChanged(showing == 1);
+ }
+ }
+ }
+
private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
final boolean nowPluggedIn = current.isPluggedIn();
final boolean wasPluggedIn = old.isPluggedIn();
@@ -659,6 +677,13 @@ public class KeyguardUpdateMonitor {
callback.onSimStateChanged(mSimState);
}
+ public void sendKeyguardVisibilityChanged(boolean showing) {
+ if (DEBUG) Log.d(TAG, "sendKeyguardVisibilityChanged(" + showing + ")");
+ Message message = mHandler.obtainMessage(MSG_KEYGUARD_VISIBILITY_CHANGED);
+ message.arg1 = showing ? 1 : 0;
+ message.sendToTarget();
+ }
+
public void reportClockVisible(boolean visible) {
mClockVisible = visible;
mHandler.obtainMessage(MSG_CLOCK_VISIBILITY_CHANGED).sendToTarget();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
index 3d65e6817239..8c9ac8ba5126 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
@@ -62,6 +62,12 @@ class KeyguardUpdateMonitorCallback {
void onPhoneStateChanged(int phoneState) { }
/**
+ * Called when the visibility of the keyguard changes.
+ * @param showing Indicates if the keyguard is now visible.
+ */
+ void onKeyguardVisibilityChanged(boolean showing) { }
+
+ /**
* Called when visibility of lockscreen clock changes, such as when
* obscured by a widget.
*/
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
index 3191f4a1df24..9e3424df06e8 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
@@ -16,6 +16,7 @@
package com.android.internal.policy.impl.keyguard;
+import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
@@ -27,11 +28,11 @@ import android.media.IAudioService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.TelephonyManager;
-import android.view.KeyEvent;
-import android.widget.LinearLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
+import android.view.KeyEvent;
+import android.widget.FrameLayout;
/**
* Base class for keyguard view. {@link #reset} is where you should
@@ -42,7 +43,7 @@ import android.util.Slog;
* Handles intercepting of media keys that still work when the keyguard is
* showing.
*/
-public abstract class KeyguardViewBase extends LinearLayout {
+public abstract class KeyguardViewBase extends FrameLayout {
private static final int BACKGROUND_COLOR = 0x70000000;
private AudioManager mAudioManager;
@@ -249,7 +250,10 @@ public abstract class KeyguardViewBase extends LinearLayout {
@Override
public void dispatchSystemUiVisibilityChanged(int visibility) {
super.dispatchSystemUiVisibilityChanged(visibility);
- setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
+
+ if (!(mContext instanceof Activity)) {
+ setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
+ }
}
public void setViewMediatorCallback(
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index b66c8833b0b7..b224c0898f4d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -48,7 +48,7 @@ import com.android.internal.widget.LockPatternUtils;
* reported to this class by the current {@link KeyguardViewBase}.
*/
public class KeyguardViewManager {
- private final static boolean DEBUG = false;
+ private final static boolean DEBUG = KeyguardViewMediator.DEBUG;
private static String TAG = "KeyguardViewManager";
public static boolean USE_UPPER_CASE = true;
@@ -96,7 +96,7 @@ public class KeyguardViewManager {
boolean enableScreenRotation = shouldEnableScreenRotation();
- maybeCreateKeyguardLocked(enableScreenRotation, options);
+ maybeCreateKeyguardLocked(enableScreenRotation, false, options);
maybeEnableScreenRotation(enableScreenRotation);
// Disable common aspects of the system/status/navigation bars that are not appropriate or
@@ -104,7 +104,7 @@ public class KeyguardViewManager {
// activities. Other disabled bits are handled by the KeyguardViewMediator talking
// directly to the status bar service.
final int visFlags = View.STATUS_BAR_DISABLE_HOME;
- if (DEBUG) Log.v(TAG, "KGVM: Set visibility on " + mKeyguardHost + " to " + visFlags);
+ if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");
mKeyguardHost.setSystemUiVisibility(visFlags);
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
@@ -127,7 +127,12 @@ public class KeyguardViewManager {
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- maybeCreateKeyguardLocked(shouldEnableScreenRotation(), null);
+ if (mKeyguardHost.getVisibility() == View.VISIBLE) {
+ // only propagate configuration messages if we're currently showing
+ maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null);
+ } else {
+ if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible");
+ }
}
@Override
@@ -145,7 +150,8 @@ public class KeyguardViewManager {
SparseArray<Parcelable> mStateContainer = new SparseArray<Parcelable>();
- private void maybeCreateKeyguardLocked(boolean enableScreenRotation, Bundle options) {
+ private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,
+ Bundle options) {
final boolean isActivity = (mContext instanceof Activity); // for test activity
if (mKeyguardHost != null) {
@@ -189,7 +195,9 @@ public class KeyguardViewManager {
mViewManager.addView(mKeyguardHost, lp);
}
- inflateKeyguardView(options);
+ if (force || mKeyguardView == null) {
+ inflateKeyguardView(options);
+ }
updateUserActivityTimeoutInWindowLayoutParams();
mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
@@ -230,11 +238,6 @@ public class KeyguardViewManager {
mKeyguardView.showNextSecurityScreenIfPresent();
}
}
-
- if (mScreenOn) {
- mKeyguardView.show();
- mKeyguardView.requestFocus();
- }
}
public void updateUserActivityTimeout() {
@@ -295,7 +298,7 @@ public class KeyguardViewManager {
if (DEBUG) Log.d(TAG, "reset()");
// User might have switched, check if we need to go back to keyguard
// TODO: It's preferable to stay and show the correct lockscreen or unlock if none
- maybeCreateKeyguardLocked(shouldEnableScreenRotation(), options);
+ maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, options);
}
public synchronized void onScreenTurnedOff() {
@@ -398,6 +401,15 @@ public class KeyguardViewManager {
}
/**
+ * Dismisses the keyguard by going to the next screen or making it gone.
+ */
+ public synchronized void dismiss() {
+ if (mScreenOn) {
+ mKeyguardView.dismiss();
+ }
+ }
+
+ /**
* @return Whether the keyguard is showing
*/
public synchronized boolean isShowing() {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index cb70922e01ba..53cbb39fd832 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -46,7 +46,6 @@ import android.util.EventLog;
import android.util.Log;
import android.view.KeyEvent;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicy;
import com.android.internal.telephony.IccCardConstants;
@@ -96,7 +95,7 @@ import com.android.internal.widget.LockPatternUtils;
*/
public class KeyguardViewMediator {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
- private final static boolean DEBUG = false;
+ final static boolean DEBUG = false;
private final static boolean DBG_WAKE = false;
private final static String TAG = "KeyguardViewMediator";
@@ -520,11 +519,11 @@ public class KeyguardViewMediator {
if (DEBUG) Log.d(TAG, "onSystemReady");
mSystemReady = true;
mUpdateMonitor.registerCallback(mUpdateCallback);
-
+
// Disable alternate unlock right after boot until things have settled.
mUpdateMonitor.setAlternateUnlockEnabled(false);
mUpdateMonitor.setIsFirstBoot(true);
-
+
doKeyguardLocked();
}
// Most services aren't available until the system reaches the ready state, so we
@@ -629,7 +628,9 @@ public class KeyguardViewMediator {
mScreenOn = true;
cancelDoKeyguardLaterLocked();
if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence);
- notifyScreenOnLocked(showListener);
+ if (showListener != null) {
+ notifyScreenOnLocked(showListener);
+ }
}
maybeSendUserPresentBroadcast();
}
@@ -769,6 +770,7 @@ public class KeyguardViewMediator {
*/
public void setHidden(boolean isHidden) {
if (DEBUG) Log.d(TAG, "setHidden " + isHidden);
+ mUpdateMonitor.sendKeyguardVisibilityChanged(!isHidden);
mHandler.removeMessages(SET_HIDDEN);
Message msg = mHandler.obtainMessage(SET_HIDDEN, (isHidden ? 1 : 0), 0);
mHandler.sendMessage(msg);
@@ -811,9 +813,7 @@ public class KeyguardViewMediator {
}
/**
- * Enable the keyguard if the settings are appropriate. Return true if all
- * work that will happen is done; returns false if the caller can wait for
- * the keyguard to be shown.
+ * Enable the keyguard if the settings are appropriate.
*/
private void doKeyguardLocked(Bundle options) {
// if another app is disabling us, don't show
@@ -865,6 +865,13 @@ public class KeyguardViewMediator {
}
/**
+ * Dismiss the keyguard through the security layers.
+ */
+ public void dismiss() {
+ mKeyguardViewManager.dismiss();
+ }
+
+ /**
* Send message to keyguard telling it to reset its state.
* @param options options about how to show the keyguard
* @see #handleReset()
@@ -1306,13 +1313,6 @@ public class KeyguardViewMediator {
// (like recents). Temporary enable/disable (e.g. the "back" button) are
// done in KeyguardHostView.
flags |= StatusBarManager.DISABLE_RECENT;
- if (!mScreenOn) {
- // Disable all navbar buttons on screen off. The navigation bar will hide
- // these immediately to avoid seeing the end of layout transition animations
- // if quickly turning back on.
- flags |= StatusBarManager.DISABLE_HOME;
- flags |= StatusBarManager.DISABLE_BACK;
- }
if (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
// showing secure lockscreen; disable expanding.
flags |= StatusBarManager.DISABLE_EXPAND;
@@ -1328,7 +1328,9 @@ public class KeyguardViewMediator {
+ " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags));
}
- mStatusBarManager.disable(flags);
+ if (!(mContext instanceof Activity)) {
+ mStatusBarManager.disable(flags);
+ }
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
new file mode 100644
index 000000000000..85245ba439b1
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+
+public class KeyguardViewStateManager implements SlidingChallengeLayout.OnChallengeScrolledListener {
+
+ private KeyguardWidgetPager mPagedView;
+ private ChallengeLayout mChallengeLayout;
+ private Runnable mHideHintsRunnable;
+ private int[] mTmpPoint = new int[2];
+ private int[] mTmpLoc = new int[2];
+
+ private KeyguardSecurityView mKeyguardSecurityContainer;
+ private static final int SCREEN_ON_HINT_DURATION = 1000;
+ private static final int SCREEN_ON_RING_HINT_DELAY = 300;
+ Handler mMainQueue = new Handler(Looper.myLooper());
+
+ int mLastScrollState = SlidingChallengeLayout.SCROLL_STATE_IDLE;
+
+ // Paged view state
+ private int mPageListeningToSlider = -1;
+ private int mCurrentPage = -1;
+
+ int mChallengeTop = 0;
+
+ public KeyguardViewStateManager() {
+ }
+
+ public void setPagedView(KeyguardWidgetPager pagedView) {
+ mPagedView = pagedView;
+ }
+
+ public void setChallengeLayout(ChallengeLayout layout) {
+ mChallengeLayout = layout;
+ }
+
+ public boolean isChallengeShowing() {
+ if (mChallengeLayout != null) {
+ return mChallengeLayout.isChallengeShowing();
+ }
+ return false;
+ }
+
+ public void setSecurityViewContainer(KeyguardSecurityView container) {
+ mKeyguardSecurityContainer = container;
+ }
+
+ public void onPageBeginMoving() {
+ if (mChallengeLayout.isChallengeShowing()) {
+ mChallengeLayout.showChallenge(false);
+ }
+ if (mHideHintsRunnable != null) {
+ mMainQueue.removeCallbacks(mHideHintsRunnable);
+ mHideHintsRunnable = null;
+ }
+ }
+
+ public void onPageEndMoving() {
+ }
+
+ public void showBouncer(boolean show) {
+ mChallengeLayout.showBouncer();
+ }
+
+ public void onPageSwitch(View newPage, int newPageIndex) {
+ // Reset the previous page size and ensure the current page is sized appropriately.
+ // We only modify the page state if it is not currently under control by the slider.
+ // This prevents conflicts.
+ if (mPagedView != null && mChallengeLayout != null) {
+ KeyguardWidgetFrame prevPage = mPagedView.getWidgetPageAt(mCurrentPage);
+ if (prevPage != null && mCurrentPage != mPageListeningToSlider) {
+ prevPage.resetSize();
+ }
+
+ KeyguardWidgetFrame newCurPage = mPagedView.getWidgetPageAt(newPageIndex);
+ boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
+ if (challengeOverlapping && !newCurPage.isSmall()
+ && mPageListeningToSlider != newPageIndex) {
+ newCurPage.shrinkWidget();
+ }
+ }
+ mCurrentPage = newPageIndex;
+ }
+
+ private int getChallengeTopRelativeToFrame(KeyguardWidgetFrame frame, int top) {
+ mTmpPoint[0] = 0;
+ mTmpPoint[1] = top;
+ mapPoint((View) mChallengeLayout, frame, mTmpPoint);
+ return mTmpPoint[1];
+ }
+
+ /**
+ * Simple method to map a point from one view's coordinates to another's. Note: this method
+ * doesn't account for transforms, so if the views will be transformed, this should not be used.
+ *
+ * @param fromView The view to which the point is relative
+ * @param toView The view into which the point should be mapped
+ * @param pt The point
+ */
+ private void mapPoint(View fromView, View toView, int pt[]) {
+ fromView.getLocationInWindow(mTmpLoc);
+
+ int x = mTmpLoc[0];
+ int y = mTmpLoc[1];
+
+ toView.getLocationInWindow(mTmpLoc);
+ int vX = mTmpLoc[0];
+ int vY = mTmpLoc[1];
+
+ pt[0] += x - vX;
+ pt[1] += y - vY;
+ }
+
+ @Override
+ public void onScrollStateChanged(int scrollState) {
+ if (mPagedView == null || mChallengeLayout == null) return;
+ boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping();
+
+ if (scrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
+ KeyguardWidgetFrame frame = mPagedView.getWidgetPageAt(mPageListeningToSlider);
+ if (frame == null) return;
+
+ if (!challengeOverlapping) {
+ frame.resetSize();
+ }
+ frame.onChallengeActive(mChallengeLayout.isChallengeShowing());
+ frame.hideFrame(this);
+
+ if (challengeOverlapping) {
+ mPagedView.setOnlyAllowEdgeSwipes(true);
+ } else {
+ mPagedView.setOnlyAllowEdgeSwipes(false);
+ }
+
+ if (mChallengeLayout.isChallengeShowing()) {
+ mKeyguardSecurityContainer.onResume();
+ } else {
+ mKeyguardSecurityContainer.onPause();
+ }
+ mPageListeningToSlider = -1;
+ } else if (mLastScrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) {
+ // Whether dragging or settling, if the last state was idle, we use this signal
+ // to update the current page who will receive events from the sliding challenge.
+ // We resize the frame as appropriate.
+ mPageListeningToSlider = mPagedView.getNextPage();
+ KeyguardWidgetFrame frame = mPagedView.getWidgetPageAt(mPageListeningToSlider);
+ if (frame == null) return;
+
+ frame.showFrame(this);
+
+ // As soon as the security begins sliding, the widget becomes small (if it wasn't
+ // small to begin with).
+ if (!frame.isSmall()) {
+ // We need to fetch the final page, in case the pages are in motion.
+ mPageListeningToSlider = mPagedView.getNextPage();
+ frame.shrinkWidget();
+ }
+ // View is on the move. Pause the security view until it completes.
+ mKeyguardSecurityContainer.onPause();
+
+ frame.onChallengeActive(true);
+ }
+ mLastScrollState = scrollState;
+ }
+
+ @Override
+ public void onScrollPositionChanged(float scrollPosition, int challengeTop) {
+ mChallengeTop = challengeTop;
+ KeyguardWidgetFrame frame = mPagedView.getWidgetPageAt(mPageListeningToSlider);
+ if (frame != null) {
+ frame.adjustFrame(getChallengeTopRelativeToFrame(frame, mChallengeTop));
+ }
+ }
+
+ public void showUsabilityHints() {
+ mMainQueue.postDelayed( new Runnable() {
+ @Override
+ public void run() {
+ mKeyguardSecurityContainer.showUsabilityHint();
+ }
+ } , SCREEN_ON_RING_HINT_DELAY);
+ mPagedView.showInitialPageHints();
+ mHideHintsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mPagedView.hideOutlinesAndSidePages();
+ mHideHintsRunnable = null;
+ }
+ };
+
+ mMainQueue.postDelayed(mHideHintsRunnable, SCREEN_ON_HINT_DURATION);
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java
new file mode 100644
index 000000000000..cf16ef2a715d
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.internal.R;
+
+public class KeyguardWidgetCarousel extends KeyguardWidgetPager {
+
+ private float mAdjacentPagesAngle;
+ private static float MAX_SCROLL_PROGRESS = 1.3f;
+ private static float CAMERA_DISTANCE = 10000;
+
+ public KeyguardWidgetCarousel(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardWidgetCarousel(Context context) {
+ this(context, null, 0);
+ }
+
+ public KeyguardWidgetCarousel(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mAdjacentPagesAngle = context.getResources().getInteger(R.integer.kg_carousel_angle);
+ }
+
+ protected float getMaxScrollProgress() {
+ return MAX_SCROLL_PROGRESS;
+ }
+
+ public float getAlphaForPage(int screenCenter, int index) {
+ View child = getChildAt(index);
+ if (child == null) return 0f;
+
+ float scrollProgress = getScrollProgress(screenCenter, child, index);
+ if (!isOverScrollChild(index, scrollProgress)) {
+ scrollProgress = getBoundedScrollProgress(screenCenter, child, index);
+ float alpha = 1 - Math.abs(scrollProgress / MAX_SCROLL_PROGRESS);
+ return alpha;
+ } else {
+ return 1f;
+ }
+ }
+
+ private void updatePageAlphaValues(int screenCenter) {
+ if (mChildrenOutlineFadeAnimation != null) {
+ mChildrenOutlineFadeAnimation.cancel();
+ mChildrenOutlineFadeAnimation = null;
+ }
+ if (!isReordering(false)) {
+ for (int i = 0; i < getChildCount(); i++) {
+ KeyguardWidgetFrame child = getWidgetPageAt(i);
+ if (child != null) {
+ float alpha = getAlphaForPage(screenCenter, i);
+ child.setBackgroundAlpha(alpha);
+ child.setContentAlpha(alpha);
+ }
+ }
+ }
+
+ }
+
+ @Override
+ protected void screenScrolled(int screenCenter) {
+ mScreenCenter = screenCenter;
+ updatePageAlphaValues(screenCenter);
+ for (int i = 0; i < getChildCount(); i++) {
+ KeyguardWidgetFrame v = getWidgetPageAt(i);
+ float scrollProgress = getScrollProgress(screenCenter, v, i);
+ if (v == mDragView || v == null) continue;
+ v.setCameraDistance(CAMERA_DISTANCE);
+
+ if (isOverScrollChild(i, scrollProgress)) {
+ v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress);
+ v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0);
+ } else {
+ scrollProgress = getBoundedScrollProgress(screenCenter, v, i);
+ int width = v.getMeasuredWidth();
+ float pivotX = (width / 2f) + scrollProgress * (width / 2f);
+ float pivotY = v.getMeasuredHeight() / 2;
+ float rotationY = - mAdjacentPagesAngle * scrollProgress;
+ v.setPivotX(pivotX);
+ v.setPivotY(pivotY);
+ v.setRotationY(rotationY);
+ v.setOverScrollAmount(0f, false);
+ }
+
+ float alpha = v.getAlpha();
+ // If the view has 0 alpha, we set it to be invisible so as to prevent
+ // it from accepting touches
+ if (alpha == 0) {
+ v.setVisibility(INVISIBLE);
+ } else if (v.getVisibility() != VISIBLE) {
+ v.setVisibility(VISIBLE);
+ }
+ }
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
index 311eec68bc59..e9c90a77b55b 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
@@ -16,20 +16,23 @@
package com.android.internal.policy.impl.keyguard;
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Shader;
-import android.os.PowerManager;
-import android.os.SystemClock;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.view.View;
import android.widget.FrameLayout;
import com.android.internal.R;
@@ -38,6 +41,8 @@ public class KeyguardWidgetFrame extends FrameLayout {
private final static PorterDuffXfermode sAddBlendMode =
new PorterDuffXfermode(PorterDuff.Mode.ADD);
+ static final float OUTLINE_ALPHA_MULTIPLIER = 0.6f;
+
private int mGradientColor;
private LinearGradient mForegroundGradient;
private LinearGradient mLeftToRightGradient;
@@ -48,8 +53,21 @@ public class KeyguardWidgetFrame extends FrameLayout {
private float mOverScrollAmount = 0f;
private final Rect mForegroundRect = new Rect();
private int mForegroundAlpha = 0;
- private PowerManager mPowerManager;
- private boolean mDisableInteraction;
+ private CheckLongPressHelper mLongPressHelper;
+ private Animator mFrameFade;
+ private boolean mIsSmall = false;
+
+ private float mBackgroundAlpha;
+ private float mContentAlpha;
+ private float mBackgroundAlphaMultiplier = 1.0f;
+ private Drawable mBackgroundDrawable;
+ private Rect mBackgroundRect = new Rect();
+ private static int mSmallWidgetHeight;
+
+ // Multiple callers may try and adjust the alpha of the frame. When a caller shows
+ // the outlines, we give that caller control, and nobody else can fade them out.
+ // This prevents animation conflicts.
+ private Object mBgAlphaController;
public KeyguardWidgetFrame(Context context) {
this(context, null, 0);
@@ -62,43 +80,271 @@ public class KeyguardWidgetFrame extends FrameLayout {
public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mLongPressHelper = new CheckLongPressHelper(this);
Resources res = context.getResources();
- int hPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_horizontal_padding);
- int topPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_top_padding);
- int bottomPadding = res.getDimensionPixelSize(R.dimen.kg_widget_pager_bottom_padding);
- setPadding(hPadding, topPadding, hPadding, bottomPadding);
+ // TODO: this padding should really correspond to the padding embedded in the background
+ // drawable (ie. outlines).
+ int padding = (int) (res.getDisplayMetrics().density * 8);
+ setPadding(padding, padding, padding, padding);
+
+ mBackgroundDrawable = res.getDrawable(R.drawable.kg_bouncer_bg_white);
mGradientColor = res.getColor(com.android.internal.R.color.kg_widget_pager_gradient);
mGradientPaint.setXfermode(sAddBlendMode);
}
- public void setDisableUserInteraction(boolean disabled) {
- mDisableInteraction = disabled;
+ @Override
+ protected void onDetachedFromWindow() {
+ cancelLongPress();
+ }
+
+ public void setMaxChallengeTop(int top) {
+ mSmallWidgetHeight = top - getPaddingTop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (!mDisableInteraction) {
- mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
- return super.onInterceptTouchEvent(ev);
+ // Watch for longpress events at this level to make sure
+ // users can always pick up this widget
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mLongPressHelper.postCheckForLongPress(ev);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mLongPressHelper.onMove(ev);
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mLongPressHelper.cancelLongPress();
+ break;
+ }
+
+ // Otherwise continue letting touch events fall through to children
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // Watch for longpress events at this level to make sure
+ // users can always pick up this widget
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_MOVE:
+ mLongPressHelper.onMove(ev);
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mLongPressHelper.cancelLongPress();
+ break;
}
+
+ // We return true here to ensure that we will get cancel / up signal
+ // even if none of our children have requested touch.
return true;
}
@Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- drawGradientOverlay(canvas);
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ cancelLongPress();
+ }
+ @Override
+ public void cancelLongPress() {
+ super.cancelLongPress();
+ mLongPressHelper.cancelLongPress();
}
+
private void drawGradientOverlay(Canvas c) {
mGradientPaint.setShader(mForegroundGradient);
mGradientPaint.setAlpha(mForegroundAlpha);
c.drawRect(mForegroundRect, mGradientPaint);
}
+ protected void drawBg(Canvas canvas) {
+ if (mBackgroundAlpha > 0.0f) {
+ Drawable bg = mBackgroundDrawable;
+
+ bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255));
+ bg.setBounds(mBackgroundRect);
+ bg.draw(canvas);
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ drawBg(canvas);
+ super.dispatchDraw(canvas);
+ drawGradientOverlay(canvas);
+ }
+
+ /**
+ * Because this view has fading outlines, it is essential that we enable hardware
+ * layers on the content (child) so that updating the alpha of the outlines doesn't
+ * result in the content layer being recreated.
+ */
+ public void enableHardwareLayersForContent() {
+ View widget = getContent();
+ if (widget != null) {
+ widget.setLayerType(LAYER_TYPE_HARDWARE, null);
+ }
+ }
+
+ /**
+ * Because this view has fading outlines, it is essential that we enable hardware
+ * layers on the content (child) so that updating the alpha of the outlines doesn't
+ * result in the content layer being recreated.
+ */
+ public void disableHardwareLayersForContent() {
+ View widget = getContent();
+ if (widget != null) {
+ widget.setLayerType(LAYER_TYPE_NONE, null);
+ }
+ }
+
+ public void enableHardwareLayers() {
+ setLayerType(LAYER_TYPE_HARDWARE, null);
+ }
+
+ public void disableHardwareLayers() {
+ setLayerType(LAYER_TYPE_NONE, null);
+ }
+
+ public View getContent() {
+ return getChildAt(0);
+ }
+
+ public int getContentAppWidgetId() {
+ View content = getContent();
+ if (content instanceof AppWidgetHostView) {
+ return ((AppWidgetHostView) content).getAppWidgetId();
+ } else {
+ return ((KeyguardStatusView) content).getAppWidgetId();
+ }
+ }
+
+ public float getBackgroundAlpha() {
+ return mBackgroundAlpha;
+ }
+
+ public void setBackgroundAlphaMultiplier(float multiplier) {
+ if (Float.compare(mBackgroundAlphaMultiplier, multiplier) != 0) {
+ mBackgroundAlphaMultiplier = multiplier;
+ invalidate();
+ }
+ }
+
+ public float getBackgroundAlphaMultiplier() {
+ return mBackgroundAlphaMultiplier;
+ }
+
+ public void setBackgroundAlpha(float alpha) {
+ if (Float.compare(mBackgroundAlpha, alpha) != 0) {
+ mBackgroundAlpha = alpha;
+ invalidate();
+ }
+ }
+
+ public float getContentAlpha() {
+ return mContentAlpha;
+ }
+
+ public void setContentAlpha(float alpha) {
+ mContentAlpha = alpha;
+ View content = getContent();
+ if (content != null) {
+ content.setAlpha(alpha);
+ }
+ }
+
+ /**
+ * Set the top location of the challenge.
+ *
+ * @param top The top of the challenge, in _local_ coordinates, or -1 to indicate the challenge
+ * is down.
+ */
+ private void setChallengeTop(int top, boolean updateWidgetSize) {
+ // The widget starts below the padding, and extends to the top of the challengs.
+ int widgetHeight = top - getPaddingTop();
+ int frameHeight = top + getPaddingBottom();
+ setFrameHeight(frameHeight);
+ if (updateWidgetSize) {
+ setWidgetHeight(widgetHeight);
+ }
+ }
+
+ /**
+ * Depending on whether the security is up, the widget size needs to change
+ *
+ * @param height The height of the widget, -1 for full height
+ */
+ private void setWidgetHeight(int height) {
+ boolean needLayout = false;
+ View widget = getContent();
+ if (widget != null) {
+ LayoutParams lp = (LayoutParams) widget.getLayoutParams();
+ if (lp.height != height) {
+ needLayout = true;
+ lp.height = height;
+ }
+ }
+ if (needLayout) {
+ requestLayout();
+ }
+ }
+
+ public boolean isSmall() {
+ return mIsSmall;
+ }
+
+ public void adjustFrame(int challengeTop) {
+ setChallengeTop(challengeTop, false);
+ }
+
+ public void shrinkWidget() {
+ mIsSmall = true;
+ setChallengeTop(mSmallWidgetHeight, true);
+ }
+
+ public void resetSize() {
+ mIsSmall = false;
+ setFrameHeight(getMeasuredHeight());
+ setWidgetHeight(LayoutParams.MATCH_PARENT);
+ }
+
+ public void setFrameHeight(int height) {
+ height = Math.min(height, getMeasuredHeight());
+ mBackgroundRect.set(0, 0, getMeasuredWidth(), height);
+ invalidate();
+ }
+
+ public void hideFrame(Object caller) {
+ fadeFrame(caller, false, 0f, 150);
+ }
+
+ public void showFrame(Object caller) {
+ fadeFrame(caller, true, OUTLINE_ALPHA_MULTIPLIER, 150);
+ }
+
+ public void fadeFrame(Object caller, boolean takeControl, float alpha, int duration) {
+ if (takeControl) {
+ mBgAlphaController = caller;
+ }
+
+ if (mBgAlphaController != caller) return;
+
+ if (mFrameFade != null) {
+ mFrameFade.cancel();
+ mFrameFade = null;
+ }
+ PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", alpha);
+ mFrameFade = ObjectAnimator.ofPropertyValuesHolder(this, bgAlpha);
+ mFrameFade.setDuration(duration);
+ mFrameFade.start();
+ }
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
@@ -110,6 +356,8 @@ public class KeyguardWidgetFrame extends FrameLayout {
mGradientColor, 0, Shader.TileMode.CLAMP);
mRightToLeftGradient = new LinearGradient(x1, 0f, x0, 0f,
mGradientColor, 0, Shader.TileMode.CLAMP);
+ mBackgroundRect.set(0, 0, w, h);
+ invalidate();
}
void setOverScrollAmount(float r, boolean left) {
@@ -120,4 +368,17 @@ public class KeyguardWidgetFrame extends FrameLayout {
invalidate();
}
}
+
+ public void onActive(boolean isActive) {
+ // hook for subclasses
+ }
+
+ public boolean onUserInteraction(int action) {
+ // hook for subclasses
+ return false;
+ }
+
+ public void onChallengeActive(boolean challengeActive) {
+ // hook for subclasses
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index 1e65665b2af7..800ccc08671b 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -15,29 +15,57 @@
*/
package com.android.internal.policy.impl.keyguard;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
+import android.content.res.Resources;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.android.internal.R;
-public class KeyguardWidgetPager extends PagedView {
+import com.android.internal.widget.LockPatternUtils;
+
+import java.util.ArrayList;
+
+public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener,
+ OnLongClickListener {
+
ZInterpolator mZInterpolator = new ZInterpolator(0.5f);
private static float CAMERA_DISTANCE = 10000;
- private static float TRANSITION_SCALE_FACTOR = 0.74f;
- private static float TRANSITION_PIVOT = 0.65f;
- private static float TRANSITION_MAX_ROTATION = 30;
+ protected static float OVERSCROLL_MAX_ROTATION = 30;
private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
- private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
- private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
+
+ private KeyguardViewStateManager mViewStateManager;
+ private LockPatternUtils mLockPatternUtils;
+
+ // Related to the fading in / out background outlines
+ private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
+ private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 75;
+ protected AnimatorSet mChildrenOutlineFadeAnimation;
+ private float mChildrenOutlineAlpha = 0;
+ private float mSidePagesAlpha = 1f;
+ protected int mScreenCenter;
+ private boolean mHasLayout = false;
+ private boolean mHasMeasure = false;
+ private boolean mShowHintsOnLayout = false;
+
+ private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000;
+
+ private int mPage = 0;
+ private Callbacks mCallbacks;
+
+ private boolean mCameraWidgetEnabled;
public KeyguardWidgetPager(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -52,46 +80,221 @@ public class KeyguardWidgetPager extends PagedView {
if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
+
+ setPageSwitchListener(this);
+
+ Resources r = getResources();
+ mCameraWidgetEnabled = r.getBoolean(R.bool.kg_enable_camera_default_widget);
+ }
+
+ public void setViewStateManager(KeyguardViewStateManager viewStateManager) {
+ mViewStateManager = viewStateManager;
+ }
+
+ public void setLockPatternUtils(LockPatternUtils l) {
+ mLockPatternUtils = l;
+ }
+
+ @Override
+ public void onPageSwitch(View newPage, int newPageIndex) {
+ boolean showingStatusWidget = false;
+ if (newPage instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) newPage;
+ if (vg.getChildAt(0) instanceof KeyguardStatusView) {
+ showingStatusWidget = true;
+ }
+ }
+
+ // Disable the status bar clock if we're showing the default status widget
+ if (showingStatusWidget) {
+ setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK);
+ } else {
+ setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK);
+ }
+
+ // Extend the display timeout if the user switches pages
+ if (mPage != newPageIndex) {
+ int oldPageIndex = mPage;
+ mPage = newPageIndex;
+ userActivity();
+ KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex);
+ if (oldWidgetPage != null) {
+ oldWidgetPage.onActive(false);
+ }
+ KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex);
+ if (newWidgetPage != null) {
+ newWidgetPage.onActive(true);
+ }
+ }
+ if (mViewStateManager != null) {
+ mViewStateManager.onPageSwitch(newPage, newPageIndex);
+ }
+ }
+
+ private void userActivity() {
+ if (mCallbacks != null) {
+ mCallbacks.onUserActivityTimeoutChanged();
+ mCallbacks.userActivity();
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ KeyguardWidgetFrame currentWidgetPage = getWidgetPageAt(getCurrentPage());
+ if (currentWidgetPage != null && currentWidgetPage.onUserInteraction(ev.getAction())) {
+ return true;
+ }
+ return super.onTouchEvent(ev);
+ }
+
+ public void showPagingFeedback() {
+ // Nothing yet.
+ }
+
+ public long getUserActivityTimeout() {
+ View page = getPageAt(mPage);
+ if (page instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup) page;
+ View view = vg.getChildAt(0);
+ if (!(view instanceof KeyguardStatusView)
+ && !(view instanceof KeyguardMultiUserSelectorView)) {
+ return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT;
+ }
+ }
+ return -1;
+ }
+
+ public void setCallbacks(Callbacks callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ public interface Callbacks {
+ public void userActivity();
+ public void onUserActivityTimeoutChanged();
+ }
+
+ public void addWidget(View widget) {
+ addWidget(widget, -1);
+ }
+
+
+ public void onRemoveView(View v) {
+ int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
+ mLockPatternUtils.removeAppWidget(appWidgetId);
+ }
+
+ public void onAddView(View v, int index) {
+ int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
+ getVisiblePages(mTempVisiblePagesRange);
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ // Subtract from the index to take into account pages before the reorderable
+ // pages (e.g. the "add widget" page)
+ mLockPatternUtils.addAppWidget(appWidgetId, index - mTempVisiblePagesRange[0]);
}
/*
- * We wrap widgets in a special frame which handles drawing the overscroll foreground.
+ * We wrap widgets in a special frame which handles drawing the over scroll foreground.
*/
- public void addWidget(AppWidgetHostView widget) {
- KeyguardWidgetFrame frame = new KeyguardWidgetFrame(getContext());
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
- lp.gravity = Gravity.CENTER;
- // The framework adds a default padding to AppWidgetHostView. We don't need this padding
- // for the Keyguard, so we override it to be 0.
- widget.setPadding(0, 0, 0, 0);
- widget.setContentDescription(widget.getAppWidgetInfo().label);
- frame.addView(widget, lp);
- addView(frame);
+ public void addWidget(View widget, int pageIndex) {
+ KeyguardWidgetFrame frame;
+ // All views contained herein should be wrapped in a KeyguardWidgetFrame
+ if (!(widget instanceof KeyguardWidgetFrame)) {
+ frame = new KeyguardWidgetFrame(getContext());
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT);
+ lp.gravity = Gravity.TOP;
+ // The framework adds a default padding to AppWidgetHostView. We don't need this padding
+ // for the Keyguard, so we override it to be 0.
+ widget.setPadding(0, 0, 0, 0);
+ if (widget instanceof AppWidgetHostView) {
+ AppWidgetHostView awhv = (AppWidgetHostView) widget;
+ widget.setContentDescription(awhv.getAppWidgetInfo().label);
+ }
+ frame.addView(widget, lp);
+ } else {
+ frame = (KeyguardWidgetFrame) widget;
+ }
+
+ ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ frame.setOnLongClickListener(this);
+
+ if (pageIndex == -1) {
+ addView(frame, pageLp);
+ } else {
+ addView(frame, pageIndex, pageLp);
+ }
}
- protected void onUnhandledTap(MotionEvent ev) {
- if (getParent() instanceof KeyguardWidgetRegion) {
- ((KeyguardWidgetRegion) getParent()).showPagingFeedback();
+ // We enforce that all children are KeyguardWidgetFrames
+ @Override
+ public void addView(View child, int index) {
+ enforceKeyguardWidgetFrame(child);
+ super.addView(child, index);
+ }
+
+ @Override
+ public void addView(View child, int width, int height) {
+ enforceKeyguardWidgetFrame(child);
+ super.addView(child, width, height);
+ }
+
+ @Override
+ public void addView(View child, LayoutParams params) {
+ enforceKeyguardWidgetFrame(child);
+ super.addView(child, params);
+ }
+
+ @Override
+ public void addView(View child, int index, LayoutParams params) {
+ enforceKeyguardWidgetFrame(child);
+ super.addView(child, index, params);
+ }
+
+ private void enforceKeyguardWidgetFrame(View child) {
+ if (!(child instanceof KeyguardWidgetFrame)) {
+ throw new IllegalArgumentException(
+ "KeyguardWidgetPager children must be KeyguardWidgetFrames");
}
}
+ public KeyguardWidgetFrame getWidgetPageAt(int index) {
+ // This is always a valid cast as we've guarded the ability to
+ return (KeyguardWidgetFrame) getChildAt(index);
+ }
+
+ protected void onUnhandledTap(MotionEvent ev) {
+ showPagingFeedback();
+ }
+
@Override
protected void onPageBeginMoving() {
- // Enable hardware layers while pages are moving
- // TODO: We should only do this for the two views that are actually moving
- int children = getChildCount();
- for (int i = 0; i < children; i++) {
- getChildAt(i).setLayerType(LAYER_TYPE_HARDWARE, null);
+ if (mViewStateManager != null) {
+ mViewStateManager.onPageBeginMoving();
}
+ showOutlinesAndSidePages();
+ userActivity();
}
@Override
protected void onPageEndMoving() {
- // Disable hardware layers while pages are moving
+ if (mViewStateManager != null) {
+ mViewStateManager.onPageEndMoving();
+ }
+ hideOutlinesAndSidePages();
+ }
+
+ private void enablePageLayers() {
int children = getChildCount();
for (int i = 0; i < children; i++) {
- getChildAt(i).setLayerType(LAYER_TYPE_NONE, null);
+ getWidgetPageAt(i).enableHardwareLayersForContent();
+ }
+ }
+
+ private void disablePageLayers() {
+ int children = getChildCount();
+ for (int i = 0; i < children; i++) {
+ getWidgetPageAt(i).disableHardwareLayersForContent();
}
}
@@ -118,7 +321,7 @@ public class KeyguardWidgetPager extends PagedView {
public String getCurrentPageDescription() {
final int nextPageIndex = getNextPage();
if (nextPageIndex >= 0 && nextPageIndex < getChildCount()) {
- KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(nextPageIndex);
+ KeyguardWidgetFrame frame = getWidgetPageAt(nextPageIndex);
CharSequence title = frame.getChildAt(0).getContentDescription();
if (title == null) {
title = "";
@@ -135,76 +338,48 @@ public class KeyguardWidgetPager extends PagedView {
acceleratedOverScroll(amount);
}
- // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
+ float backgroundAlphaInterpolator(float r) {
+ return Math.min(1f, r);
+ }
+
+ private void updatePageAlphaValues(int screenCenter) {
+ }
+
+ public float getAlphaForPage(int screenCenter, int index) {
+ return 1f;
+ }
+
+ public float getOutlineAlphaForPage(int screenCenter, int index) {
+ return getAlphaForPage(screenCenter, index) * KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER;
+ }
+
+ protected boolean isOverScrollChild(int index, float scrollProgress) {
+ boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
+ return (isInOverscroll && (index == 0 && scrollProgress < 0 ||
+ index == getChildCount() - 1 && scrollProgress > 0));
+ }
+
@Override
protected void screenScrolled(int screenCenter) {
- super.screenScrolled(screenCenter);
-
+ mScreenCenter = screenCenter;
+ updatePageAlphaValues(screenCenter);
for (int i = 0; i < getChildCount(); i++) {
- View v = getPageAt(i);
+ KeyguardWidgetFrame v = getWidgetPageAt(i);
+ if (v == mDragView) continue;
if (v != null) {
float scrollProgress = getScrollProgress(screenCenter, v, i);
- float interpolatedProgress =
- mZInterpolator.getInterpolation(Math.abs(Math.min(scrollProgress, 0)));
- float scale = (1 - interpolatedProgress) +
- interpolatedProgress * TRANSITION_SCALE_FACTOR;
- float translationX = Math.min(0, scrollProgress) * v.getMeasuredWidth();
-
- float alpha;
+ v.setCameraDistance(mDensity * CAMERA_DISTANCE);
- if (scrollProgress < 0) {
- alpha = scrollProgress < 0 ? mAlphaInterpolator.getInterpolation(
- 1 - Math.abs(scrollProgress)) : 1.0f;
+ if (isOverScrollChild(i, scrollProgress) && PERFORM_OVERSCROLL_ROTATION) {
+ v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress);
+ v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0);
} else {
- // On large screens we need to fade the page as it nears its leftmost position
- alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress);
- }
-
- v.setCameraDistance(mDensity * CAMERA_DISTANCE);
- int pageWidth = v.getMeasuredWidth();
- int pageHeight = v.getMeasuredHeight();
-
- if (PERFORM_OVERSCROLL_ROTATION) {
- if (i == 0 && scrollProgress < 0) {
- // Overscroll to the left
- v.setPivotX(TRANSITION_PIVOT * pageWidth);
- v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
- if (v instanceof KeyguardWidgetFrame) {
- ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
- true);
- }
- scale = 1.0f;
- alpha = 1.0f;
- // On the first page, we don't want the page to have any lateral motion
- translationX = 0;
- } else if (i == getChildCount() - 1 && scrollProgress > 0) {
- // Overscroll to the right
- v.setPivotX((1 - TRANSITION_PIVOT) * pageWidth);
- v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
- scale = 1.0f;
- alpha = 1.0f;
- if (v instanceof KeyguardWidgetFrame) {
- ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress),
- false);
- }
- // On the last page, we don't want the page to have any lateral motion.
- translationX = 0;
- } else {
- v.setPivotY(pageHeight / 2.0f);
- v.setPivotX(pageWidth / 2.0f);
- v.setRotationY(0f);
- if (v instanceof KeyguardWidgetFrame) {
- ((KeyguardWidgetFrame) v).setOverScrollAmount(0, false);
- }
- }
+ v.setRotationY(0f);
+ v.setOverScrollAmount(0, false);
}
- v.setTranslationX(translationX);
- v.setScaleX(scale);
- v.setScaleY(scale);
- v.setAlpha(alpha);
-
+ float alpha = v.getAlpha();
// If the view has 0 alpha, we set it to be invisible so as to prevent
// it from accepting touches
if (alpha == 0) {
@@ -215,4 +390,208 @@ public class KeyguardWidgetPager extends PagedView {
}
}
}
+
+ @Override
+ void boundByReorderablePages(boolean isReordering, int[] range) {
+ if (isReordering) {
+ if (isAddWidgetPageVisible()) {
+ range[0]++;
+ }
+ if (isMusicWidgetVisible()) {
+ range[1]--;
+ }
+ if (isCameraWidgetVisible()) {
+ range[1]--;
+ }
+ }
+ }
+
+ /*
+ * Special widgets
+ */
+ boolean isAddWidgetPageVisible() {
+ // TODO: Make proper test once we decide whether the add-page is always showing
+ return true;
+ }
+ boolean isMusicWidgetVisible() {
+ // TODO: Make proper test once we have music in the list
+ return false;
+ }
+ boolean isCameraWidgetVisible() {
+ return mCameraWidgetEnabled;
+ }
+
+ @Override
+ protected void onStartReordering() {
+ super.onStartReordering();
+ showOutlinesAndSidePages();
+ }
+
+ @Override
+ protected void onEndReordering() {
+ super.onEndReordering();
+ hideOutlinesAndSidePages();
+ }
+
+ void showOutlinesAndSidePages() {
+ enablePageLayers();
+ animateOutlinesAndSidePages(true);
+ }
+
+ void hideOutlinesAndSidePages() {
+ animateOutlinesAndSidePages(false);
+ }
+
+ public void showInitialPageHints() {
+ if (mHasLayout) {
+ showOutlinesAndSidePages();
+ } else {
+ // The layout hints depend on layout being run once
+ mShowHintsOnLayout = true;
+ }
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mHasMeasure = false;
+ mHasLayout = false;
+ }
+
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (mShowHintsOnLayout) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ showOutlinesAndSidePages();
+ }
+ });
+ mShowHintsOnLayout = false;
+ }
+ mHasLayout = true;
+ }
+
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int maxChallengeTop = -1;
+ View parent = (View) getParent();
+ boolean challengeShowing = false;
+ // Widget pages need to know where the top of the sliding challenge is so that they
+ // now how big the widget should be when the challenge is up. We compute it here and
+ // then propagate it to each of our children.
+ if (parent.getParent() instanceof SlidingChallengeLayout) {
+ SlidingChallengeLayout scl = (SlidingChallengeLayout) parent.getParent();
+ int top = scl.getMaxChallengeTop();
+
+ // This is a bit evil, but we need to map a coordinate relative to the SCL into a
+ // coordinate relative to our children, hence we subtract the top padding.s
+ maxChallengeTop = top - getPaddingTop();
+ challengeShowing = scl.isChallengeShowing();
+ }
+
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ KeyguardWidgetFrame frame = getWidgetPageAt(i);
+ frame.setMaxChallengeTop(maxChallengeTop);
+
+ // On the very first measure pass, if the challenge is showing, we need to make sure
+ // that the widget on the current page is small.
+ if (challengeShowing && i == mCurrentPage && !mHasMeasure) {
+ frame.shrinkWidget();
+ }
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ void animateOutlinesAndSidePages(final boolean show) {
+ if (mChildrenOutlineFadeAnimation != null) {
+ mChildrenOutlineFadeAnimation.cancel();
+ mChildrenOutlineFadeAnimation = null;
+ }
+
+ int count = getChildCount();
+ PropertyValuesHolder alpha;
+ ArrayList<Animator> anims = new ArrayList<Animator>();
+
+ int duration = show ? CHILDREN_OUTLINE_FADE_IN_DURATION :
+ CHILDREN_OUTLINE_FADE_OUT_DURATION;
+
+ int curPage = getNextPage();
+ for (int i = 0; i < count; i++) {
+ float finalContentAlpha;
+ if (show) {
+ finalContentAlpha = getAlphaForPage(mScreenCenter, i);
+ } else if (!show && i == curPage) {
+ finalContentAlpha = 1f;
+ } else {
+ finalContentAlpha = 0f;
+ }
+ KeyguardWidgetFrame child = getWidgetPageAt(i);
+ alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalContentAlpha);
+ ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha);
+ anims.add(a);
+
+ float finalOutlineAlpha = show ? getOutlineAlphaForPage(mScreenCenter, i) : 0f;
+ child.fadeFrame(this, show, finalOutlineAlpha, duration);
+ }
+
+ mChildrenOutlineFadeAnimation = new AnimatorSet();
+ mChildrenOutlineFadeAnimation.playTogether(anims);
+
+ mChildrenOutlineFadeAnimation.setDuration(duration);
+ mChildrenOutlineFadeAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!show) {
+ disablePageLayers();
+ }
+ }
+ });
+ mChildrenOutlineFadeAnimation.start();
+ }
+
+ public void setChildrenOutlineAlpha(float alpha) {
+ mChildrenOutlineAlpha = alpha;
+ for (int i = 0; i < getChildCount(); i++) {
+ getWidgetPageAt(i).setBackgroundAlpha(alpha);
+ }
+ }
+
+ public void setSidePagesAlpha(float alpha) {
+ // This gives the current page, or the destination page if in transit.
+ int curPage = getNextPage();
+ mSidePagesAlpha = alpha;
+ for (int i = 0; i < getChildCount(); i++) {
+ if (curPage != i) {
+ getWidgetPageAt(i).setContentAlpha(alpha);
+ } else {
+ // We lock the current page alpha to 1.
+ getWidgetPageAt(i).setContentAlpha(1.0f);
+ }
+ }
+ }
+
+ public void setChildrenOutlineMultiplier(float alpha) {
+ mChildrenOutlineAlpha = alpha;
+ for (int i = 0; i < getChildCount(); i++) {
+ getWidgetPageAt(i).setBackgroundAlphaMultiplier(alpha);
+ }
+ }
+
+ public float getSidePagesAlpha() {
+ return mSidePagesAlpha;
+ }
+
+ public float getChildrenOutlineAlpha() {
+ return mChildrenOutlineAlpha;
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ // Disallow long pressing to reorder if the challenge is showing
+ if (!mViewStateManager.isChallengeShowing() && startReordering()) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java
deleted file mode 100644
index 4ff6f27302ce..000000000000
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetRegion.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.policy.impl.keyguard;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-import com.android.internal.R;
-public class KeyguardWidgetRegion extends LinearLayout implements PagedView.PageSwitchListener {
- KeyguardGlowStripView mLeftStrip;
- KeyguardGlowStripView mRightStrip;
- KeyguardWidgetPager mPager;
- private int mPage = 0;
- private Callbacks mCallbacks;
-
- // We are disabling touch interaction of the widget region for factory ROM.
- private static final boolean DISABLE_TOUCH_INTERACTION = true;
-
- private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000;
-
- public KeyguardWidgetRegion(Context context) {
- this(context, null, 0);
- }
-
- public KeyguardWidgetRegion(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public KeyguardWidgetRegion(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mLeftStrip = (KeyguardGlowStripView) findViewById(R.id.left_strip);
- mRightStrip = (KeyguardGlowStripView) findViewById(R.id.right_strip);
- mPager = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
- mPager.setPageSwitchListener(this);
-
- setSoundEffectsEnabled(false);
- if (!DISABLE_TOUCH_INTERACTION) {
- setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- showPagingFeedback();
- }
- });
- }
- }
-
- public void showPagingFeedback() {
- if ((mPage < mPager.getPageCount() - 1)) {
- mLeftStrip.makeEmGo();
- }
- if ((mPage > 0)) {
- mRightStrip.makeEmGo();
- }
- }
-
- @Override
- public void onPageSwitch(View newPage, int newPageIndex) {
- boolean showingStatusWidget = false;
- if (newPage instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) newPage;
- if (vg.getChildAt(0) instanceof KeyguardStatusView) {
- showingStatusWidget = true;
- }
- }
-
- // Disable the status bar clock if we're showing the default status widget
- if (showingStatusWidget) {
- setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK);
- } else {
- setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK);
- }
-
- // Extend the display timeout if the user switches pages
- if (mPage != newPageIndex) {
- mPage = newPageIndex;
- if (mCallbacks != null) {
- mCallbacks.onUserActivityTimeoutChanged();
- mCallbacks.userActivity();
- }
- }
- }
-
- public long getUserActivityTimeout() {
- View page = mPager.getPageAt(mPage);
- if (page instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) page;
- View view = vg.getChildAt(0);
- if (!(view instanceof KeyguardStatusView)
- && !(view instanceof KeyguardMultiUserSelectorView)) {
- return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT;
- }
- }
- return -1;
- }
-
- public void setCallbacks(Callbacks callbacks) {
- mCallbacks = callbacks;
- }
-
- public interface Callbacks {
- public void userActivity();
- public void onUserActivityTimeoutChanged();
- }
-}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
new file mode 100644
index 000000000000..a207f5d1048d
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import com.android.internal.R;
+
+public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayout {
+ private static final String TAG = "MultiPaneChallengeLayout";
+
+ final int mOrientation;
+ private boolean mIsBouncing;
+
+ public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
+ public static final int VERTICAL = LinearLayout.VERTICAL;
+
+ private View mChallengeView;
+ private View mUserSwitcherView;
+ private View mScrimView;
+ private OnBouncerStateChangedListener mBouncerListener;
+
+ private final Rect mTempRect = new Rect();
+
+ private final OnClickListener mScrimClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ hideBouncer();
+ }
+ };
+
+ public MultiPaneChallengeLayout(Context context) {
+ this(context, null);
+ }
+
+ public MultiPaneChallengeLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public MultiPaneChallengeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.MultiPaneChallengeLayout, defStyleAttr, 0);
+ mOrientation = a.getInt(R.styleable.MultiPaneChallengeLayout_orientation,
+ HORIZONTAL);
+ a.recycle();
+ }
+
+ @Override
+ public boolean isChallengeShowing() {
+ return true;
+ }
+
+ @Override
+ public boolean isChallengeOverlapping() {
+ return false;
+ }
+
+ @Override
+ public void showChallenge(boolean b) {
+ }
+
+ @Override
+ public void showBouncer() {
+ if (mIsBouncing) return;
+ mIsBouncing = true;
+ if (mScrimView != null) {
+ mScrimView.setVisibility(GONE);
+ }
+ if (mBouncerListener != null) {
+ mBouncerListener.onBouncerStateChanged(true);
+ }
+ }
+
+ @Override
+ public void hideBouncer() {
+ if (!mIsBouncing) return;
+ mIsBouncing = false;
+ if (mScrimView != null) {
+ mScrimView.setVisibility(GONE);
+ }
+ if (mBouncerListener != null) {
+ mBouncerListener.onBouncerStateChanged(false);
+ }
+ }
+
+ @Override
+ public boolean isBouncing() {
+ return mIsBouncing;
+ }
+
+ @Override
+ public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
+ mBouncerListener = listener;
+ }
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ if (mIsBouncing && child != mChallengeView) {
+ // Clear out of the bouncer if the user tries to move focus outside of
+ // the security challenge view.
+ hideBouncer();
+ }
+ super.requestChildFocus(child, focused);
+ }
+
+ void setScrimView(View scrim) {
+ if (mScrimView != null) {
+ mScrimView.setOnClickListener(null);
+ }
+ mScrimView = scrim;
+ mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE);
+ mScrimView.setFocusable(true);
+ mScrimView.setOnClickListener(mScrimClickListener);
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
+ MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
+ throw new IllegalArgumentException(
+ "MultiPaneChallengeLayout must be measured with an exact size");
+ }
+
+ final int width = MeasureSpec.getSize(widthSpec);
+ final int height = MeasureSpec.getSize(heightSpec);
+ setMeasuredDimension(width, height);
+
+ int widthUsed = 0;
+ int heightUsed = 0;
+
+ // First pass. Find the challenge view and measure the user switcher,
+ // which consumes space in the layout.
+ mChallengeView = null;
+ mUserSwitcherView = null;
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
+ if (mChallengeView != null) {
+ throw new IllegalStateException(
+ "There may only be one child of type challenge");
+ }
+ mChallengeView = child;
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) {
+ if (mUserSwitcherView != null) {
+ throw new IllegalStateException(
+ "There may only be one child of type userSwitcher");
+ }
+ mUserSwitcherView = child;
+
+ if (child.getVisibility() == GONE) continue;
+
+ int adjustedWidthSpec = widthSpec;
+ int adjustedHeightSpec = heightSpec;
+ if (lp.maxWidth >= 0) {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(lp.maxWidth, MeasureSpec.getSize(widthSpec)),
+ MeasureSpec.EXACTLY);
+ }
+ if (lp.maxHeight >= 0) {
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(lp.maxHeight, MeasureSpec.getSize(heightSpec)),
+ MeasureSpec.EXACTLY);
+ }
+ // measureChildWithMargins will resolve layout direction for the LayoutParams
+ measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);
+
+ // Only subtract out space from one dimension. Favor vertical.
+ // Offset by 1.5x to add some balance along the other edge.
+ if (Gravity.isVertical(lp.gravity)) {
+ heightUsed += child.getMeasuredHeight() * 1.5f;
+ } else if (Gravity.isHorizontal(lp.gravity)) {
+ widthUsed += child.getMeasuredWidth() * 1.5f;
+ }
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
+ setScrimView(child);
+ child.measure(widthSpec, heightSpec);
+ }
+ }
+
+ // Second pass. Measure everything that's left.
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER ||
+ lp.childType == LayoutParams.CHILD_TYPE_SCRIM ||
+ child.getVisibility() == GONE) {
+ // Don't need to measure GONE children, and the user switcher was already measured.
+ continue;
+ }
+
+ int adjustedWidthSpec;
+ int adjustedHeightSpec;
+ if (lp.centerWithinArea > 0) {
+ if (mOrientation == HORIZONTAL) {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ (int) ((width - widthUsed) * lp.centerWithinArea + 0.5f),
+ MeasureSpec.EXACTLY);
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(heightSpec) - heightUsed, MeasureSpec.EXACTLY);
+ } else {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(widthSpec) - widthUsed, MeasureSpec.EXACTLY);
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ (int) ((height - heightUsed) * lp.centerWithinArea + 0.5f),
+ MeasureSpec.EXACTLY);
+ }
+ } else {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(widthSpec) - widthUsed, MeasureSpec.EXACTLY);
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(heightSpec) - heightUsed, MeasureSpec.EXACTLY);
+ }
+ if (lp.maxWidth >= 0) {
+ adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(lp.maxWidth, MeasureSpec.getSize(adjustedWidthSpec)),
+ MeasureSpec.EXACTLY);
+ }
+ if (lp.maxHeight >= 0) {
+ adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(lp.maxHeight, MeasureSpec.getSize(adjustedHeightSpec)),
+ MeasureSpec.EXACTLY);
+ }
+
+ measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final Rect padding = mTempRect;
+ padding.left = getPaddingLeft();
+ padding.top = getPaddingTop();
+ padding.right = getPaddingRight();
+ padding.bottom = getPaddingBottom();
+ final int width = r - l;
+ final int height = b - t;
+
+ // Reserve extra space in layout for the user switcher by modifying
+ // local padding during this layout pass
+ if (mUserSwitcherView != null && mUserSwitcherView.getVisibility() != GONE) {
+ layoutWithGravity(width, height, mUserSwitcherView, padding, true);
+ }
+
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+
+ // We did the user switcher above if we have one.
+ if (child == mUserSwitcherView || child.getVisibility() == GONE) continue;
+
+ if (child == mScrimView) {
+ child.layout(0, 0, width, height);
+ continue;
+ }
+
+ layoutWithGravity(width, height, child, padding, false);
+ }
+ }
+
+ private void layoutWithGravity(int width, int height, View child, Rect padding,
+ boolean adjustPadding) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ final int gravity = Gravity.getAbsoluteGravity(lp.gravity, getLayoutDirection());
+
+ final boolean fixedLayoutSize = lp.centerWithinArea > 0;
+ final boolean fixedLayoutHorizontal = fixedLayoutSize && mOrientation == HORIZONTAL;
+ final boolean fixedLayoutVertical = fixedLayoutSize && mOrientation == VERTICAL;
+
+ final int adjustedWidth;
+ final int adjustedHeight;
+ if (fixedLayoutHorizontal) {
+ final int paddedWidth = width - padding.left - padding.right;
+ adjustedWidth = (int) (paddedWidth * lp.centerWithinArea + 0.5f);
+ adjustedHeight = height;
+ } else if (fixedLayoutVertical) {
+ final int paddedHeight = height - padding.top - padding.bottom;
+ adjustedWidth = width;
+ adjustedHeight = (int) (paddedHeight * lp.centerWithinArea + 0.5f);
+ } else {
+ adjustedWidth = width;
+ adjustedHeight = height;
+ }
+
+ final boolean isVertical = Gravity.isVertical(gravity);
+ final boolean isHorizontal = Gravity.isHorizontal(gravity);
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+
+ int left = padding.left;
+ int top = padding.top;
+ int right = left + childWidth;
+ int bottom = top + childHeight;
+ switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
+ case Gravity.TOP:
+ top = fixedLayoutVertical ?
+ padding.top + (adjustedHeight - childHeight) / 2 : padding.top;
+ bottom = top + childHeight;
+ if (adjustPadding && isVertical) {
+ padding.top = bottom;
+ padding.bottom += childHeight / 2;
+ }
+ break;
+ case Gravity.BOTTOM:
+ bottom = fixedLayoutVertical
+ ? height - padding.bottom - (adjustedHeight - childHeight) / 2
+ : height - padding.bottom;
+ top = bottom - childHeight;
+ if (adjustPadding && isVertical) {
+ padding.bottom = height - top;
+ padding.top += childHeight / 2;
+ }
+ break;
+ case Gravity.CENTER_VERTICAL:
+ final int paddedHeight = height - padding.top - padding.bottom;
+ top = padding.top + (paddedHeight - childHeight) / 2;
+ bottom = top + childHeight;
+ break;
+ }
+ switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.LEFT:
+ left = fixedLayoutHorizontal ?
+ padding.left + (adjustedWidth - childWidth) / 2 : padding.left;
+ right = left + childWidth;
+ if (adjustPadding && isHorizontal && !isVertical) {
+ padding.left = right;
+ padding.right += childWidth / 2;
+ }
+ break;
+ case Gravity.RIGHT:
+ right = fixedLayoutHorizontal
+ ? width - padding.right - (adjustedWidth - childWidth) / 2
+ : width - padding.right;
+ left = right - childWidth;
+ if (adjustPadding && isHorizontal && !isVertical) {
+ padding.right = width - left;
+ padding.left += childWidth / 2;
+ }
+ break;
+ case Gravity.CENTER_HORIZONTAL:
+ final int paddedWidth = width - padding.left - padding.right;
+ left = (paddedWidth - childWidth) / 2;
+ right = left + childWidth;
+ break;
+ }
+ child.layout(left, top, right, bottom);
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs, this);
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
+ p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
+ new LayoutParams(p);
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams();
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams;
+ }
+
+ public static class LayoutParams extends MarginLayoutParams {
+
+ public float centerWithinArea = 0;
+
+ public int childType = 0;
+
+ public static final int CHILD_TYPE_NONE = 0;
+ public static final int CHILD_TYPE_WIDGET = 1;
+ public static final int CHILD_TYPE_CHALLENGE = 2;
+ public static final int CHILD_TYPE_USER_SWITCHER = 3;
+ public static final int CHILD_TYPE_SCRIM = 4;
+
+ public int gravity = Gravity.NO_GRAVITY;
+
+ public int maxWidth = -1;
+ public int maxHeight = -1;
+
+ public LayoutParams() {
+ this(WRAP_CONTENT, WRAP_CONTENT);
+ }
+
+ LayoutParams(Context c, AttributeSet attrs, MultiPaneChallengeLayout parent) {
+ super(c, attrs);
+
+ final TypedArray a = c.obtainStyledAttributes(attrs,
+ R.styleable.MultiPaneChallengeLayout_Layout);
+
+ centerWithinArea = a.getFloat(
+ R.styleable.MultiPaneChallengeLayout_Layout_layout_centerWithinArea, 0);
+ childType = a.getInt(R.styleable.MultiPaneChallengeLayout_Layout_layout_childType,
+ CHILD_TYPE_NONE);
+ gravity = a.getInt(R.styleable.MultiPaneChallengeLayout_Layout_layout_gravity,
+ Gravity.NO_GRAVITY);
+ maxWidth = a.getDimensionPixelSize(
+ R.styleable.MultiPaneChallengeLayout_Layout_layout_maxWidth, -1);
+ maxHeight = a.getDimensionPixelSize(
+ R.styleable.MultiPaneChallengeLayout_Layout_layout_maxHeight, -1);
+
+ // Default gravity settings based on type and parent orientation
+ if (gravity == Gravity.NO_GRAVITY) {
+ if (parent.mOrientation == HORIZONTAL) {
+ switch (childType) {
+ case CHILD_TYPE_WIDGET:
+ gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
+ break;
+ case CHILD_TYPE_CHALLENGE:
+ gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
+ break;
+ case CHILD_TYPE_USER_SWITCHER:
+ gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ break;
+ }
+ } else {
+ switch (childType) {
+ case CHILD_TYPE_WIDGET:
+ gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+ break;
+ case CHILD_TYPE_CHALLENGE:
+ gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ break;
+ case CHILD_TYPE_USER_SWITCHER:
+ gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ break;
+ }
+ }
+ }
+
+ a.recycle();
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(MarginLayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(LayoutParams source) {
+ this((MarginLayoutParams) source);
+
+ centerWithinArea = source.centerWithinArea;
+ childType = source.childType;
+ gravity = source.gravity;
+ maxWidth = source.maxWidth;
+ maxHeight = source.maxHeight;
+ }
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java
new file mode 100644
index 000000000000..060cc03e0f92
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.text.SpannableStringBuilder;
+import android.text.style.TextAppearanceSpan;
+import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
+
+public class NumPadKey extends Button {
+ // list of "ABC", etc per digit, starting with '0'
+ static String sKlondike[];
+
+ int mDigit = -1;
+ int mTextViewResId;
+ TextView mTextView = null;
+ boolean mEnableHaptics;
+
+ private View.OnClickListener mListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View thisView) {
+ if (mTextView == null) {
+ if (mTextViewResId > 0) {
+ final View v = NumPadKey.this.getRootView().findViewById(mTextViewResId);
+ if (v != null && v instanceof TextView) {
+ mTextView = (TextView) v;
+ }
+ }
+ }
+ if (mTextView != null) {
+ mTextView.append(String.valueOf(mDigit));
+ }
+ doHapticKeyClick();
+ }
+ };
+
+ public NumPadKey(Context context) {
+ this(context, null);
+ }
+
+ public NumPadKey(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public NumPadKey(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NumPadKey);
+ mDigit = a.getInt(R.styleable.NumPadKey_digit, mDigit);
+ setTextViewResId(a.getResourceId(R.styleable.NumPadKey_textView, 0));
+
+ setOnClickListener(mListener);
+
+ mEnableHaptics = new LockPatternUtils(context).isTactileFeedbackEnabled();
+
+ SpannableStringBuilder builder = new SpannableStringBuilder();
+ builder.append(String.valueOf(mDigit));
+ if (mDigit >= 0) {
+ if (sKlondike == null) {
+ sKlondike = context.getResources().getStringArray(
+ R.array.lockscreen_num_pad_klondike);
+ }
+ if (sKlondike != null && sKlondike.length > mDigit) {
+ final String extra = sKlondike[mDigit];
+ final int extraLen = extra.length();
+ if (extraLen > 0) {
+ builder.append(extra);
+ builder.setSpan(
+ new TextAppearanceSpan(context, R.style.TextAppearance_NumPadKey_Klondike),
+ builder.length()-extraLen, builder.length(), 0);
+ }
+ }
+ }
+ setText(builder);
+ }
+
+ public void setTextView(TextView tv) {
+ mTextView = tv;
+ }
+
+ public void setTextViewResId(int resId) {
+ mTextView = null;
+ mTextViewResId = resId;
+ }
+
+ // Cause a VIRTUAL_KEY vibration
+ public void doHapticKeyClick() {
+ if (mEnableHaptics) {
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+ | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ }
+ }
+}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
index 86c05b196e3f..657a31f3d38c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
@@ -18,12 +18,16 @@ package com.android.internal.policy.impl.keyguard;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Parcel;
@@ -41,6 +45,8 @@ import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.Scroller;
@@ -52,7 +58,7 @@ import java.util.ArrayList;
* An abstraction of the original Workspace which supports browsing through a
* sequential list of "pages"
*/
-public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
+public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
private static final String TAG = "WidgetPagedView";
private static final boolean DEBUG = false;
protected static final int INVALID_PAGE = -1;
@@ -60,7 +66,7 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
// the min drag distance for a fling to register, to prevent random page shifts
private static final int MIN_LENGTH_FOR_FLING = 25;
- protected static final int PAGE_SNAP_ANIMATION_DURATION = 550;
+ protected static final int PAGE_SNAP_ANIMATION_DURATION = 750;
protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
protected static final float NANOTIME_DIV = 1000000000.0f;
@@ -78,7 +84,9 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
private static final int MIN_FLING_VELOCITY = 250;
// We are disabling touch interaction of the widget region for factory ROM.
- private static final boolean DISABLE_TOUCH_INTERACTION = true;
+ private static final boolean DISABLE_TOUCH_INTERACTION = false;
+ private static final boolean DISABLE_TOUCH_SIDE_PAGES = true;
+ private static final boolean DISABLE_FLING_TO_DELETE = false;
static final int AUTOMATIC_PAGE_SPACING = -1;
@@ -100,7 +108,11 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
protected Scroller mScroller;
private VelocityTracker mVelocityTracker;
+ private float mParentDownMotionX;
+ private float mParentDownMotionY;
private float mDownMotionX;
+ private float mDownMotionY;
+ private float mDownScrollX;
protected float mLastMotionX;
protected float mLastMotionXRemainder;
protected float mLastMotionY;
@@ -114,6 +126,8 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
protected final static int TOUCH_STATE_SCROLLING = 1;
protected final static int TOUCH_STATE_PREV_PAGE = 2;
protected final static int TOUCH_STATE_NEXT_PAGE = 3;
+ protected final static int TOUCH_STATE_REORDERING = 4;
+
protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
protected int mTouchState = TOUCH_STATE_REST;
@@ -121,22 +135,13 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
protected OnLongClickListener mLongClickListener;
- protected boolean mAllowLongPress = true;
-
protected int mTouchSlop;
private int mPagingTouchSlop;
private int mMaximumVelocity;
private int mMinimumWidth;
protected int mPageSpacing;
- protected int mPageLayoutPaddingTop;
- protected int mPageLayoutPaddingBottom;
- protected int mPageLayoutPaddingLeft;
- protected int mPageLayoutPaddingRight;
- protected int mPageLayoutWidthGap;
- protected int mPageLayoutHeightGap;
protected int mCellCountX = 0;
protected int mCellCountY = 0;
- protected boolean mCenterPagesVertically;
protected boolean mAllowOverScroll = true;
protected int mUnboundedScrollX;
protected int[] mTempVisiblePagesRange = new int[2];
@@ -162,7 +167,7 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
protected boolean mContentIsRefreshable = true;
// If true, modify alpha of neighboring pages as user scrolls left/right
- protected boolean mFadeInAdjacentScreens = true;
+ protected boolean mFadeInAdjacentScreens = false;
// It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
// to switch to a new page
@@ -188,6 +193,51 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
protected static final int sScrollIndicatorFadeOutDuration = 650;
protected static final int sScrollIndicatorFlashDuration = 650;
+ // The viewport whether the pages are to be contained (the actual view may be larger than the
+ // viewport)
+ private Rect mViewport = new Rect();
+
+ // Reordering
+ // We use the min scale to determine how much to expand the actually PagedView measured
+ // dimensions such that when we are zoomed out, the view is not clipped
+ private int REORDERING_DROP_REPOSITION_DURATION = 200;
+ protected int REORDERING_REORDER_REPOSITION_DURATION = 300;
+ protected int REORDERING_ZOOM_IN_OUT_DURATION = 250;
+ private int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 300;
+ private float REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE = 0.1f;
+ private float mMinScale = 1f;
+ protected View mDragView;
+ private AnimatorSet mZoomInOutAnim;
+ private Runnable mSidePageHoverRunnable;
+ private int mSidePageHoverIndex = -1;
+ // This variable's scope is only for the duration of startReordering() and endReordering()
+ private boolean mReorderingStarted = false;
+ // This variable's scope is for the duration of startReordering() and after the zoomIn()
+ // animation after endReordering()
+ private boolean mIsReordering;
+ // The runnable that settles the page after snapToPage and animateDragViewToOriginalPosition
+ private int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2;
+ private int mPostReorderingPreZoomInRemainingAnimationCount;
+ private Runnable mPostReorderingPreZoomInRunnable;
+
+ // Edge swiping
+ private boolean mOnlyAllowEdgeSwipes = false;
+ private boolean mDownEventOnEdge = false;
+ private int mEdgeSwipeRegionSize = 0;
+
+ // Convenience/caching
+ private Matrix mTmpInvMatrix = new Matrix();
+ private float[] mTmpPoint = new float[2];
+
+ // Fling to delete
+ private int FLING_TO_DELETE_FADE_OUT_DURATION = 350;
+ private float FLING_TO_DELETE_FRICTION = 0.035f;
+ // The degrees specifies how much deviation from the up vector to still consider a fling "up"
+ private float FLING_TO_DELETE_MAX_FLING_DEGREES = 35f;
+ private int FLING_TO_DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250;
+ protected int mFlingToDeleteThresholdVelocity = -1400;
+ private boolean mIsFlingingToDelete = false;
+
public interface PageSwitchListener {
void onPageSwitch(View newPage, int newPageIndex);
}
@@ -205,24 +255,15 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.PagedView, defStyle, 0);
setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0));
- mPageLayoutPaddingTop = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingTop, 0);
- mPageLayoutPaddingBottom = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingBottom, 0);
- mPageLayoutPaddingLeft = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingLeft, 0);
- mPageLayoutPaddingRight = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingRight, 0);
- mPageLayoutWidthGap = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutWidthGap, 0);
- mPageLayoutHeightGap = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutHeightGap, 0);
mScrollIndicatorPaddingLeft =
a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0);
mScrollIndicatorPaddingRight =
- a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
+ a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0);
a.recycle();
+ mEdgeSwipeRegionSize =
+ getResources().getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size);
+
setHapticFeedbackEnabled(false);
init();
}
@@ -235,7 +276,6 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
mDirtyPageContent.ensureCapacity(32);
mScroller = new Scroller(getContext(), new ScrollInterpolator());
mCurrentPage = 0;
- mCenterPagesVertically = true;
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
@@ -243,12 +283,76 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mDensity = getResources().getDisplayMetrics().density;
+ // Scale the fling-to-delete threshold by the density
+ mFlingToDeleteThresholdVelocity =
+ (int) (mFlingToDeleteThresholdVelocity * mDensity);
+
mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity);
mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity);
setOnHierarchyChangeListener(this);
}
+ // Convenience methods to map points from self to parent and vice versa
+ float[] mapPointFromSelfToParent(float x, float y) {
+ mTmpPoint[0] = x;
+ mTmpPoint[1] = y;
+ getMatrix().mapPoints(mTmpPoint);
+ mTmpPoint[0] += getLeft();
+ mTmpPoint[1] += getTop();
+ return mTmpPoint;
+ }
+ float[] mapPointFromParentToSelf(float x, float y) {
+ mTmpPoint[0] = x - getLeft();
+ mTmpPoint[1] = y - getTop();
+ getMatrix().invert(mTmpInvMatrix);
+ mTmpInvMatrix.mapPoints(mTmpPoint);
+ return mTmpPoint;
+ }
+
+ void updateDragViewTranslationDuringDrag() {
+ float x = mLastMotionX - mDownMotionX + getScrollX() - mDownScrollX;
+ float y = mLastMotionY - mDownMotionY;
+ mDragView.setTranslationX(x);
+ mDragView.setTranslationY(y);
+
+ if (DEBUG) Log.d(TAG, "PagedView.updateDragViewTranslationDuringDrag(): " + x + ", " + y);
+ }
+
+ public void setMinScale(float f) {
+ mMinScale = f;
+ requestLayout();
+ }
+
+ @Override
+ public void setScaleX(float scaleX) {
+ super.setScaleX(scaleX);
+ if (isReordering(true)) {
+ float[] p = mapPointFromParentToSelf(mParentDownMotionX, mParentDownMotionY);
+ mLastMotionX = p[0];
+ mLastMotionY = p[1];
+ updateDragViewTranslationDuringDrag();
+ }
+ }
+
+ // Convenience methods to get the actual width/height of the PagedView (since it is measured
+ // to be larger to account for the minimum possible scale)
+ int getViewportWidth() {
+ return mViewport.width();
+ }
+ int getViewportHeight() {
+ return mViewport.height();
+ }
+
+ // Convenience methods to get the offset ASSUMING that we are centering the pages in the
+ // PagedView both horizontally and vertically
+ int getViewportOffsetX() {
+ return (getMeasuredWidth() - getViewportWidth()) / 2;
+ }
+ int getViewportOffsetY() {
+ return (getMeasuredHeight() - getViewportHeight()) / 2;
+ }
+
public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
mPageSwitchListener = pageSwitchListener;
if (mPageSwitchListener != null) {
@@ -328,6 +432,10 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
invalidate();
}
+ public void setOnlyAllowEdgeSwipes(boolean enable) {
+ mOnlyAllowEdgeSwipes = enable;
+ }
+
protected void notifyPageSwitchListener() {
if (mPageSwitchListener != null) {
mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
@@ -400,6 +508,14 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
mTouchX = x;
mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+
+ // Update the last motion events when scrolling
+ if (isReordering(true)) {
+ float[] p = mapPointFromParentToSelf(mParentDownMotionX, mParentDownMotionY);
+ mLastMotionX = p[0];
+ mLastMotionY = p[1];
+ updateDragViewTranslationDuringDrag();
+ }
}
// we moved this functionality to a helper function so SmoothPagedView can reuse it
@@ -424,6 +540,8 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
pageEndMoving();
}
+ onPostReorderingAnimationCompleted();
+
// Notify the user when the page changes
AccessibilityManager accessibilityManager = (AccessibilityManager)
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
@@ -454,10 +572,20 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
return;
}
- final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ // We measure the dimensions of the PagedView to be larger than the pages so that when we
+ // zoom out (and scale down), the view is still contained in the parent
+ View parent = (View) getParent();
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ // NOTE: We multiply by 1.5f to account for the fact that depending on the offset of the
+ // viewport, we can be at most one and a half screens offset once we scale down
+ int parentWidthSize = (int) (1.5f * parent.getMeasuredWidth());
+ int parentHeightSize = parent.getMeasuredHeight();
+ int scaledWidthSize = (int) (parentWidthSize / mMinScale);
+ int scaledHeightSize = (int) (parentHeightSize / mMinScale);
+ mViewport.set(0, 0, widthSize, heightSize);
if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -481,13 +609,29 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
// The children are given the same width and height as the workspace
// unless they were set to WRAP_CONTENT
if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
+ if (DEBUG) Log.d(TAG, "PagedView.scaledSize: " + scaledWidthSize + ", " + scaledHeightSize);
+ if (DEBUG) Log.d(TAG, "PagedView.parentSize: " + parentWidthSize + ", " + parentHeightSize);
+ if (DEBUG) Log.d(TAG, "PagedView.horizontalPadding: " + horizontalPadding);
+ if (DEBUG) Log.d(TAG, "PagedView.verticalPadding: " + verticalPadding);
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
// disallowing padding in paged view (just pass 0)
final View child = getPageAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ int childWidthMode;
+ if (lp.width == LayoutParams.WRAP_CONTENT) {
+ childWidthMode = MeasureSpec.AT_MOST;
+ } else {
+ childWidthMode = MeasureSpec.EXACTLY;
+ }
- int childWidthMode = MeasureSpec.EXACTLY;
- int childHeightMode = MeasureSpec.EXACTLY;
+ int childHeightMode;
+ if (lp.height == LayoutParams.WRAP_CONTENT) {
+ childHeightMode = MeasureSpec.AT_MOST;
+ } else {
+ childHeightMode = MeasureSpec.EXACTLY;
+ }
final int childWidthMeasureSpec =
MeasureSpec.makeMeasureSpec(widthSize - horizontalPadding, childWidthMode);
@@ -496,21 +640,20 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
-
- setMeasuredDimension(widthSize, heightSize);
+ setMeasuredDimension(scaledWidthSize, scaledHeightSize);
// We can't call getChildOffset/getRelativeChildOffset until we set the measured dimensions.
// We also wait until we set the measured dimensions before flushing the cache as well, to
// ensure that the cache is filled with good values.
invalidateCachedOffsets();
- if (mChildCountOnLastMeasure != getChildCount()) {
+ if (mChildCountOnLastMeasure != getChildCount() && !mIsFlingingToDelete) {
setCurrentPage(mCurrentPage);
}
mChildCountOnLastMeasure = getChildCount();
if (childCount > 0) {
- if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getMeasuredWidth() + ", "
+ if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getViewportWidth() + ", "
+ getChildWidth(0));
// Calculate the variable page spacing if necessary
@@ -547,19 +690,21 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
}
if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
- final int verticalPadding = getPaddingTop() + getPaddingBottom();
final int childCount = getChildCount();
- int childLeft = getRelativeChildOffset(0);
+ int offsetX = getViewportOffsetX();
+ int offsetY = getViewportOffsetY();
+
+ // Update the viewport offsets
+ mViewport.offset(offsetX, offsetY);
+
+ int childLeft = offsetX + getRelativeChildOffset(0);
for (int i = 0; i < childCount; i++) {
final View child = getPageAt(i);
+ int childTop = offsetY + getPaddingTop();
if (child.getVisibility() != View.GONE) {
final int childWidth = getScaledMeasuredWidth(child);
final int childHeight = child.getMeasuredHeight();
- int childTop = getPaddingTop();
- if (mCenterPagesVertically) {
- childTop += ((getMeasuredHeight() - verticalPadding) - childHeight) / 2;
- }
if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop);
child.layout(childLeft, childTop,
@@ -577,22 +722,6 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
}
protected void screenScrolled(int screenCenter) {
- if (isScrollingIndicatorEnabled()) {
- updateScrollingIndicator();
- }
- boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
-
- if (mFadeInAdjacentScreens && !isInOverscroll) {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child != null) {
- float scrollProgress = getScrollProgress(screenCenter, child, i);
- float alpha = 1 - Math.abs(scrollProgress);
- child.setAlpha(alpha);
- }
- }
- invalidate();
- }
}
@Override
@@ -606,7 +735,7 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
@Override
public void onChildViewRemoved(View parent, View child) {
- // TODO Auto-generated method stub
+ mForceScreenScrolled = true;
}
protected void invalidateCachedOffsets() {
@@ -659,7 +788,7 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
} else {
final int padding = getPaddingLeft() + getPaddingRight();
final int offset = getPaddingLeft() +
- (getMeasuredWidth() - padding - getChildWidth(index)) / 2;
+ (getViewportWidth() - padding - getChildWidth(index)) / 2;
if (mChildRelativeOffsets != null) {
mChildRelativeOffsets[index] = offset;
}
@@ -676,33 +805,47 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
return (int) (maxWidth * mLayoutScale + 0.5f);
}
+ void boundByReorderablePages(boolean isReordering, int[] range) {
+ // Do nothing
+ }
+
+ // TODO: Fix this
protected void getVisiblePages(int[] range) {
+ range[0] = 0;
+ range[1] = getPageCount() - 1;
+
+ /*
final int pageCount = getChildCount();
if (pageCount > 0) {
- final int screenWidth = getMeasuredWidth();
+ final int screenWidth = getViewportWidth();
int leftScreen = 0;
int rightScreen = 0;
+ int offsetX = getViewportOffsetX() + getScrollX();
View currPage = getPageAt(leftScreen);
while (leftScreen < pageCount - 1 &&
currPage.getX() + currPage.getWidth() -
- currPage.getPaddingRight() < getScrollX()) {
+ currPage.getPaddingRight() < offsetX) {
leftScreen++;
currPage = getPageAt(leftScreen);
}
rightScreen = leftScreen;
currPage = getPageAt(rightScreen + 1);
while (rightScreen < pageCount - 1 &&
- currPage.getX() - currPage.getPaddingLeft() < getScrollX() + screenWidth) {
+ currPage.getX() - currPage.getPaddingLeft() < offsetX + screenWidth) {
rightScreen++;
currPage = getPageAt(rightScreen + 1);
}
- range[0] = leftScreen;
- range[1] = rightScreen;
+
+ // TEMP: this is a hacky way to ensure that animations to new pages are not clipped
+ // because we don't draw them while scrolling?
+ range[0] = Math.max(0, leftScreen - 1);
+ range[1] = Math.min(rightScreen + 1, getChildCount() - 1);
} else {
range[0] = -1;
range[1] = -1;
}
+ */
}
protected boolean shouldDrawChild(View child) {
@@ -711,7 +854,7 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
@Override
protected void dispatchDraw(Canvas canvas) {
- int halfScreenSize = getMeasuredWidth() / 2;
+ int halfScreenSize = getViewportWidth() / 2;
// mOverScrollX is equal to getScrollX() when we're within the normal scroll range.
// Otherwise it is equal to the scaled overscroll position.
int screenCenter = mOverScrollX + halfScreenSize;
@@ -728,6 +871,7 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
final int pageCount = getChildCount();
if (pageCount > 0) {
getVisiblePages(mTempVisiblePagesRange);
+ boundByReorderablePages(isReordering(false), mTempVisiblePagesRange);
final int leftScreen = mTempVisiblePagesRange[0];
final int rightScreen = mTempVisiblePagesRange[1];
if (leftScreen != -1 && rightScreen != -1) {
@@ -737,13 +881,20 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
canvas.clipRect(getScrollX(), getScrollY(), getScrollX() + getRight() - getLeft(),
getScrollY() + getBottom() - getTop());
- for (int i = getChildCount() - 1; i >= 0; i--) {
+ // Draw all the children, leaving the drag view for last
+ for (int i = pageCount - 1; i >= 0; i--) {
final View v = getPageAt(i);
+ if (v == mDragView) continue;
if (mForceDrawAllChildrenNextFrame ||
(leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) {
drawChild(canvas, v, drawingTime);
}
}
+ // Draw the drag view on top (if there is one)
+ if (mDragView != null) {
+ drawChild(canvas, mDragView, drawingTime);
+ }
+
mForceDrawAllChildrenNextFrame = false;
canvas.restore();
}
@@ -836,31 +987,17 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
}
/**
- * {@inheritDoc}
- */
- @Override
- public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- if (disallowIntercept) {
- // We need to make sure to cancel our long press if
- // a scrollable widget takes over touch events
- final View currentPage = getPageAt(mCurrentPage);
- currentPage.cancelLongPress();
- }
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
- }
-
- /**
* Return true if a tap at (x, y) should trigger a flip to the previous page.
*/
protected boolean hitsPreviousPage(float x, float y) {
- return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
+ return (x < getViewportOffsetX() + getRelativeChildOffset(mCurrentPage) - mPageSpacing);
}
/**
* Return true if a tap at (x, y) should trigger a flip to the next page.
*/
protected boolean hitsNextPage(float x, float y) {
- return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
+ return (x > (getViewportOffsetX() + getViewportWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
}
@Override
@@ -912,12 +1049,23 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
final float y = ev.getY();
// Remember location of down touch
mDownMotionX = x;
+ mDownMotionY = y;
+ mDownScrollX = getScrollX();
mLastMotionX = x;
mLastMotionY = y;
+ float[] p = mapPointFromSelfToParent(x, y);
+ mParentDownMotionX = p[0];
+ mParentDownMotionY = p[1];
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
- mAllowLongPress = true;
+
+ // Determine if the down event is within the threshold to be an edge swipe
+ int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
+ int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
+ if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
+ mDownEventOnEdge = true;
+ }
/*
* If being flinged and user touches the screen, initiate drag;
@@ -935,12 +1083,14 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
// check if this can be the beginning of a tap on the side of the pages
// to scroll the current page
- if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
- if (getChildCount() > 0) {
- if (hitsPreviousPage(x, y)) {
- mTouchState = TOUCH_STATE_PREV_PAGE;
- } else if (hitsNextPage(x, y)) {
- mTouchState = TOUCH_STATE_NEXT_PAGE;
+ if (!DISABLE_TOUCH_SIDE_PAGES) {
+ if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) {
+ if (getChildCount() > 0) {
+ if (hitsPreviousPage(x, y)) {
+ mTouchState = TOUCH_STATE_PREV_PAGE;
+ } else if (hitsNextPage(x, y)) {
+ mTouchState = TOUCH_STATE_NEXT_PAGE;
+ }
}
}
}
@@ -949,10 +1099,7 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- mTouchState = TOUCH_STATE_REST;
- mAllowLongPress = false;
- mActivePointerId = INVALID_POINTER;
- releaseVelocityTracker();
+ resetTouchState();
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -982,9 +1129,17 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
* of the down event.
*/
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+
if (pointerIndex == -1) {
return;
}
+
+ // If we're only allowing edge swipes, we break out early if the down event wasn't
+ // at the edge.
+ if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) {
+ return;
+ }
+
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
final int xDiff = (int) Math.abs(x - mLastMotionX);
@@ -1002,38 +1157,36 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
mTotalMotionX += Math.abs(mLastMotionX - x);
mLastMotionX = x;
mLastMotionXRemainder = 0;
- mTouchX = getScrollX();
+ mTouchX = getViewportOffsetX() + getScrollX();
mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
pageBeginMoving();
}
- // Either way, cancel any pending longpress
- cancelCurrentPageLongPress();
}
}
- protected void cancelCurrentPageLongPress() {
- if (mAllowLongPress) {
- mAllowLongPress = false;
- // Try canceling the long press. It could also have been scheduled
- // by a distant descendant, so use the mAllowLongPress flag to block
- // everything
- final View currentPage = getPageAt(mCurrentPage);
- if (currentPage != null) {
- currentPage.cancelLongPress();
- }
- }
+ protected float getMaxScrollProgress() {
+ return 1.0f;
+ }
+
+ protected float getBoundedScrollProgress(int screenCenter, View v, int page) {
+ final int halfScreenSize = getViewportWidth() / 2;
+
+ screenCenter = Math.min(mScrollX + halfScreenSize, screenCenter);
+ screenCenter = Math.max(halfScreenSize, screenCenter);
+
+ return getScrollProgress(screenCenter, v, page);
}
protected float getScrollProgress(int screenCenter, View v, int page) {
- final int halfScreenSize = getMeasuredWidth() / 2;
+ final int halfScreenSize = getViewportWidth() / 2;
int totalDistance = getScaledMeasuredWidth(v) + mPageSpacing;
int delta = screenCenter - (getChildOffset(page) -
getRelativeChildOffset(page) + halfScreenSize);
float scrollProgress = delta / (totalDistance * 1.0f);
- scrollProgress = Math.min(scrollProgress, 1.0f);
- scrollProgress = Math.max(scrollProgress, -1.0f);
+ scrollProgress = Math.min(scrollProgress, getMaxScrollProgress());
+ scrollProgress = Math.max(scrollProgress, - getMaxScrollProgress());
return scrollProgress;
}
@@ -1045,7 +1198,7 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
}
protected void acceleratedOverScroll(float amount) {
- int screenSize = getMeasuredWidth();
+ int screenSize = getViewportWidth();
// We want to reach the max over scroll effect when the user has
// over scrolled half the size of the screen
@@ -1070,7 +1223,7 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
}
protected void dampedOverScroll(float amount) {
- int screenSize = getMeasuredWidth();
+ int screenSize = getViewportWidth();
float f = (amount / screenSize);
@@ -1130,9 +1283,22 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
// Remember where the motion event started
mDownMotionX = mLastMotionX = ev.getX();
+ mDownMotionY = mLastMotionY = ev.getY();
+ mDownScrollX = getScrollX();
+ float[] p = mapPointFromSelfToParent(mLastMotionX, mLastMotionY);
+ mParentDownMotionX = p[0];
+ mParentDownMotionY = p[1];
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
+
+ // Determine if the down event is within the threshold to be an edge swipe
+ int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize;
+ int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize;
+ if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) {
+ mDownEventOnEdge = true;
+ }
+
if (mTouchState == TOUCH_STATE_SCROLLING) {
pageBeginMoving();
}
@@ -1164,6 +1330,107 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
} else {
awakenScrollBars();
}
+ } else if (mTouchState == TOUCH_STATE_REORDERING) {
+ // Update the last motion position
+ mLastMotionX = ev.getX();
+ mLastMotionY = ev.getY();
+
+ // Update the parent down so that our zoom animations take this new movement into
+ // account
+ float[] pt = mapPointFromSelfToParent(mLastMotionX, mLastMotionY);
+ mParentDownMotionX = pt[0];
+ mParentDownMotionY = pt[1];
+ updateDragViewTranslationDuringDrag();
+
+ // Find the closest page to the touch point
+ final int dragViewIndex = indexOfChild(mDragView);
+ int bufferSize = (int) (REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE *
+ getViewportWidth());
+ int leftBufferEdge = (int) (mapPointFromSelfToParent(mViewport.left, 0)[0]
+ + bufferSize);
+ int rightBufferEdge = (int) (mapPointFromSelfToParent(mViewport.right, 0)[0]
+ - bufferSize);
+
+ if (DEBUG) Log.d(TAG, "leftBufferEdge: " + leftBufferEdge);
+ if (DEBUG) Log.d(TAG, "rightBufferEdge: " + rightBufferEdge);
+ if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX);
+ if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY);
+ if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX);
+ if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);
+
+ float parentX = mParentDownMotionX;
+ int pageIndexToSnapTo = -1;
+ if (parentX < leftBufferEdge && dragViewIndex > 0) {
+ pageIndexToSnapTo = dragViewIndex - 1;
+ } else if (parentX > rightBufferEdge && dragViewIndex < getChildCount() - 1) {
+ pageIndexToSnapTo = dragViewIndex + 1;
+ }
+
+ final int pageUnderPointIndex = pageIndexToSnapTo;
+ if (pageUnderPointIndex > -1) {
+ mTempVisiblePagesRange[0] = 0;
+ mTempVisiblePagesRange[1] = getPageCount() - 1;
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ if (mTempVisiblePagesRange[0] <= pageUnderPointIndex &&
+ pageUnderPointIndex <= mTempVisiblePagesRange[1] &&
+ pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
+ mSidePageHoverIndex = pageUnderPointIndex;
+ mSidePageHoverRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Update the down scroll position to account for the fact that the
+ // current page is moved
+ mDownScrollX = getChildOffset(pageUnderPointIndex)
+ - getRelativeChildOffset(pageUnderPointIndex);
+
+ // Setup the scroll to the correct page before we swap the views
+ snapToPage(pageUnderPointIndex);
+
+ // For each of the pages between the paged view and the drag view,
+ // animate them from the previous position to the new position in
+ // the layout (as a result of the drag view moving in the layout)
+ int shiftDelta = (dragViewIndex < pageUnderPointIndex) ? -1 : 1;
+ int lowerIndex = (dragViewIndex < pageUnderPointIndex) ?
+ dragViewIndex + 1 : pageUnderPointIndex;
+ int upperIndex = (dragViewIndex > pageUnderPointIndex) ?
+ dragViewIndex - 1 : pageUnderPointIndex;
+ for (int i = lowerIndex; i <= upperIndex; ++i) {
+ View v = getChildAt(i);
+ // dragViewIndex < pageUnderPointIndex, so after we remove the
+ // drag view all subsequent views to pageUnderPointIndex will
+ // shift down.
+ int oldX = getViewportOffsetX() + getChildOffset(i);
+ int newX = getViewportOffsetX() + getChildOffset(i + shiftDelta);
+
+ // Animate the view translation from its old position to its new
+ // position
+ AnimatorSet anim = (AnimatorSet) v.getTag();
+ if (anim != null) {
+ anim.cancel();
+ }
+
+ v.setTranslationX(oldX - newX);
+ anim = new AnimatorSet();
+ anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION);
+ anim.playTogether(
+ ObjectAnimator.ofFloat(v, "translationX", 0f));
+ anim.start();
+ v.setTag(anim);
+ }
+
+ removeView(mDragView);
+ onRemoveView(mDragView);
+ addView(mDragView, pageUnderPointIndex);
+ onAddView(mDragView, pageUnderPointIndex);
+ mSidePageHoverIndex = -1;
+ }
+ };
+ postDelayed(mSidePageHoverRunnable, REORDERING_SIDE_PAGE_HOVER_TIMEOUT);
+ }
+ } else {
+ removeCallbacks(mSidePageHoverRunnable);
+ mSidePageHoverIndex = -1;
+ }
} else {
determineScrollingStart(ev);
}
@@ -1232,21 +1499,29 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
} else {
snapToDestination();
}
+ } else if (mTouchState == TOUCH_STATE_REORDERING) {
+ if (!DISABLE_FLING_TO_DELETE) {
+ // Check the velocity and see if we are flinging-to-delete
+ PointF flingToDeleteVector = isFlingingToDelete();
+ if (flingToDeleteVector != null) {
+ onFlingToDelete(flingToDeleteVector);
+ }
+ }
} else {
onUnhandledTap(ev);
}
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- releaseVelocityTracker();
+
+ // Remove the callback to wait for the side page hover timeout
+ removeCallbacks(mSidePageHoverRunnable);
+ // End any intermediate reordering states
+ resetTouchState();
break;
case MotionEvent.ACTION_CANCEL:
if (mTouchState == TOUCH_STATE_SCROLLING) {
snapToDestination();
}
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- releaseVelocityTracker();
+ resetTouchState();
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -1257,6 +1532,20 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
return true;
}
+ //public abstract void onFlingToDelete(View v);
+ public abstract void onRemoveView(View v);
+ public abstract void onAddView(View v, int index);
+
+ private void resetTouchState() {
+ releaseVelocityTracker();
+ endReordering();
+ mTouchState = TOUCH_STATE_REST;
+ mActivePointerId = INVALID_POINTER;
+ mDownEventOnEdge = false;
+ }
+
+ protected void onUnhandledTap(MotionEvent ev) {}
+
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
@@ -1319,8 +1608,6 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
}
}
- protected void onUnhandledTap(MotionEvent ev) {}
-
@Override
public void requestChildFocus(View child, View focused) {
super.requestChildFocus(child, focused);
@@ -1352,16 +1639,28 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
return (minWidth > measuredWidth) ? minWidth : measuredWidth;
}
+ int getPageNearestToPoint(float x) {
+ int index = 0;
+ for (int i = 0; i < getChildCount(); ++i) {
+ if (x < getChildAt(i).getRight() - getScrollX()) {
+ return index;
+ } else {
+ index++;
+ }
+ }
+ return Math.min(index, getChildCount() - 1);
+ }
+
int getPageNearestToCenterOfScreen() {
int minDistanceFromScreenCenter = Integer.MAX_VALUE;
int minDistanceFromScreenCenterIndex = -1;
- int screenCenter = getScrollX() + (getMeasuredWidth() / 2);
+ int screenCenter = getViewportOffsetX() + getScrollX() + (getViewportWidth() / 2);
final int childCount = getChildCount();
for (int i = 0; i < childCount; ++i) {
View layout = (View) getPageAt(i);
int childWidth = getScaledMeasuredWidth(layout);
int halfChildWidth = (childWidth / 2);
- int childCenter = getChildOffset(i) + halfChildWidth;
+ int childCenter = getViewportOffsetX() + getChildOffset(i) + halfChildWidth;
int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
minDistanceFromScreenCenter = distanceFromScreenCenter;
@@ -1397,11 +1696,11 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
protected void snapToPageWithVelocity(int whichPage, int velocity) {
whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
- int halfScreenSize = getMeasuredWidth() / 2;
+ int halfScreenSize = getViewportWidth() / 2;
if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): "
- + getMeasuredWidth() + ", " + getChildWidth(whichPage));
+ + getViewportWidth() + ", " + getChildWidth(whichPage));
final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
int delta = newX - mUnboundedScrollX;
int duration = 0;
@@ -1435,20 +1734,29 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
protected void snapToPage(int whichPage) {
snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
}
+ protected void snapToPageImmediately(int whichPage) {
+ snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true);
+ }
protected void snapToPage(int whichPage, int duration) {
+ snapToPage(whichPage, duration, false);
+ }
+ protected void snapToPage(int whichPage, int duration, boolean immediate) {
whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage));
- if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getMeasuredWidth() + ", "
+ if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getViewportWidth() + ", "
+ getChildWidth(whichPage));
int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
final int sX = mUnboundedScrollX;
final int delta = newX - sX;
- snapToPage(whichPage, delta, duration);
+ snapToPage(whichPage, delta, duration, immediate);
}
protected void snapToPage(int whichPage, int delta, int duration) {
+ snapToPage(whichPage, delta, duration, false);
+ }
+ protected void snapToPage(int whichPage, int delta, int duration, boolean immediate) {
mNextPage = whichPage;
View focusedChild = getFocusedChild();
@@ -1459,7 +1767,9 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
pageBeginMoving();
awakenScrollBars(duration);
- if (duration == 0) {
+ if (immediate) {
+ duration = 0;
+ } else if (duration == 0) {
duration = Math.abs(delta);
}
@@ -1467,6 +1777,12 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
notifyPageSwitchListener();
+
+ // Trigger a compute() to finish switching pages if necessary
+ if (immediate) {
+ computeScroll();
+ }
+
invalidate();
}
@@ -1500,21 +1816,6 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
return result;
}
- /**
- * @return True is long presses are still allowed for the current touch
- */
- public boolean allowLongPress() {
- return mAllowLongPress;
- }
-
- /**
- * Set true to allow long-press events to be triggered, usually checked by
- * {@link Launcher} to accept or block dpad-initiated long-presses.
- */
- public void setAllowLongPress(boolean allowLongPress) {
- mAllowLongPress = allowLongPress;
- }
-
public static class SavedState extends BaseSavedState {
int currentPage = -1;
@@ -1653,7 +1954,7 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
if (!isScrollingIndicatorEnabled()) return;
if (mScrollIndicator == null) return;
int numPages = getChildCount();
- int pageWidth = getMeasuredWidth();
+ int pageWidth = getViewportWidth();
int lastChildIndex = Math.max(0, getChildCount() - 1);
int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex);
int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight;
@@ -1675,6 +1976,325 @@ public class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeL
mScrollIndicator.setTranslationX(indicatorPos);
}
+ // Animate the drag view back to the original position
+ void animateDragViewToOriginalPosition() {
+ if (mDragView != null) {
+ AnimatorSet anim = new AnimatorSet();
+ anim.setDuration(REORDERING_DROP_REPOSITION_DURATION);
+ anim.playTogether(
+ ObjectAnimator.ofFloat(mDragView, "translationX", 0f),
+ ObjectAnimator.ofFloat(mDragView, "translationY", 0f));
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onPostReorderingAnimationCompleted();
+ }
+ });
+ anim.start();
+ }
+ }
+
+ // "Zooms out" the PagedView to reveal more side pages
+ boolean zoomOut() {
+ if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
+ mZoomInOutAnim.cancel();
+ }
+
+ if (!(getScaleX() < 1f || getScaleY() < 1f)) {
+ mZoomInOutAnim = new AnimatorSet();
+ mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
+ mZoomInOutAnim.playTogether(
+ ObjectAnimator.ofFloat(this, "scaleX", mMinScale),
+ ObjectAnimator.ofFloat(this, "scaleY", mMinScale));
+ mZoomInOutAnim.start();
+ return true;
+ }
+ return false;
+ }
+
+ protected void onStartReordering() {
+ // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.)
+ mTouchState = TOUCH_STATE_REORDERING;
+ mIsReordering = true;
+
+ // We must invalidate to trigger a redraw to update the layers such that the drag view
+ // is always drawn on top
+ invalidate();
+ }
+
+ private void onPostReorderingAnimationCompleted() {
+ // Trigger the callback when reordering has settled
+ --mPostReorderingPreZoomInRemainingAnimationCount;
+ if (mPostReorderingPreZoomInRunnable != null &&
+ mPostReorderingPreZoomInRemainingAnimationCount == 0) {
+ mPostReorderingPreZoomInRunnable.run();
+ mPostReorderingPreZoomInRunnable = null;
+ }
+ }
+
+ protected void onEndReordering() {
+ mIsReordering = false;
+ }
+
+ public boolean startReordering() {
+ int dragViewIndex = getPageNearestToCenterOfScreen();
+ mTempVisiblePagesRange[0] = 0;
+ mTempVisiblePagesRange[1] = getPageCount() - 1;
+ boundByReorderablePages(true, mTempVisiblePagesRange);
+ mReorderingStarted = true;
+
+ // Check if we are within the reordering range
+ if (mTempVisiblePagesRange[0] <= dragViewIndex &&
+ dragViewIndex <= mTempVisiblePagesRange[1]) {
+ if (zoomOut()) {
+ // Find the drag view under the pointer
+ mDragView = getChildAt(dragViewIndex);
+
+ onStartReordering();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ boolean isReordering(boolean testTouchState) {
+ boolean state = mIsReordering;
+ if (testTouchState) {
+ state &= (mTouchState == TOUCH_STATE_REORDERING);
+ }
+ return state;
+ }
+ void endReordering() {
+ // For simplicity, we call endReordering sometimes even if reordering was never started.
+ // In that case, we don't want to do anything.
+ if (!mReorderingStarted) return;
+ mReorderingStarted = false;
+
+ // If we haven't flung-to-delete the current child, then we just animate the drag view
+ // back into position
+ if (!mIsFlingingToDelete) {
+ mPostReorderingPreZoomInRunnable = new Runnable() {
+ public void run() {
+ Runnable onCompleteRunnable = new Runnable() {
+ @Override
+ public void run() {
+ onEndReordering();
+ }
+ };
+ zoomIn(onCompleteRunnable);
+ };
+ };
+
+ mPostReorderingPreZoomInRemainingAnimationCount =
+ NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT;
+ // Snap to the current page
+ snapToPage(indexOfChild(mDragView), 0);
+ // Animate the drag view back to the front position
+ animateDragViewToOriginalPosition();
+ }
+ }
+
+ // "Zooms in" the PagedView to highlight the current page
+ boolean zoomIn(final Runnable onCompleteRunnable) {
+ if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
+ mZoomInOutAnim.cancel();
+ }
+ if (getScaleX() < 1f || getScaleY() < 1f) {
+ mZoomInOutAnim = new AnimatorSet();
+ mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION);
+ mZoomInOutAnim.playTogether(
+ ObjectAnimator.ofFloat(this, "scaleX", 1f),
+ ObjectAnimator.ofFloat(this, "scaleY", 1f));
+ mZoomInOutAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mDragView = null;
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDragView = null;
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ }
+ });
+ mZoomInOutAnim.start();
+ return true;
+ } else {
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ }
+ return false;
+ }
+
+ /*
+ * Flinging to delete - IN PROGRESS
+ */
+ private PointF isFlingingToDelete() {
+ ViewConfiguration config = ViewConfiguration.get(getContext());
+ mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
+
+ if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
+ // Do a quick dot product test to ensure that we are flinging upwards
+ PointF vel = new PointF(mVelocityTracker.getXVelocity(),
+ mVelocityTracker.getYVelocity());
+ PointF upVec = new PointF(0f, -1f);
+ float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
+ (vel.length() * upVec.length()));
+ if (theta <= Math.toRadians(FLING_TO_DELETE_MAX_FLING_DEGREES)) {
+ return vel;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Creates an animation from the current drag view along its current velocity vector.
+ * For this animation, the alpha runs for a fixed duration and we update the position
+ * progressively.
+ */
+ private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener {
+ private View mDragView;
+ private PointF mVelocity;
+ private Rect mFrom;
+ private long mPrevTime;
+ private float mFriction;
+
+ private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f);
+
+ public FlingAlongVectorAnimatorUpdateListener(View dragView, PointF vel, Rect from,
+ long startTime, float friction) {
+ mDragView = dragView;
+ mVelocity = vel;
+ mFrom = from;
+ mPrevTime = startTime;
+ mFriction = 1f - (mDragView.getResources().getDisplayMetrics().density * friction);
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = ((Float) animation.getAnimatedValue()).floatValue();
+ long curTime = AnimationUtils.currentAnimationTimeMillis();
+
+ mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f);
+ mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f);
+
+ mDragView.setTranslationX(mFrom.left);
+ mDragView.setTranslationY(mFrom.top);
+ mDragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t));
+
+ mVelocity.x *= mFriction;
+ mVelocity.y *= mFriction;
+ mPrevTime = curTime;
+ }
+ };
+
+ public void onFlingToDelete(PointF vel) {
+ final long startTime = AnimationUtils.currentAnimationTimeMillis();
+
+ // NOTE: Because it takes time for the first frame of animation to actually be
+ // called and we expect the animation to be a continuation of the fling, we have
+ // to account for the time that has elapsed since the fling finished. And since
+ // we don't have a startDelay, we will always get call to update when we call
+ // start() (which we want to ignore).
+ final TimeInterpolator tInterpolator = new TimeInterpolator() {
+ private int mCount = -1;
+ private long mStartTime;
+ private float mOffset;
+ /* Anonymous inner class ctor */ {
+ mStartTime = startTime;
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ if (mCount < 0) {
+ mCount++;
+ } else if (mCount == 0) {
+ mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() -
+ mStartTime) / FLING_TO_DELETE_FADE_OUT_DURATION);
+ mCount++;
+ }
+ return Math.min(1f, mOffset + t);
+ }
+ };
+
+ final Rect from = new Rect();
+ final View dragView = mDragView;
+ from.left = (int) dragView.getTranslationX();
+ from.top = (int) dragView.getTranslationY();
+ AnimatorUpdateListener updateCb = new FlingAlongVectorAnimatorUpdateListener(dragView, vel,
+ from, startTime, FLING_TO_DELETE_FRICTION);
+
+ final Runnable onAnimationEndRunnable = new Runnable() {
+ @Override
+ public void run() {
+ int dragViewIndex = indexOfChild(dragView);
+ // Setup the scroll to the correct page before we swap the views
+ snapToPageImmediately(dragViewIndex - 1);
+
+ // For each of the pages around the drag view, animate them from the previous
+ // position to the new position in the layout (as a result of the drag view moving
+ // in the layout)
+ // NOTE: We can make an assumption here because we have side-bound pages that we
+ // will always have pages to animate in from the left
+ int lowerIndex = 0;
+ int upperIndex = dragViewIndex - 1;
+ for (int i = lowerIndex; i <= upperIndex; ++i) {
+ View v = getChildAt(i);
+ // dragViewIndex < pageUnderPointIndex, so after we remove the
+ // drag view all subsequent views to pageUnderPointIndex will
+ // shift down.
+ int oldX = 0;
+ if (i == 0) {
+ oldX = -(getViewportOffsetX() + getChildOffset(i));
+ } else {
+ oldX = getViewportOffsetX() + getChildOffset(i - 1);
+ }
+ int newX = getViewportOffsetX() + getChildOffset(i);
+
+ // Animate the view translation from its old position to its new
+ // position
+ AnimatorSet anim = (AnimatorSet) v.getTag();
+ if (anim != null) {
+ anim.cancel();
+ }
+
+ v.setTranslationX(oldX - newX);
+ anim = new AnimatorSet();
+ anim.setDuration(FLING_TO_DELETE_SLIDE_IN_SIDE_PAGE_DURATION);
+ anim.playTogether(
+ ObjectAnimator.ofFloat(v, "translationX", 0f));
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsFlingingToDelete = false;
+ }
+ });
+ anim.start();
+ v.setTag(anim);
+ }
+
+ removeView(dragView);
+ onRemoveView(dragView);
+ }
+ };
+
+ // Create and start the animation
+ ValueAnimator mDropAnim = new ValueAnimator();
+ mDropAnim.setInterpolator(tInterpolator);
+ mDropAnim.setDuration(FLING_TO_DELETE_FADE_OUT_DURATION);
+ mDropAnim.setFloatValues(0f, 1f);
+ mDropAnim.addUpdateListener(updateCb);
+ mDropAnim.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ onAnimationEndRunnable.run();
+ }
+ });
+ mDropAnim.start();
+ mIsFlingingToDelete = true;
+ }
+
/* Accessibility */
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java b/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java
index b57d8c13af96..ec6472faeea7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java
@@ -22,4 +22,6 @@ public interface SecurityMessageDisplay {
public void setMessage(int resId, boolean important);
public void setMessage(int resId, boolean important, Object... formatArgs);
+
+ public void setTimeout(int timeout_ms);
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
new file mode 100644
index 000000000000..35eccbb1e805
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
@@ -0,0 +1,1014 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Log;
+import android.util.Property;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+import android.widget.Scroller;
+
+import com.android.internal.R;
+
+/**
+ * This layout handles interaction with the sliding security challenge views
+ * that overlay/resize other keyguard contents.
+ */
+public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout {
+ private static final String TAG = "SlidingChallengeLayout";
+ private static final boolean DEBUG = false;
+
+ // The drag handle is measured in dp above & below the top edge of the
+ // challenge view; these parameters change based on whether the challenge
+ // is open or closed.
+ private static final int DRAG_HANDLE_CLOSED_ABOVE = 64; // dp
+ private static final int DRAG_HANDLE_CLOSED_BELOW = 0; // dp
+ private static final int DRAG_HANDLE_OPEN_ABOVE = 8; // dp
+ private static final int DRAG_HANDLE_OPEN_BELOW = 0; // dp
+
+ private static final boolean OPEN_ON_CLICK = true;
+
+ private static final int HANDLE_ANIMATE_DURATION = 200; // ms
+
+ // Drawn to show the drag handle in closed state; crossfades to the challenge view
+ // when challenge is fully visible
+ private Drawable mHandleDrawable;
+ private Drawable mFrameDrawable;
+ private Drawable mDragIconDrawable;
+
+ // Initialized during measurement from child layoutparams
+ private View mChallengeView;
+ private View mScrimView;
+
+ // Range: 0 (fully hidden) to 1 (fully visible)
+ private float mChallengeOffset = 1.f;
+ private boolean mChallengeShowing = true;
+ private boolean mIsBouncing = false;
+
+ private final Scroller mScroller;
+ private int mScrollState;
+ private OnChallengeScrolledListener mScrollListener;
+ private OnBouncerStateChangedListener mBouncerListener;
+
+ public static final int SCROLL_STATE_IDLE = 0;
+ public static final int SCROLL_STATE_DRAGGING = 1;
+ public static final int SCROLL_STATE_SETTLING = 2;
+
+ private static final int MAX_SETTLE_DURATION = 600; // ms
+
+ // ID of the pointer in charge of a current drag
+ private int mActivePointerId = INVALID_POINTER;
+ private static final int INVALID_POINTER = -1;
+
+ // True if the user is currently dragging the slider
+ private boolean mDragging;
+ // True if the user may not drag until a new gesture begins
+ private boolean mBlockDrag;
+
+ private VelocityTracker mVelocityTracker;
+ private int mMinVelocity;
+ private int mMaxVelocity;
+ private float mGestureStartX, mGestureStartY; // where did you first touch the screen?
+ private int mGestureStartChallengeBottom; // where was the challenge at that time?
+
+ private int mDragHandleClosedBelow; // handle hitrect extension into the challenge view
+ private int mDragHandleClosedAbove; // extend the handle's hitrect this far above the line
+ private int mDragHandleOpenBelow; // handle hitrect extension into the challenge view
+ private int mDragHandleOpenAbove; // extend the handle's hitrect this far above the line
+
+ private int mDragHandleEdgeSlop;
+ private int mChallengeBottomBound; // Number of pixels from the top of the challenge view
+ // that should remain on-screen
+
+ private int mTouchSlop;
+
+ float mHandleAlpha;
+ float mFrameAlpha;
+ private ObjectAnimator mHandleAnimation;
+ private ObjectAnimator mFrameAnimation;
+
+ static final Property<SlidingChallengeLayout, Float> HANDLE_ALPHA =
+ new FloatProperty<SlidingChallengeLayout>("handleAlpha") {
+ @Override
+ public void setValue(SlidingChallengeLayout view, float value) {
+ view.mHandleAlpha = value;
+ view.invalidate();
+ }
+
+ @Override
+ public Float get(SlidingChallengeLayout view) {
+ return view.mHandleAlpha;
+ }
+ };
+
+ static final Property<SlidingChallengeLayout, Float> FRAME_ALPHA =
+ new FloatProperty<SlidingChallengeLayout>("frameAlpha") {
+ @Override
+ public void setValue(SlidingChallengeLayout view, float value) {
+ if (view.mFrameDrawable != null) {
+ view.mFrameAlpha = value;
+ view.mFrameDrawable.setAlpha((int) (value * 0xFF));
+ view.mFrameDrawable.invalidateSelf();
+ }
+ }
+
+ @Override
+ public Float get(SlidingChallengeLayout view) {
+ return view.mFrameAlpha;
+ }
+ };
+
+ // True if at least one layout pass has happened since the view was attached.
+ private boolean mHasLayout;
+
+ private static final Interpolator sMotionInterpolator = new Interpolator() {
+ public float getInterpolation(float t) {
+ t -= 1.0f;
+ return t * t * t * t * t + 1.0f;
+ }
+ };
+
+ private static final Interpolator sHandleFadeInterpolator = new Interpolator() {
+ public float getInterpolation(float t) {
+ return t * t;
+ }
+ };
+
+ private final Runnable mEndScrollRunnable = new Runnable () {
+ public void run() {
+ completeChallengeScroll();
+ }
+ };
+
+ private final OnClickListener mScrimClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ hideBouncer();
+ }
+ };
+
+ /**
+ * Listener interface that reports changes in scroll state of the challenge area.
+ */
+ public interface OnChallengeScrolledListener {
+ /**
+ * The scroll state itself changed.
+ *
+ * <p>scrollState will be one of the following:</p>
+ *
+ * <ul>
+ * <li><code>SCROLL_STATE_IDLE</code> - The challenge area is stationary.</li>
+ * <li><code>SCROLL_STATE_DRAGGING</code> - The user is actively dragging
+ * the challenge area.</li>
+ * <li><code>SCROLL_STATE_SETTLING</code> - The challenge area is animating
+ * into place.</li>
+ * </ul>
+ *
+ * <p>Do not perform expensive operations (e.g. layout)
+ * while the scroll state is not <code>SCROLL_STATE_IDLE</code>.</p>
+ *
+ * @param scrollState The new scroll state of the challenge area.
+ */
+ public void onScrollStateChanged(int scrollState);
+
+ /**
+ * The precise position of the challenge area has changed.
+ *
+ * <p>NOTE: It is NOT safe to modify layout or call any View methods that may
+ * result in a requestLayout anywhere in your view hierarchy as a result of this call.
+ * It may be called during drawing.</p>
+ *
+ * @param scrollPosition New relative position of the challenge area.
+ * 1.f = fully visible/ready to be interacted with.
+ * 0.f = fully invisible/inaccessible to the user.
+ * @param challengeTop Position of the top edge of the challenge view in px in the
+ * SlidingChallengeLayout's coordinate system.
+ */
+ public void onScrollPositionChanged(float scrollPosition, int challengeTop);
+ }
+
+ public SlidingChallengeLayout(Context context) {
+ this(context, null);
+ }
+
+ public SlidingChallengeLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SlidingChallengeLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.SlidingChallengeLayout, defStyle, 0);
+ setDragDrawables(a.getDrawable(R.styleable.SlidingChallengeLayout_dragHandle),
+ a.getDrawable(R.styleable.SlidingChallengeLayout_dragIcon));
+
+ a.recycle();
+
+ mScroller = new Scroller(context, sMotionInterpolator);
+
+ final ViewConfiguration vc = ViewConfiguration.get(context);
+ mMinVelocity = vc.getScaledMinimumFlingVelocity();
+ mMaxVelocity = vc.getScaledMaximumFlingVelocity();
+
+ mDragHandleEdgeSlop = getResources().getDimensionPixelSize(
+ R.dimen.kg_edge_swipe_region_size);
+
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+
+ final float density = getResources().getDisplayMetrics().density;
+
+ // top half of the lock icon, plus another 25% to be sure
+ mDragHandleClosedAbove = (int) (DRAG_HANDLE_CLOSED_ABOVE * density + 0.5f);
+ mDragHandleClosedBelow = (int) (DRAG_HANDLE_CLOSED_BELOW * density + 0.5f);
+ mDragHandleOpenAbove = (int) (DRAG_HANDLE_OPEN_ABOVE * density + 0.5f);
+ mDragHandleOpenBelow = (int) (DRAG_HANDLE_OPEN_BELOW * density + 0.5f);
+
+ // how much space to account for in the handle when closed
+ mChallengeBottomBound = mDragHandleClosedBelow;
+
+ setWillNotDraw(false);
+ }
+
+ public void setDragDrawables(Drawable handle, Drawable icon) {
+ mHandleDrawable = handle;
+ mDragIconDrawable = icon;
+ }
+
+ public void setDragIconDrawable(Drawable d) {
+ mDragIconDrawable = d;
+ }
+
+ public void showHandle(boolean visible) {
+ if (visible) {
+ if (mHandleAnimation != null) {
+ mHandleAnimation.cancel();
+ mHandleAnimation = null;
+ }
+ mHandleAlpha = 1.f;
+ invalidate();
+ } else {
+ animateHandle(false);
+ }
+ }
+
+ void animateHandle(boolean visible) {
+ if (mHandleAnimation != null) {
+ mHandleAnimation.cancel();
+ mHandleAnimation = null;
+ }
+ final float targetAlpha = visible ? 1.f : 0.f;
+ if (targetAlpha == mHandleAlpha) {
+ return;
+ }
+ mHandleAnimation = ObjectAnimator.ofFloat(this, HANDLE_ALPHA, targetAlpha);
+ mHandleAnimation.setInterpolator(sHandleFadeInterpolator);
+ mHandleAnimation.setDuration(HANDLE_ANIMATE_DURATION);
+ mHandleAnimation.start();
+ }
+
+ void animateFrame(boolean visible, boolean full) {
+ if (mFrameDrawable == null) return;
+
+ if (mFrameAnimation != null) {
+ mFrameAnimation.cancel();
+ mFrameAnimation = null;
+ }
+ final float targetAlpha = visible ? (full ? 1.f : 0.5f) : 0.f;
+ if (targetAlpha == mFrameAlpha) {
+ return;
+ }
+
+ mFrameAnimation = ObjectAnimator.ofFloat(this, FRAME_ALPHA, targetAlpha);
+ mFrameAnimation.setInterpolator(sHandleFadeInterpolator);
+ mFrameAnimation.setDuration(HANDLE_ANIMATE_DURATION);
+ mFrameAnimation.start();
+ }
+
+ private void sendInitialListenerUpdates() {
+ if (mScrollListener != null) {
+ int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0;
+ mScrollListener.onScrollPositionChanged(mChallengeOffset, challengeTop);
+ mScrollListener.onScrollStateChanged(mScrollState);
+ }
+ }
+
+ public void setOnChallengeScrolledListener(OnChallengeScrolledListener listener) {
+ mScrollListener = listener;
+ if (mHasLayout) {
+ sendInitialListenerUpdates();
+ }
+ }
+
+ public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
+ mBouncerListener = listener;
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mHasLayout = false;
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ removeCallbacks(mEndScrollRunnable);
+ mHasLayout = false;
+ }
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ if (mIsBouncing && child != mChallengeView) {
+ // Clear out of the bouncer if the user tries to move focus outside of
+ // the security challenge view.
+ hideBouncer();
+ }
+ super.requestChildFocus(child, focused);
+ }
+
+ // We want the duration of the page snap animation to be influenced by the distance that
+ // the screen has to travel, however, we don't want this duration to be effected in a
+ // purely linear fashion. Instead, we use this method to moderate the effect that the distance
+ // of travel has on the overall snap duration.
+ float distanceInfluenceForSnapDuration(float f) {
+ f -= 0.5f; // center the values about 0.
+ f *= 0.3f * Math.PI / 2.0f;
+ return (float) Math.sin(f);
+ }
+
+ void setScrollState(int state) {
+ if (mScrollState != state) {
+ mScrollState = state;
+
+ animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing);
+ animateFrame(false , false);
+ if (mScrollListener != null) {
+ mScrollListener.onScrollStateChanged(state);
+ }
+ }
+ }
+
+ void completeChallengeScroll() {
+ setChallengeShowing(mChallengeOffset != 0);
+ setScrollState(SCROLL_STATE_IDLE);
+ }
+
+ void setScrimView(View scrim) {
+ if (mScrimView != null) {
+ mScrimView.setOnClickListener(null);
+ }
+ mScrimView = scrim;
+ mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE);
+ mScrimView.setFocusable(true);
+ mScrimView.setOnClickListener(mScrimClickListener);
+ }
+
+ /**
+ * Animate the bottom edge of the challenge view to the given position.
+ *
+ * @param y desired final position for the bottom edge of the challenge view in px
+ * @param velocity velocity in
+ */
+ void animateChallengeTo(int y, int velocity) {
+ if (mChallengeView == null) {
+ // Nothing to do.
+ return;
+ }
+ final int sy = mChallengeView.getBottom();
+ final int dy = y - sy;
+ if (dy == 0) {
+ completeChallengeScroll();
+ return;
+ }
+
+ setScrollState(SCROLL_STATE_SETTLING);
+
+ final int childHeight = mChallengeView.getHeight();
+ final int halfHeight = childHeight / 2;
+ final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / childHeight);
+ final float distance = halfHeight + halfHeight *
+ distanceInfluenceForSnapDuration(distanceRatio);
+
+ int duration = 0;
+ velocity = Math.abs(velocity);
+ if (velocity > 0) {
+ duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
+ } else {
+ final float childDelta = (float) Math.abs(dy) / childHeight;
+ duration = (int) ((childDelta + 1) * 100);
+ }
+ duration = Math.min(duration, MAX_SETTLE_DURATION);
+
+ mScroller.startScroll(0, sy, 0, dy, duration);
+ postInvalidateOnAnimation();
+ }
+
+ private void setChallengeShowing(boolean showChallenge) {
+ mChallengeShowing = showChallenge;
+ }
+
+ /**
+ * @return true if the challenge is at all visible.
+ */
+ public boolean isChallengeShowing() {
+ return mChallengeShowing;
+ }
+
+ @Override
+ public boolean isChallengeOverlapping() {
+ return mChallengeShowing;
+ }
+
+ @Override
+ public boolean isBouncing() {
+ return mIsBouncing;
+ }
+
+ @Override
+ public void showBouncer() {
+ if (mIsBouncing) return;
+ showChallenge(true);
+ mIsBouncing = true;
+ if (mScrimView != null) {
+ mScrimView.setVisibility(VISIBLE);
+ }
+ animateFrame(true, true);
+ if (mBouncerListener != null) {
+ mBouncerListener.onBouncerStateChanged(true);
+ }
+ }
+
+ @Override
+ public void hideBouncer() {
+ if (!mIsBouncing) return;
+ setChallengeShowing(false);
+ mIsBouncing = false;
+ if (mScrimView != null) {
+ mScrimView.setVisibility(GONE);
+ }
+ animateFrame(false, false);
+ if (mBouncerListener != null) {
+ mBouncerListener.onBouncerStateChanged(false);
+ }
+ }
+
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean allowIntercept) {
+ // We'll intercept whoever we feel like! ...as long as it isn't a challenge view.
+ // If there are one or more pointers in the challenge view before we take over
+ // touch events, onInterceptTouchEvent will set mBlockDrag.
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ //Log.v(TAG, "onIntercept: " + ev);
+
+ final int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mGestureStartX = ev.getX();
+ mGestureStartY = ev.getY();
+ mBlockDrag = false;
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ resetTouch();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ final float x = ev.getX(i);
+ final float y = ev.getY(i);
+
+ if (!mIsBouncing &&
+ (isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
+ (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) &&
+ mActivePointerId == INVALID_POINTER) {
+ mActivePointerId = ev.getPointerId(i);
+ mGestureStartX = x;
+ mGestureStartY = y;
+ mGestureStartChallengeBottom = getChallengeBottom();
+ mDragging = true;
+ } else if (isInChallengeView(x, y)) {
+ mBlockDrag = true;
+ }
+ }
+ break;
+ }
+
+ if (mBlockDrag) {
+ mActivePointerId = INVALID_POINTER;
+ mDragging = false;
+ }
+
+ return mDragging;
+ }
+
+ private void resetTouch() {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ mActivePointerId = INVALID_POINTER;
+ mDragging = mBlockDrag = false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ final int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mBlockDrag = false;
+ mGestureStartX = ev.getX();
+ mGestureStartY = ev.getY();
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ if (mDragging) {
+ showChallenge(0);
+ }
+ resetTouch();
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) {
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ if (OPEN_ON_CLICK
+ && isInDragHandle(mGestureStartX, mGestureStartY)
+ && Math.abs(ev.getX() - mGestureStartX) <= mTouchSlop
+ && Math.abs(ev.getY() - mGestureStartY) <= mTouchSlop) {
+ showChallenge(true);
+ } else if (mDragging) {
+ mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
+ showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId));
+ }
+ resetTouch();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (!mDragging && !mBlockDrag && !mIsBouncing) {
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ final float x = ev.getX(i);
+ final float y = ev.getY(i);
+
+ if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
+ (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING))
+ && mActivePointerId == INVALID_POINTER) {
+ mGestureStartX = x;
+ mGestureStartY = y;
+ mActivePointerId = ev.getPointerId(i);
+ mGestureStartChallengeBottom = getChallengeBottom();
+ mDragging = true;
+ break;
+ }
+ }
+ }
+ // Not an else; this can be set above.
+ if (mDragging) {
+ // No-op if already in this state, but set it here in case we arrived
+ // at this point from either intercept or the above.
+ setScrollState(SCROLL_STATE_DRAGGING);
+
+ final int index = ev.findPointerIndex(mActivePointerId);
+ if (index < 0) {
+ // Oops, bogus state. We lost some touch events somewhere.
+ // Just drop it with no velocity and let things settle.
+ resetTouch();
+ showChallenge(0);
+ return true;
+ }
+ final float y = ev.getY(index);
+ final float pos = Math.min(y - mGestureStartY,
+ getLayoutBottom() - mChallengeBottomBound);
+
+ moveChallengeTo(mGestureStartChallengeBottom + (int) pos);
+ }
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * We only want to add additional vertical space to the drag handle when the panel is fully
+ * closed.
+ */
+ private int getDragHandleSizeAbove() {
+ return isChallengeShowing() ? mDragHandleOpenAbove : mDragHandleClosedAbove;
+ }
+ private int getDragHandleSizeBelow() {
+ return isChallengeShowing() ? mDragHandleOpenBelow : mDragHandleClosedBelow;
+ }
+
+ private boolean isInChallengeView(float x, float y) {
+ if (mChallengeView == null) return false;
+
+ return x >= mChallengeView.getLeft() && y >= mChallengeView.getTop() &&
+ x < mChallengeView.getRight() && y < mChallengeView.getBottom();
+ }
+
+ private boolean isInDragHandle(float x, float y) {
+ if (mChallengeView == null) return false;
+
+ return x >= mDragHandleEdgeSlop &&
+ y >= mChallengeView.getTop() - getDragHandleSizeAbove() &&
+ x < getWidth() - mDragHandleEdgeSlop &&
+ y < mChallengeView.getTop() + getDragHandleSizeBelow();
+ }
+
+ private boolean crossedDragHandle(float x, float y, float initialY) {
+ final int challengeTop = mChallengeView.getTop();
+ return x >= 0 &&
+ x < getWidth() &&
+ initialY < (challengeTop - getDragHandleSizeAbove()) &&
+ y > challengeTop + getDragHandleSizeBelow();
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec) {
+ if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
+ MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
+ throw new IllegalArgumentException(
+ "SlidingChallengeLayout must be measured with an exact size");
+ }
+
+ final int width = MeasureSpec.getSize(widthSpec);
+ final int height = MeasureSpec.getSize(heightSpec);
+ setMeasuredDimension(width, height);
+
+ // Find one and only one challenge view.
+ final View oldChallengeView = mChallengeView;
+ mChallengeView = null;
+ final int count = getChildCount();
+
+ // First iteration through the children finds special children and sets any associated
+ // state.
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
+ if (mChallengeView != null) {
+ throw new IllegalStateException(
+ "There may only be one child with layout_isChallenge=\"true\"");
+ }
+ mChallengeView = child;
+ if (mChallengeView != oldChallengeView) {
+ mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE);
+ }
+ // We're going to play silly games with the frame's background drawable later.
+ mFrameDrawable = mChallengeView.getBackground();
+ } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
+ setScrimView(child);
+ }
+ if (child.getVisibility() == GONE) continue;
+ }
+
+ // We want to measure the challenge view first, for various reasons that I'd rather
+ // not get into here.
+ if (mChallengeView != null) {
+ measureChildWithMargins(mChallengeView, widthSpec, 0, heightSpec, 0);
+ }
+
+ // Measure the rest of the children
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ // Don't measure the challenge view twice!
+ if (child != mChallengeView) {
+ measureChildWithMargins(child, widthSpec, 0, heightSpec, 0);
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int paddingLeft = getPaddingLeft();
+ final int paddingTop = getPaddingTop();
+ final int paddingRight = getPaddingRight();
+ final int paddingBottom = getPaddingBottom();
+ final int width = r - l;
+ final int height = b - t;
+
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+
+ if (child.getVisibility() == GONE) continue;
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
+ // Challenge views pin to the bottom, offset by a portion of their height,
+ // and center horizontally.
+ final int center = (paddingLeft + width - paddingRight) / 2;
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+ final int left = center - childWidth / 2;
+ final int layoutBottom = height - paddingBottom - lp.bottomMargin;
+ // We use the top of the challenge view to position the handle, so
+ // we never want less than the handle size showing at the bottom.
+ final int bottom = layoutBottom + (int) ((childHeight - mChallengeBottomBound)
+ * (1 - mChallengeOffset));
+ child.setAlpha(mChallengeOffset / 2 + 0.5f);
+ child.layout(left, bottom - childHeight, left + childWidth, bottom);
+ } else {
+ // Non-challenge views lay out from the upper left, layered.
+ child.layout(paddingLeft + lp.leftMargin,
+ paddingTop + lp.topMargin,
+ paddingLeft + child.getMeasuredWidth(),
+ paddingTop + child.getMeasuredHeight());
+ }
+ }
+
+ if (!mHasLayout) {
+ if (mFrameDrawable != null) {
+ mFrameDrawable.setAlpha(0);
+ }
+ mHasLayout = true;
+ }
+ }
+
+ public void computeScroll() {
+ super.computeScroll();
+
+ if (!mScroller.isFinished()) {
+ if (mChallengeView == null) {
+ // Can't scroll if the view is missing.
+ Log.e(TAG, "Challenge view missing in computeScroll");
+ mScroller.abortAnimation();
+ return;
+ }
+
+ mScroller.computeScrollOffset();
+ moveChallengeTo(mScroller.getCurrY());
+
+ if (mScroller.isFinished()) {
+ post(mEndScrollRunnable);
+ }
+ }
+ }
+
+ @Override
+ public void draw(Canvas c) {
+ super.draw(c);
+
+ final Paint debugPaint;
+ if (DEBUG) {
+ debugPaint = new Paint();
+ debugPaint.setColor(0x40FF00CC);
+ // show the isInDragHandle() rect
+ c.drawRect(mDragHandleEdgeSlop,
+ mChallengeView.getTop() - getDragHandleSizeAbove(),
+ getWidth() - mDragHandleEdgeSlop,
+ mChallengeView.getTop() + getDragHandleSizeBelow(),
+ debugPaint);
+ }
+
+ if (mChallengeView != null && mHandleAlpha > 0) {
+ final int top = mChallengeView.getTop();
+ final int handleHeight;
+ final int challengeLeft = mChallengeView.getLeft();
+ final int challengeRight = mChallengeView.getRight();
+ if (mHandleDrawable != null) {
+ handleHeight = mHandleDrawable.getIntrinsicHeight();
+ mHandleDrawable.setBounds(challengeLeft, top, challengeRight, top + handleHeight);
+ mHandleDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
+ mHandleDrawable.draw(c);
+ } else {
+ handleHeight = 0;
+ }
+
+ if (DEBUG) {
+ // now show the actual drag handle
+ debugPaint.setStyle(Paint.Style.STROKE);
+ debugPaint.setStrokeWidth(1);
+ debugPaint.setColor(0xFF80FF00);
+ c.drawRect(challengeLeft, top, challengeRight, top + handleHeight, debugPaint);
+ }
+
+ if (mDragIconDrawable != null) {
+ final int closedTop = getLayoutBottom() - mChallengeBottomBound;
+ final int iconWidth = mDragIconDrawable.getIntrinsicWidth();
+ final int iconHeight = mDragIconDrawable.getIntrinsicHeight();
+ final int iconLeft = (challengeLeft + challengeRight - iconWidth) / 2;
+ final int iconTop = closedTop +
+ (mDragHandleClosedBelow - mDragHandleClosedAbove - iconHeight) / 2;
+ mDragIconDrawable.setBounds(iconLeft, iconTop, iconLeft + iconWidth,
+ iconTop + iconHeight);
+ mDragIconDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
+ mDragIconDrawable.draw(c);
+
+ if (DEBUG) {
+ debugPaint.setColor(0xFF00FF00);
+ c.drawRect(iconLeft, iconTop, iconLeft + iconWidth,
+ iconTop + iconHeight, debugPaint);
+ }
+ }
+ }
+ }
+
+ public int getMaxChallengeTop() {
+ if (mChallengeView == null) return 0;
+
+ final int layoutBottom = getLayoutBottom();
+ final int challengeHeight = mChallengeView.getMeasuredHeight();
+ return layoutBottom - challengeHeight;
+ }
+
+ /**
+ * Move the bottom edge of mChallengeView to a new position and notify the listener
+ * if it represents a change in position. Changes made through this method will
+ * be stable across layout passes. If this method is called before first layout of
+ * this SlidingChallengeLayout it will have no effect.
+ *
+ * @param bottom New bottom edge in px in this SlidingChallengeLayout's coordinate system.
+ * @return true if the challenge view was moved
+ */
+ private boolean moveChallengeTo(int bottom) {
+ if (mChallengeView == null || !mHasLayout) {
+ return false;
+ }
+
+ final int layoutBottom = getLayoutBottom();
+ final int challengeHeight = mChallengeView.getHeight();
+
+ bottom = Math.max(layoutBottom,
+ Math.min(bottom, layoutBottom + challengeHeight - mChallengeBottomBound));
+
+ float offset = 1.f - (float) (bottom - layoutBottom) /
+ (challengeHeight - mChallengeBottomBound);
+ mChallengeOffset = offset;
+ if (offset > 0 && !mChallengeShowing) {
+ setChallengeShowing(true);
+ }
+
+ mChallengeView.layout(mChallengeView.getLeft(),
+ bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom);
+
+ mChallengeView.setAlpha(offset / 2 + 0.5f);
+ if (mScrollListener != null) {
+ mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop());
+ }
+ postInvalidateOnAnimation();
+ return true;
+ }
+
+ /**
+ * The bottom edge of this SlidingChallengeLayout's coordinate system; will coincide with
+ * the bottom edge of mChallengeView when the challenge is fully opened.
+ */
+ private int getLayoutBottom() {
+ final int bottomMargin = (mChallengeView == null)
+ ? 0
+ : ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
+ final int layoutBottom = getMeasuredHeight() - getPaddingBottom() - bottomMargin;
+ return layoutBottom;
+ }
+
+ /**
+ * The bottom edge of mChallengeView; essentially, where the sliding challenge 'is'.
+ */
+ private int getChallengeBottom() {
+ if (mChallengeView == null) return 0;
+
+ return mChallengeView.getBottom();
+ }
+
+ /**
+ * Show or hide the challenge view, animating it if necessary.
+ * @param show true to show, false to hide
+ */
+ public void showChallenge(boolean show) {
+ showChallenge(show, 0);
+ if (!show) {
+ // Block any drags in progress so that callers can use this to disable dragging
+ // for other touch interactions.
+ mBlockDrag = true;
+ }
+ }
+
+ private void showChallenge(int velocity) {
+ boolean show = false;
+ if (Math.abs(velocity) > mMinVelocity) {
+ show = velocity < 0;
+ } else {
+ show = mChallengeOffset >= 0.5f;
+ }
+ showChallenge(show, velocity);
+ }
+
+ private void showChallenge(boolean show, int velocity) {
+ if (mChallengeView == null) {
+ setChallengeShowing(false);
+ return;
+ }
+
+ if (mHasLayout) {
+ final int layoutBottom = getLayoutBottom();
+ animateChallengeTo(show ? layoutBottom :
+ layoutBottom + mChallengeView.getHeight() - mChallengeBottomBound, velocity);
+ }
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
+ p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
+ new LayoutParams(p);
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams();
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams;
+ }
+
+ public static class LayoutParams extends MarginLayoutParams {
+ public int childType = CHILD_TYPE_NONE;
+ public static final int CHILD_TYPE_NONE = 0;
+ public static final int CHILD_TYPE_CHALLENGE = 2;
+ public static final int CHILD_TYPE_SCRIM = 4;
+
+ public LayoutParams() {
+ this(MATCH_PARENT, WRAP_CONTENT);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(android.view.ViewGroup.LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(MarginLayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(LayoutParams source) {
+ super(source);
+
+ childType = source.childType;
+ }
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+
+ final TypedArray a = c.obtainStyledAttributes(attrs,
+ R.styleable.SlidingChallengeLayout_Layout);
+ childType = a.getInt(R.styleable.SlidingChallengeLayout_Layout_layout_childType,
+ CHILD_TYPE_NONE);
+ a.recycle();
+ }
+ }
+}
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index f96083369175..440f8e1c08cf 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -130,12 +130,14 @@ class AlarmManagerService extends IAlarmManager.Stub {
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mTimeTickSender = PendingIntent.getBroadcast(context, 0,
+ mTimeTickSender = PendingIntent.getBroadcastAsUser(context, 0,
new Intent(Intent.ACTION_TIME_TICK).addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0,
+ UserHandle.ALL);
Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- mDateChangeSender = PendingIntent.getBroadcast(context, 0, intent, 0);
+ mDateChangeSender = PendingIntent.getBroadcastAsUser(context, 0, intent,
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
// now that we have initied the driver schedule the alarm
mClockReceiver= new ClockReceiver();
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index a02fc8db5e22..82dbf54a7f70 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -466,10 +466,13 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
if (Intent.ACTION_USER_REMOVED.equals(action)) {
onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
UserHandle.USER_NULL));
- } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
- onStoppingUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_NULL));
}
+ // TODO: Race condition causing problems when cleaning up on stopping a user.
+ // Comment this out for now.
+ // else if (Intent.ACTION_USER_STOPPING.equals(action)) {
+ // onStoppingUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ // UserHandle.USER_NULL));
+ // }
}
}, userFilter);
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7c482f58f80b..6b277c7887e2 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -558,8 +558,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
synchronized (mLock) {
// Automation service is not bound, so pretend it died to perform clean up.
- if (mUiAutomationService != null
- && mUiAutomationService.mServiceInterface == serviceClient) {
+ if (mUiAutomationService != null && mUiAutomationService.mServiceInterface != null
+ && serviceClient != null && mUiAutomationService.mServiceInterface
+ .asBinder() == serviceClient.asBinder()) {
mUiAutomationService.binderDied();
}
}
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index e76bf44bf5c3..247d8a049270 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import android.util.DisplayMetrics;
+import android.view.Display;
import android.view.Surface;
import libcore.util.Objects;
@@ -138,6 +139,17 @@ final class DisplayDeviceInfo {
*/
public int rotation = Surface.ROTATION_0;
+ /**
+ * Display type.
+ */
+ public int type;
+
+ /**
+ * Display address, or null if none.
+ * Interpretation varies by display type.
+ */
+ public String address;
+
public void setAssumedDensityForExternalDisplay(int width, int height) {
densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
// Technically, these values should be smaller than the apparent density
@@ -162,7 +174,9 @@ final class DisplayDeviceInfo {
&& yDpi == other.yDpi
&& flags == other.flags
&& touch == other.touch
- && rotation == other.rotation;
+ && rotation == other.rotation
+ && type == other.type
+ && Objects.equal(address, other.address);
}
@Override
@@ -181,6 +195,8 @@ final class DisplayDeviceInfo {
flags = other.flags;
touch = other.touch;
rotation = other.rotation;
+ type = other.type;
+ address = other.address;
}
// For debugging purposes
@@ -191,6 +207,8 @@ final class DisplayDeviceInfo {
+ "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
+ ", touch " + touchToString(touch) + flagsToString(flags)
+ ", rotation " + rotation
+ + ", type " + Display.typeToString(type)
+ + ", address " + address
+ "}";
}
diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
index 919733db2b3e..7a104d7e5a8a 100644
--- a/services/java/com/android/server/display/HeadlessDisplayAdapter.java
+++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
@@ -19,6 +19,7 @@ package com.android.server.display;
import android.content.Context;
import android.os.Handler;
import android.util.DisplayMetrics;
+import android.view.Display;
/**
* Provides a fake default display for headless systems.
@@ -63,6 +64,7 @@ final class HeadlessDisplayAdapter extends DisplayAdapter {
mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
| DisplayDeviceInfo.FLAG_SECURE
| DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
+ mInfo.type = Display.TYPE_BUILT_IN;
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
}
return mInfo;
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index fa56b83103f3..b37d57fbb0ff 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -22,6 +22,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.SystemProperties;
import android.util.SparseArray;
+import android.view.Display;
import android.view.DisplayEventReceiver;
import android.view.Surface;
import android.view.Surface.PhysicalDisplayInfo;
@@ -139,11 +140,13 @@ final class LocalDisplayAdapter extends DisplayAdapter {
com.android.internal.R.string.display_manager_built_in_display_name);
mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
| DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
+ mInfo.type = Display.TYPE_BUILT_IN;
mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
mInfo.xDpi = mPhys.xDpi;
mInfo.yDpi = mPhys.yDpi;
mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
} else {
+ mInfo.type = Display.TYPE_HDMI;
mInfo.name = getContext().getResources().getString(
com.android.internal.R.string.display_manager_hdmi_display_name);
mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index aa62aeee8ea2..1583137aeb49 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -189,6 +189,8 @@ final class LogicalDisplay {
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SECURE) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_SECURE;
}
+ mBaseDisplayInfo.type = deviceInfo.type;
+ mBaseDisplayInfo.address = deviceInfo.address;
mBaseDisplayInfo.name = deviceInfo.name;
mBaseDisplayInfo.appWidth = deviceInfo.width;
mBaseDisplayInfo.appHeight = deviceInfo.height;
diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java
index c35fd98e4ea8..36e9f74c6a93 100644
--- a/services/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -27,6 +27,7 @@ import android.os.IBinder;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Slog;
+import android.view.Display;
import android.view.Gravity;
import android.view.Surface;
@@ -240,6 +241,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
mInfo.xDpi = mDensityDpi;
mInfo.yDpi = mDensityDpi;
mInfo.flags = 0;
+ mInfo.type = Display.TYPE_OVERLAY;
mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
}
return mInfo;
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index 2ea83eedfa09..45fff303b7c6 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -39,6 +39,7 @@ import android.os.Message;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
+import android.view.Display;
import android.view.Surface;
import java.io.PrintWriter;
@@ -293,9 +294,10 @@ final class WifiDisplayAdapter extends DisplayAdapter {
float refreshRate = 60.0f; // TODO: get this for real
String name = display.getFriendlyDisplayName();
+ String address = display.getDeviceAddress();
IBinder displayToken = Surface.createDisplay(name, secure);
mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
- refreshRate, deviceFlags, surface);
+ refreshRate, deviceFlags, address, surface);
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
scheduleUpdateNotificationLocked();
@@ -515,12 +517,13 @@ final class WifiDisplayAdapter extends DisplayAdapter {
private final int mHeight;
private final float mRefreshRate;
private final int mFlags;
+ private final String mAddress;
private Surface mSurface;
private DisplayDeviceInfo mInfo;
public WifiDisplayDevice(IBinder displayToken, String name,
- int width, int height, float refreshRate, int flags,
+ int width, int height, float refreshRate, int flags, String address,
Surface surface) {
super(WifiDisplayAdapter.this, displayToken);
mName = name;
@@ -528,6 +531,7 @@ final class WifiDisplayAdapter extends DisplayAdapter {
mHeight = height;
mRefreshRate = refreshRate;
mFlags = flags;
+ mAddress = address;
mSurface = surface;
}
@@ -555,6 +559,8 @@ final class WifiDisplayAdapter extends DisplayAdapter {
mInfo.height = mHeight;
mInfo.refreshRate = mRefreshRate;
mInfo.flags = mFlags;
+ mInfo.type = Display.TYPE_WIFI;
+ mInfo.address = mAddress;
mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 6ef39ac10733..83672c540c2d 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -1009,6 +1009,8 @@ public class PackageManagerService extends IPackageManager.Stub {
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
+ mAppInstallDir = new File(dataDir, "app");
+ mAppLibInstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
@@ -1218,8 +1220,6 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- mAppInstallDir = new File(dataDir, "app");
- mAppLibInstallDir = new File(dataDir, "app-lib");
//look for any incomplete package installations
ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
//clean up list
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 3a54514d9c05..94494ae892c9 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -196,7 +196,7 @@ final class Settings {
final String name = pkg.packageName;
PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags,
- user, add);
+ user, add, true /* allowInstall */);
return p;
}
@@ -358,7 +358,7 @@ final class Settings {
private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
String nativeLibraryPathString, int vc, int pkgFlags,
- UserHandle installUser, boolean add) {
+ UserHandle installUser, boolean add, boolean allowInstall) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (!p.codePath.equals(codePath)) {
@@ -432,7 +432,7 @@ final class Settings {
Slog.i(PackageManagerService.TAG, "Stopping package " + name, e);
}
List<UserInfo> users = getAllUsers();
- if (users != null) {
+ if (users != null && allowInstall) {
for (UserInfo user : users) {
// By default we consider this app to be installed
// for the user if no user has been specified (which
@@ -498,7 +498,7 @@ final class Settings {
addPackageSettingLPw(p, name, sharedUser);
}
} else {
- if (installUser != null) {
+ if (installUser != null && allowInstall) {
// The caller has explicitly specified the user they want this
// package installed for, and the package already exists.
// Make sure it conforms to the new request.
@@ -1701,24 +1701,6 @@ final class Settings {
Log.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
}
- if (mBackupStoppedPackagesFilename.exists()
- || mStoppedPackagesFilename.exists()) {
- // Read old file
- readStoppedLPw();
- mBackupStoppedPackagesFilename.delete();
- mStoppedPackagesFilename.delete();
- // Migrate to new file format
- writePackageRestrictionsLPr(0);
- } else {
- if (users == null) {
- readPackageRestrictionsLPr(0);
- } else {
- for (UserInfo user : users) {
- readPackageRestrictionsLPr(user.id);
- }
- }
- }
-
final int N = mPendingPackages.size();
for (int i = 0; i < N; i++) {
final PendingPackage pp = mPendingPackages.get(i);
@@ -1727,7 +1709,7 @@ final class Settings {
PackageSetting p = getPackageLPw(pp.name, null, pp.realName,
(SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags,
- UserHandle.ALL, true);
+ null, true /* add */, false /* allowInstall */);
if (p == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unable to create application package for " + pp.name);
@@ -1748,6 +1730,24 @@ final class Settings {
}
mPendingPackages.clear();
+ if (mBackupStoppedPackagesFilename.exists()
+ || mStoppedPackagesFilename.exists()) {
+ // Read old file
+ readStoppedLPw();
+ mBackupStoppedPackagesFilename.delete();
+ mStoppedPackagesFilename.delete();
+ // Migrate to new file format
+ writePackageRestrictionsLPr(0);
+ } else {
+ if (users == null) {
+ readPackageRestrictionsLPr(0);
+ } else {
+ for (UserInfo user : users) {
+ readPackageRestrictionsLPr(user.id);
+ }
+ }
+ }
+
/*
* Make sure all the updated system packages have their shared users
* associated with them.
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index fb93d059a14b..e05442b3b905 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -26,6 +26,7 @@ import android.app.IStopUserCallback;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
@@ -75,6 +76,7 @@ public class UserManagerService extends IUserManager.Stub {
private static final String ATTR_SERIAL_NO = "serialNumber";
private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
private static final String ATTR_PARTIAL = "partial";
+ private static final String ATTR_USER_VERSION = "version";
private static final String TAG_USERS = "users";
private static final String TAG_USER = "user";
@@ -84,6 +86,8 @@ public class UserManagerService extends IUserManager.Stub {
private static final int MIN_USER_ID = 10;
+ private static final int USER_VERSION = 1;
+
private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
private final Context mContext;
@@ -104,6 +108,7 @@ public class UserManagerService extends IUserManager.Stub {
// This resets on a reboot. Otherwise it keeps incrementing so that user ids are
// not reused in quick succession
private int mNextUserId = MIN_USER_ID;
+ private int mUserVersion = 0;
private static UserManagerService sInstance;
@@ -432,12 +437,17 @@ public class UserManagerService extends IUserManager.Stub {
if (lastSerialNumber != null) {
mNextSerialNumber = Integer.parseInt(lastSerialNumber);
}
+ String versionNumber = parser.getAttributeValue(null, ATTR_USER_VERSION);
+ if (versionNumber != null) {
+ mUserVersion = Integer.parseInt(versionNumber);
+ }
}
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
String id = parser.getAttributeValue(null, ATTR_ID);
UserInfo user = readUser(Integer.parseInt(id));
+
if (user != null) {
mUsers.put(user.id, user);
if (user.isGuest()) {
@@ -450,6 +460,7 @@ public class UserManagerService extends IUserManager.Stub {
}
}
updateUserIdsLocked();
+ upgradeIfNecessary();
} catch (IOException ioe) {
fallbackToSingleUserLocked();
} catch (XmlPullParserException pe) {
@@ -464,9 +475,35 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ /**
+ * This fixes an incorrect initialization of user name for the owner.
+ * TODO: Remove in the next release.
+ */
+ private void upgradeIfNecessary() {
+ int userVersion = mUserVersion;
+ if (userVersion < 1) {
+ // Assign a proper name for the owner, if not initialized correctly before
+ UserInfo user = mUsers.get(UserHandle.USER_OWNER);
+ if ("Primary".equals(user.name)) {
+ user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name);
+ writeUserLocked(user);
+ }
+ userVersion = 1;
+ }
+
+ if (userVersion < USER_VERSION) {
+ Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
+ + USER_VERSION);
+ } else {
+ mUserVersion = userVersion;
+ writeUserListLocked();
+ }
+ }
+
private void fallbackToSingleUserLocked() {
// Create the primary user
- UserInfo primary = new UserInfo(0, "Primary", null,
+ UserInfo primary = new UserInfo(0,
+ mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
mUsers.put(0, primary);
mNextSerialNumber = MIN_USER_ID;
@@ -547,6 +584,7 @@ public class UserManagerService extends IUserManager.Stub {
serializer.startTag(null, TAG_USERS);
serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
+ serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion));
for (int i = 0; i < mUsers.size(); i++) {
UserInfo user = mUsers.valueAt(i);
diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java
index 6bb7ec5946e8..fa318f8b6198 100644
--- a/services/java/com/android/server/power/DisplayPowerState.java
+++ b/services/java/com/android/server/power/DisplayPowerState.java
@@ -302,8 +302,8 @@ final class DisplayPowerState {
public void run() {
mScreenUpdatePending = false;
- if (mPhotonicModulator.setState(mScreenOn,
- mScreenOn ? (int)(mScreenBrightness * mElectronBeamLevel) : 0)) {
+ int brightness = mScreenOn && mElectronBeamLevel > 0f ? mScreenBrightness : 0;
+ if (mPhotonicModulator.setState(mScreenOn, brightness)) {
mScreenReady = true;
invokeCleanListenerIfNeeded();
}
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 269eac074d65..9a62482f17c7 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -264,6 +264,8 @@ public class WindowAnimator {
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
}
}
+ if (WindowManagerService.DEBUG_WALLPAPER_LIGHT && !token.hidden) Slog.d(TAG,
+ "Hiding wallpaper " + token + " from " + w);
token.hidden = true;
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 209cad17974c..3532c0c9d992 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -192,6 +192,7 @@ public class WindowManagerService extends IWindowManager.Stub
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
static final boolean DEBUG_WALLPAPER = false;
+ static final boolean DEBUG_WALLPAPER_LIGHT = true || DEBUG_WALLPAPER;
static final boolean DEBUG_DRAG = false;
static final boolean DEBUG_SCREEN_ON = false;
static final boolean DEBUG_SCREENSHOT = false;
@@ -1625,7 +1626,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (foundW == null && windowDetachedI >= 0) {
- if (DEBUG_WALLPAPER) Slog.v(TAG,
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"Found animating detached wallpaper activity: #" + i + "=" + w);
foundW = w;
foundI = windowDetachedI;
@@ -1641,12 +1642,12 @@ public class WindowManagerService extends IWindowManager.Stub
// enough (we'll just wait until whatever transition is pending
// executes).
if (mWallpaperTarget != null && mWallpaperTarget.mAppToken != null) {
- if (DEBUG_WALLPAPER) Slog.v(TAG,
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"Wallpaper not changing: waiting for app anim in current target");
return 0;
}
if (foundW != null && foundW.mAppToken != null) {
- if (DEBUG_WALLPAPER) Slog.v(TAG,
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"Wallpaper not changing: waiting for app anim in found target");
return 0;
}
@@ -1654,7 +1655,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (mWallpaperTarget != foundW
&& (mLowerWallpaperTarget == null || mLowerWallpaperTarget != foundW)) {
- if (DEBUG_WALLPAPER) {
+ if (DEBUG_WALLPAPER_LIGHT) {
Slog.v(TAG, "New wallpaper target: " + foundW
+ " oldTarget: " + mWallpaperTarget);
}
@@ -1674,17 +1675,17 @@ public class WindowManagerService extends IWindowManager.Stub
boolean foundAnim = foundW.mWinAnimator.mAnimation != null
|| (foundW.mAppToken != null &&
foundW.mAppToken.mAppAnimator.animation != null);
- if (DEBUG_WALLPAPER) {
+ if (DEBUG_WALLPAPER_LIGHT) {
Slog.v(TAG, "New animation: " + foundAnim
+ " old animation: " + oldAnim);
}
if (foundAnim && oldAnim) {
int oldI = windows.indexOf(oldW);
- if (DEBUG_WALLPAPER) {
+ if (DEBUG_WALLPAPER_LIGHT) {
Slog.v(TAG, "New i: " + foundI + " old i: " + oldI);
}
if (oldI >= 0) {
- if (DEBUG_WALLPAPER) {
+ if (DEBUG_WALLPAPER_LIGHT) {
Slog.v(TAG, "Animating wallpapers: old#" + oldI
+ "=" + oldW + "; new#" + foundI
+ "=" + foundW);
@@ -1692,7 +1693,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Set the new target correctly.
if (foundW.mAppToken != null && foundW.mAppToken.hiddenRequested) {
- if (DEBUG_WALLPAPER) {
+ if (DEBUG_WALLPAPER_LIGHT) {
Slog.v(TAG, "Old wallpaper still the target.");
}
mWallpaperTarget = oldW;
@@ -1704,7 +1705,7 @@ public class WindowManagerService extends IWindowManager.Stub
// the wallpaper below the lower.
else if (foundI > oldI) {
// The new target is on top of the old one.
- if (DEBUG_WALLPAPER) {
+ if (DEBUG_WALLPAPER_LIGHT) {
Slog.v(TAG, "Found target above old target.");
}
mUpperWallpaperTarget = foundW;
@@ -1713,7 +1714,7 @@ public class WindowManagerService extends IWindowManager.Stub
foundI = oldI;
} else {
// The new target is below the old one.
- if (DEBUG_WALLPAPER) {
+ if (DEBUG_WALLPAPER_LIGHT) {
Slog.v(TAG, "Found target below old target.");
}
mUpperWallpaperTarget = oldW;
@@ -1732,7 +1733,7 @@ public class WindowManagerService extends IWindowManager.Stub
|| (mUpperWallpaperTarget.mAppToken != null
&& mUpperWallpaperTarget.mAppToken.mAppAnimator.animation != null);
if (!lowerAnimating || !upperAnimating) {
- if (DEBUG_WALLPAPER) {
+ if (DEBUG_WALLPAPER_LIGHT) {
Slog.v(TAG, "No longer animating wallpaper targets!");
}
mLowerWallpaperTarget = null;
@@ -1810,6 +1811,8 @@ public class WindowManagerService extends IWindowManager.Stub
curTokenIndex--;
WindowToken token = mWallpaperTokens.get(curTokenIndex);
if (token.hidden == visible) {
+ if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
+ "Wallpaper token " + token + " hidden=" + !visible);
changed |= ADJUST_WALLPAPER_VISIBILITY_CHANGED;
token.hidden = !visible;
// Need to do a layout to ensure the wallpaper now has the
@@ -1831,7 +1834,7 @@ public class WindowManagerService extends IWindowManager.Stub
dispatchWallpaperVisibility(wallpaper, visible);
wallpaper.mWinAnimator.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
- if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "adjustWallpaper win "
+ if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win "
+ wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
// First, if this window is at the current index, then all
@@ -1858,7 +1861,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
// Now stick it in.
- if (DEBUG_WALLPAPER || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
+ if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
Slog.v(TAG, "Moving wallpaper " + wallpaper
+ " from " + oldIndex + " to " + foundI);
}
@@ -2033,9 +2036,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (wallpaper.mWallpaperVisible != visible) {
wallpaper.mWallpaperVisible = visible;
try {
- if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Slog.v(TAG,
- "Updating visibility of wallpaper " + wallpaper
- + ": " + visible + " Callers=" + Debug.getCallers(2));
+ if (DEBUG_VISIBILITY || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
+ "Updating vis of wallpaper " + wallpaper
+ + ": " + visible + " from:\n" + Debug.getCallers(4, " "));
wallpaper.mClient.dispatchAppVisibility(visible);
} catch (RemoteException e) {
}
@@ -8800,10 +8803,10 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState oldWallpaper = mWallpaperTarget;
if (mLowerWallpaperTarget != null
&& mLowerWallpaperTarget.mAppToken != null) {
- if (DEBUG_WALLPAPER) Slog.v(TAG,
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"wallpaperForceHiding changed with lower="
+ mLowerWallpaperTarget);
- if (DEBUG_WALLPAPER) Slog.v(TAG,
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"hidden=" + mLowerWallpaperTarget.mAppToken.hidden +
" hiddenRequested=" + mLowerWallpaperTarget.mAppToken.hiddenRequested);
if (mLowerWallpaperTarget.mAppToken.hidden) {
@@ -8815,7 +8818,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
mInnerFields.mAdjResult |= adjustWallpaperWindowsLocked();
- if (DEBUG_WALLPAPER) Slog.v(TAG, "****** OLD: " + oldWallpaper
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "****** OLD: " + oldWallpaper
+ " NEW: " + mWallpaperTarget
+ " LOWER: " + mLowerWallpaperTarget);
return changes;
@@ -9189,7 +9192,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
- if (DEBUG_WALLPAPER) Slog.v(TAG,
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"First draw done in potential wallpaper target " + w);
mInnerFields.mWallpaperMayChange = true;
displayContent.pendingLayoutChanges |=
@@ -9340,18 +9343,18 @@ public class WindowManagerService extends IWindowManager.Stub
mInnerFields.mWallpaperForceHidingChanged = false;
if (mInnerFields.mWallpaperMayChange) {
- if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+ if (WindowManagerService.DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"Wallpaper may change! Adjusting");
mInnerFields.mAdjResult |= adjustWallpaperWindowsLocked();
}
if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
- if (DEBUG_WALLPAPER) Slog.v(TAG,
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"Wallpaper layer changed: assigning layers + relayout");
defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
assignLayersLocked(defaultWindows);
} else if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
- if (DEBUG_WALLPAPER) Slog.v(TAG,
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"Wallpaper visibility changed: relayout");
defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
}
@@ -10326,7 +10329,7 @@ public class WindowManagerService extends IWindowManager.Stub
Iterator<WindowToken> it = mTokenMap.values().iterator();
while (it.hasNext()) {
WindowToken token = it.next();
- pw.print(" Token "); pw.print(token.token);
+ pw.print(" "); pw.print(token);
if (dumpAll) {
pw.println(':');
token.dump(pw, " ");
@@ -10354,8 +10357,9 @@ public class WindowManagerService extends IWindowManager.Stub
pw.println();
pw.println(" Application tokens in Z order:");
for (int i=mAppTokens.size()-1; i>=0; i--) {
- pw.print(" App #"); pw.print(i); pw.println(": ");
- mAppTokens.get(i).dump(pw, " ");
+ pw.print(" App #"); pw.print(i);
+ pw.print(' '); pw.print(mAppTokens.get(i)); pw.println(":");
+ mAppTokens.get(i).dump(pw, " ");
}
}
if (mFinishedStarting.size() > 0) {
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index c195f4578d8b..0a9c3e5bfdcd 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -282,7 +282,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mEnforceSizeCompat = (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0;
if (WindowManagerService.localLOGV) Slog.v(
TAG, "Window " + this + " client=" + c.asBinder()
- + " token=" + token + " (" + mAttrs.token + ")");
+ + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
try {
c.asBinder().linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
diff --git a/services/java/com/android/server/wm/WindowToken.java b/services/java/com/android/server/wm/WindowToken.java
index 5ec151b8e805..e581915abee5 100644
--- a/services/java/com/android/server/wm/WindowToken.java
+++ b/services/java/com/android/server/wm/WindowToken.java
@@ -79,7 +79,6 @@ class WindowToken {
}
void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("token="); pw.println(token);
pw.print(prefix); pw.print("windows="); pw.println(windows);
pw.print(prefix); pw.print("windowType="); pw.print(windowType);
pw.print(" hidden="); pw.print(hidden);
@@ -97,7 +96,7 @@ class WindowToken {
StringBuilder sb = new StringBuilder();
sb.append("WindowToken{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
- sb.append(" token="); sb.append(token); sb.append('}');
+ sb.append(" "); sb.append(token); sb.append('}');
stringName = sb.toString();
}
return stringName;
diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java
index 3e5f10f1f803..55e9b2dd5336 100644
--- a/wifi/java/android/net/wifi/WifiSsid.java
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -89,21 +89,27 @@ public class WifiSsid implements Parcelable {
switch(asciiEncoded.charAt(i)) {
case '\\':
octets.write('\\');
+ i++;
break;
case '"':
octets.write('"');
+ i++;
break;
case 'n':
octets.write('\n');
+ i++;
break;
case 'r':
octets.write('\r');
+ i++;
break;
case 't':
octets.write('\t');
+ i++;
break;
case 'e':
octets.write(27); //escape char
+ i++;
break;
case 'x':
i++;