diff options
42 files changed, 1477 insertions, 758 deletions
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index d56556f..be6a770 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -244,14 +244,22 @@ public class AppWidgetHostView extends FrameLayout { * * @param newOptions The bundle of options, in addition to the size information, * can be null. - * @param minWidth The minimum width that the widget will be displayed at. - * @param minHeight The maximum height that the widget will be displayed at. - * @param maxWidth The maximum width that the widget will be displayed at. - * @param maxHeight The maximum height that the widget will be displayed at. + * @param minWidth The minimum width in dips that the widget will be displayed at. + * @param minHeight The maximum height in dips that the widget will be displayed at. + * @param maxWidth The maximum width in dips that the widget will be displayed at. + * @param maxHeight The maximum height in dips that the widget will be displayed at. * */ public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth, int maxHeight) { + updateAppWidgetSize(newOptions, minWidth, minHeight, maxWidth, maxHeight, false); + } + + /** + * @hide + */ + public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth, + int maxHeight, boolean ignorePadding) { if (newOptions == null) { newOptions = new Bundle(); } @@ -265,10 +273,10 @@ public class AppWidgetHostView extends FrameLayout { int xPaddingDips = (int) ((padding.left + padding.right) / density); int yPaddingDips = (int) ((padding.top + padding.bottom) / density); - int newMinWidth = minWidth - xPaddingDips; - int newMinHeight = minHeight - yPaddingDips; - int newMaxWidth = maxWidth - xPaddingDips; - int newMaxHeight = maxHeight - yPaddingDips; + int newMinWidth = minWidth - (ignorePadding ? 0 : xPaddingDips); + int newMinHeight = minHeight - (ignorePadding ? 0 : yPaddingDips); + int newMaxWidth = maxWidth - (ignorePadding ? 0 : xPaddingDips); + int newMaxHeight = maxHeight - (ignorePadding ? 0 : yPaddingDips); AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 3ee3c8c..e5e1a2b 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1073,11 +1073,7 @@ public class LockPatternUtils { } return appWidgetIds; } - if (appWidgetIdString == null) { - return new int[] { LockPatternUtils.ID_DEFAULT_STATUS_WIDGET }; - } else { - return new int[0]; - } + return new int[0]; } private static String combineStrings(int[] list, String separator) { diff --git a/core/res/res/drawable-hdpi/kg_widget_delete_drop_target.png b/core/res/res/drawable-hdpi/kg_widget_delete_drop_target.png Binary files differnew file mode 100644 index 0000000..84549ff --- /dev/null +++ b/core/res/res/drawable-hdpi/kg_widget_delete_drop_target.png diff --git a/core/res/res/drawable-mdpi/kg_widget_delete_drop_target.png b/core/res/res/drawable-mdpi/kg_widget_delete_drop_target.png Binary files differnew file mode 100644 index 0000000..219f3e5 --- /dev/null +++ b/core/res/res/drawable-mdpi/kg_widget_delete_drop_target.png diff --git a/core/res/res/drawable-xhdpi/kg_widget_delete_drop_target.png b/core/res/res/drawable-xhdpi/kg_widget_delete_drop_target.png Binary files differnew file mode 100644 index 0000000..d4965d9 --- /dev/null +++ b/core/res/res/drawable-xhdpi/kg_widget_delete_drop_target.png diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml index 67ac1d5..be1d5b6 100644 --- a/core/res/res/layout-land/keyguard_host_view.xml +++ b/core/res/res/layout-land/keyguard_host_view.xml @@ -33,6 +33,12 @@ android:layout_height="match_parent" android:clipChildren="false"> + <include layout="@layout/keyguard_widget_remove_drop_target" + android:id="@+id/keyguard_widget_pager_delete_target" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top|center_horizontal" /> + <include layout="@layout/keyguard_widget_pager" android:id="@+id/app_widget_container" android:layout_width="match_parent" diff --git a/core/res/res/layout-land/keyguard_widget_pager.xml b/core/res/res/layout-land/keyguard_widget_pager.xml index 975288f..02c6d0e 100644 --- a/core/res/res/layout-land/keyguard_widget_pager.xml +++ b/core/res/res/layout-land/keyguard_widget_pager.xml @@ -25,7 +25,6 @@ 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 b3270e0..9921313 100644 --- a/core/res/res/layout-port/keyguard_host_view.xml +++ b/core/res/res/layout-port/keyguard_host_view.xml @@ -35,6 +35,16 @@ <FrameLayout android:layout_width="match_parent" + android:layout_height="wrap_content"> + <include layout="@layout/keyguard_widget_remove_drop_target" + android:id="@+id/keyguard_widget_pager_delete_target" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top|center_horizontal" /> + </FrameLayout> + + <FrameLayout + android:layout_width="match_parent" android:layout_height="match_parent" androidprv:layout_childType="widgets"> <include layout="@layout/keyguard_widget_pager" diff --git a/core/res/res/layout-port/keyguard_widget_pager.xml b/core/res/res/layout-port/keyguard_widget_pager.xml index 7fd370b..7f22709 100644 --- a/core/res/res/layout-port/keyguard_widget_pager.xml +++ b/core/res/res/layout-port/keyguard_widget_pager.xml @@ -26,7 +26,6 @@ android:paddingRight="25dp" android:paddingTop="25dp" android:paddingBottom="@dimen/kg_widget_pager_bottom_padding" - 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 5d858ae..809104d 100644 --- a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml +++ b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml @@ -34,6 +34,12 @@ android:clipChildren="false" android:orientation="vertical"> + <include layout="@layout/keyguard_widget_remove_drop_target" + android:id="@+id/keyguard_widget_pager_delete_target" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top|center_horizontal" /> + <include layout="@layout/keyguard_widget_pager" android:id="@+id/app_widget_container" android:layout_width="match_parent" diff --git a/core/res/res/layout/keyguard_pin_view.xml b/core/res/res/layout/keyguard_pin_view.xml index 29e69f3..d6cfe2a 100644 --- a/core/res/res/layout/keyguard_pin_view.xml +++ b/core/res/res/layout/keyguard_pin_view.xml @@ -193,10 +193,10 @@ <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:paddingRight="30dp" android:src="@drawable/sym_keyboard_return_holo" android:contentDescription="@string/keyboardview_keycode_enter" /> diff --git a/core/res/res/layout/keyguard_sim_pin_view.xml b/core/res/res/layout/keyguard_sim_pin_view.xml index ad61709..36e1b15 100644 --- a/core/res/res/layout/keyguard_sim_pin_view.xml +++ b/core/res/res/layout/keyguard_sim_pin_view.xml @@ -19,98 +19,200 @@ <!-- This is the SIM PIN view that allows the user to enter a SIM PIN to unlock the device. --> <com.android.internal.policy.impl.keyguard.KeyguardSimPinView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/res/android" android:id="@+id/keyguard_sim_pin_view" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal"> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_lockscreen_sim"/> + + <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" - android:orientation="vertical"> - - <LinearLayout - android:layout_height="0dip" - android:layout_width="match_parent" + > + <TextView android:id="@+id/pinEntry" + android:editable="true" + android:layout_width="0dip" + android:layout_height="match_parent" android:layout_weight="1" - android:orientation="vertical" - android:gravity="center"> - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/ic_lockscreen_sim"/> - - <include layout="@layout/keyguard_message_area_large" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - - </LinearLayout> - - <!-- Password entry field --> - <!-- Note: the entire container is styled to look like the edit field, - since the backspace/IME switcher looks better inside --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginEnd="4dip" - android:layout_marginStart="4dip" - android:gravity="center_vertical" - android:background="#70000000"> - - <!-- displays dots as user enters pin --> - <EditText android:id="@+id/sim_pin_entry" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:maxLines="1" - android:singleLine="true" - android:gravity="center_horizontal" - android:layout_gravity="center_vertical" - android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left" - android:textStyle="normal" - android:inputType="textPassword" - android:textSize="36sp" - android:background="@null" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="#ffffffff" - android:imeOptions="flagForceAscii|actionDone" + android:gravity="center" + android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left" + android:singleLine="true" + android:cursorVisible="false" + android:background="@null" + android:textAppearance="@style/TextAppearance.NumPadKey" + android:imeOptions="flagForceAscii|actionDone" /> - - <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" - /> - </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" + <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" + android:contentDescription="@string/keyboardview_keycode_delete" + /> + </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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + androidprv:digit="0" + /> + <ImageButton + android:id="@+id/key_enter" + style="@style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:paddingRight="30dp" + android:src="@drawable/sym_keyboard_return_holo" + android:contentDescription="@string/keyboardview_keycode_enter" + /> </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" /> + 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.KeyguardSimPinView> diff --git a/core/res/res/layout/keyguard_sim_puk_view.xml b/core/res/res/layout/keyguard_sim_puk_view.xml index cc97005..e846a7b 100644 --- a/core/res/res/layout/keyguard_sim_puk_view.xml +++ b/core/res/res/layout/keyguard_sim_puk_view.xml @@ -20,97 +20,199 @@ carrier-provided PUK code and entering a new SIM PIN for it. --> <com.android.internal.policy.impl.keyguard.KeyguardSimPukView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/res/android" android:id="@+id/keyguard_sim_puk_view" + android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" android:gravity="center_horizontal"> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_lockscreen_sim"/> + + <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" - android:orientation="vertical"> - - <LinearLayout - android:layout_height="0dip" - android:layout_width="match_parent" + > + <TextView android:id="@+id/pinEntry" + android:editable="true" + android:layout_width="0dip" + android:layout_height="match_parent" android:layout_weight="1" - android:orientation="vertical" - android:gravity="center"> - - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/ic_lockscreen_sim"/> - - <include layout="@layout/keyguard_message_area_large" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - - </LinearLayout> - - <!-- Password entry field --> - <!-- Note: the entire container is styled to look like the edit field, - since the backspace/IME switcher looks better inside --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginEnd="4dip" - android:layout_marginStart="4dip" - android:gravity="center_vertical" - android:background="#70000000"> - - <!-- displays dots as user enters pin --> - <EditText android:id="@+id/sim_pin_entry" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:maxLines="1" - android:singleLine="true" - android:gravity="center_horizontal" - android:layout_gravity="center_vertical" - android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left" - android:textStyle="normal" - android:inputType="textPassword" - android:textSize="36sp" - android:background="@null" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textColor="#ffffffff" - android:imeOptions="flagForceAscii|actionDone" - /> - - <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:gravity="center" + android:layout_marginStart="@*android:dimen/keyguard_lockscreen_pin_margin_left" + android:singleLine="true" + android:cursorVisible="false" + android:background="@null" + android:textAppearance="@style/TextAppearance.NumPadKey" + android:imeOptions="flagForceAscii|actionDone" /> - </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" + <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" + android:contentDescription="@string/keyboardview_keycode_delete" + /> + </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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + 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/pinEntry" + androidprv:digit="0" + /> + <ImageButton + android:id="@+id/key_enter" + style="@style/Widget.Button.NumPadKey" + android:layout_width="0px" + android:layout_height="match_parent" + android:layout_weight="1" + android:paddingRight="30dp" + android:src="@drawable/sym_keyboard_return_holo" + android:contentDescription="@string/keyboardview_keycode_enter" + /> </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" /> + 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.KeyguardSimPukView> diff --git a/core/res/res/layout/keyguard_widget_remove_drop_target.xml b/core/res/res/layout/keyguard_widget_remove_drop_target.xml new file mode 100644 index 0000000..c4fe9e0 --- /dev/null +++ b/core/res/res/layout/keyguard_widget_remove_drop_target.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. +*/ +--> +<TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:gravity="center" + android:padding="20dp" + android:paddingLeft="40dp" + android:paddingRight="40dp" + android:drawableLeft="@drawable/kg_widget_delete_drop_target" + android:drawablePadding="4dp" + android:textColor="#FFF" + android:textSize="13sp" + android:shadowColor="#000" + android:shadowDy="1.0" + android:shadowRadius="1.0" + android:visibility="gone" />
\ No newline at end of file diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml index 36f2628..8f1bd9a 100644 --- a/core/res/res/values-land/dimens.xml +++ b/core/res/res/values-land/dimens.xml @@ -56,4 +56,9 @@ <!-- Bottom padding for the widget pager --> <dimen name="kg_widget_pager_bottom_padding">0dp</dimen> + <!-- If the height if keyguard drops below this threshold (most likely + due to the appearance of the IME), then drop the multiuser selector. + Landscape's layout allows this to be smaller than for portrait. --> + <dimen name="kg_squashed_layout_threshold">400dp</dimen> + </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 148560a..3b7d73a 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -404,6 +404,12 @@ --> <integer name="config_longPressOnPowerBehavior">1</integer> + <!-- Package name for default keyguard appwidget [DO NOT TRANSLATE] --> + <string name="widget_default_package_name"></string> + + <!-- Class name for default keyguard appwidget [DO NOT TRANSLATE] --> + <string name="widget_default_class_name"></string> + <!-- Indicate whether the SD card is accessible without removing the battery. --> <bool name="config_batterySdCardAccessibility">false</bool> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 4966b97..3a24cc1 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -330,4 +330,9 @@ <!-- 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> + + <!-- If the height if keyguard drops below this threshold (most likely + due to the appearance of the IME), then drop the multiuser selector. --> + <dimen name="kg_squashed_layout_threshold">600dp</dimen> + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4495316..caa8f02 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2065,15 +2065,6 @@ <!-- This can be used in any application wanting to disable the text "Emergency number" --> <string name="emergency_call_dialog_number_for_display">Emergency number</string> - <!-- String to display if the clock status widget is selected (it is the default) [CHAR LIMIT=22] --> - <string name="widget_default" msgid="8269383575996003796">Clock</string> - - <!-- Package name for default widget [DO NOT TRANSLATE] --> - <string name="widget_default_package_name">com.android.deskclock</string> - - <!-- Class name for default widget [DO NOT TRANSLATE] --> - <string name="widget_default_class_name">com.android.deskclock.DeskClock</string> - <!-- *** touch based lock / unlock *** --> <skip /> @@ -3978,6 +3969,9 @@ you will be asked to unlock your phone using an email account.\n\n Try again in <xliff:g id="number">%d</xliff:g> seconds. </string> + <!-- Sequence of characters used to separate message strings in keyguard. Typically just em-dash + with spaces on either side. [CHAR LIMIT=3] --> + <string name="kg_text_message_separator" product="default"> \u2014 </string> <!-- Message shown in dialog when user is attempting to set the music volume above the recommended maximum level for headphones --> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 180f864..f489786 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -2482,13 +2482,14 @@ please see styles_device_defaults.xml. <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> + <item name="android:paddingBottom">10dp</item> + <item name="android:paddingLeft">20dp</item> </style> <style name="TextAppearance.NumPadKey" parent="@android:style/TextAppearance"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 05edbcc..a12c14c 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -479,7 +479,6 @@ <java-symbol type="string" name="emailTypeOther" /> <java-symbol type="string" name="emailTypeWork" /> <java-symbol type="string" name="emergency_call_dialog_number_for_display" /> - <java-symbol type="string" name="widget_default" /> <java-symbol type="string" name="widget_default_package_name" /> <java-symbol type="string" name="widget_default_class_name" /> <java-symbol type="string" name="emergency_calls_only" /> @@ -1235,6 +1234,7 @@ <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="dimen" name="kg_squashed_layout_threshold" /> <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" /> @@ -1319,6 +1319,7 @@ <java-symbol type="id" name="keyguard_sim_puk_view" /> <java-symbol type="id" name="keyguard_account_view" /> <java-symbol type="id" name="keyguard_selector_fade_container" /> + <java-symbol type="id" name="keyguard_widget_pager_delete_target" /> <java-symbol type="id" name="app_widget_container" /> <java-symbol type="id" name="view_flipper" /> <java-symbol type="id" name="emergency_call_button" /> @@ -1327,9 +1328,7 @@ <java-symbol type="id" name="lockPatternView" /> <java-symbol type="id" name="forgot_password_button" /> <java-symbol type="id" name="glow_pad_view" /> - <java-symbol type="id" name="sim_pin_entry" /> <java-symbol type="id" name="delete_button" /> - <java-symbol type="id" name="sim_pin_entry" /> <java-symbol type="id" name="keyguard_user_avatar" /> <java-symbol type="id" name="keyguard_user_name" /> <java-symbol type="id" name="keyguard_transport_control" /> @@ -1478,6 +1477,7 @@ <java-symbol type="string" name="kg_failed_attempts_almost_at_login" /> <java-symbol type="string" name="kg_enter_confirm_pin_hint" /> <java-symbol type="string" name="kg_invalid_confirm_pin_hint" /> + <java-symbol type="string" name="kg_text_message_separator" /> <!-- From services --> <java-symbol type="anim" name="screen_rotate_0_enter" /> 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 983328d..5eeef93 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -224,6 +224,8 @@ public class NavigationBarView extends LinearLayout { (0 != (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT)) ? (mVertical ? mBackAltLandIcon : mBackAltIcon) : (mVertical ? mBackLandIcon : mBackIcon)); + + setDisabledFlags(mDisabledFlags, true); } public void setDisabledFlags(int disabledFlags) { @@ -237,7 +239,8 @@ public class NavigationBarView extends LinearLayout { final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); final boolean disableRecent = ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0); - final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0); + final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0) + && ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0); final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0); if (SLIPPERY_WHEN_DISABLED) { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java index 2728bfc..893df26 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java @@ -53,13 +53,14 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli private final Callbacks mCallbacks; private final WindowManager mWindowManager; private final Point mRenderedSize = new Point(); + private final int[] mScreenLocation = new int[2]; private View mWidgetView; private long mLaunchCameraStart; private boolean mActive; - private boolean mChallengeActive; private boolean mTransitioning; private boolean mDown; + private boolean mWindowFocused; private final Runnable mLaunchCameraRunnable = new Runnable() { @Override @@ -186,7 +187,7 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli } private void transitionToCamera() { - if (mTransitioning || mChallengeActive || mDown) return; + if (mTransitioning || mDown) return; mTransitioning = true; @@ -233,7 +234,7 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli public void onClick(View v) { if (DEBUG) Log.d(TAG, "clicked"); if (mTransitioning) return; - if (mActive && !mChallengeActive) { + if (mActive) { cancelTransitionToCamera(); transitionToCamera(); } @@ -242,6 +243,7 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli @Override public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); + mWindowFocused = hasWindowFocus; if (DEBUG) Log.d(TAG, "onWindowFocusChanged: " + hasWindowFocus); if (!hasWindowFocus) { mTransitioning = false; @@ -265,13 +267,29 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli } @Override - public boolean onUserInteraction(int action) { - if (mTransitioning) return true; - if (DEBUG) Log.d(TAG, "onUserInteraction " + action); + public boolean onUserInteraction(MotionEvent event) { + if (!mWindowFocused) { + if (DEBUG) Log.d(TAG, "onUserInteraction eaten: !mWindowFocused"); + return true; + } + if (mTransitioning) { + if (DEBUG) Log.d(TAG, "onUserInteraction eaten: mTransitioning"); + return true; + } + + getLocationOnScreen(mScreenLocation); + int rawBottom = mScreenLocation[1] + getHeight(); + if (event.getRawY() > rawBottom) { + if (DEBUG) Log.d(TAG, "onUserInteraction eaten: below widget"); + return true; + } + + int action = event.getAction(); mDown = action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE; - if (mActive && !mChallengeActive) { + if (mActive) { rescheduleTransitionToCamera(); } + if (DEBUG) Log.d(TAG, "onUserInteraction observed, not eaten"); return false; } @@ -282,20 +300,6 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli 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(); - } - } - } - public void onScreenTurnedOff() { if (DEBUG) Log.d(TAG, "onScreenTurnedOff"); reset(); @@ -321,7 +325,6 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli if (DEBUG) Log.d(TAG, "reset"); mLaunchCameraStart = 0; mTransitioning = false; - mChallengeActive = false; mDown = false; cancelTransitionToCamera(); animate().cancel(); @@ -347,6 +350,7 @@ public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnCli WindowManager.LayoutParams wlp = (WindowManager.LayoutParams) lp; int newWindowAnimations = isEnabled ? com.android.internal.R.style.Animation_LockScreen : 0; if (newWindowAnimations != wlp.windowAnimations) { + if (DEBUG) Log.d(TAG, "setting windowAnimations to: " + newWindowAnimations); wlp.windowAnimations = newWindowAnimations; mWindowManager.updateViewLayout(root, wlp); } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java b/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java index b031baf..f3ea992 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java @@ -27,6 +27,8 @@ import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.widget.LockPatternUtils; public class CarrierText extends TextView { + private static CharSequence mSeparator; + private LockPatternUtils mLockPatternUtils; private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { @@ -82,6 +84,7 @@ public class CarrierText extends TextView { @Override protected void onFinishInflate() { super.onFinishInflate(); + mSeparator = getResources().getString(R.string.kg_text_message_separator); KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mCallback); setSelected(true); // Allow marquee to work. } @@ -202,7 +205,7 @@ public class CarrierText extends TextView { final boolean plmnValid = !TextUtils.isEmpty(plmn); final boolean spnValid = !TextUtils.isEmpty(spn); if (plmnValid && spnValid) { - return plmn + "|" + spn; + return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString(); } else if (plmnValid) { return plmn; } else if (spnValid) { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java index db36bcc..71526d2 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardAbsKeyInputView.java @@ -130,6 +130,15 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout return mPasswordEntry.requestFocus(direction, previouslyFocusedRect); } + /* + * Override this if you have a different string for "wrong password" + * + * Note that PIN/PUK have their own implementation of verifyPasswordAndUnlock and so don't need this + */ + protected int getWrongPasswordStringId() { + return R.string.kg_wrong_password; + } + protected void verifyPasswordAndUnlock() { String entry = mPasswordEntry.getText().toString(); if (mLockPatternUtils.checkPassword(entry)) { @@ -144,7 +153,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); handleAttemptLockout(deadline); } - mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pin, true); + mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true); } mPasswordEntry.setText(""); } @@ -164,6 +173,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout @Override public void onFinish() { + mSecurityMessageDisplay.setMessage("", false); resetState(); } }.start(); 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 ca3d0a2..d286564 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -29,8 +29,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Canvas; @@ -50,7 +48,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; -import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AnimationUtils; import android.widget.RemoteViews.OnClickHandler; @@ -59,7 +56,6 @@ import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityM import com.android.internal.widget.LockPatternUtils; import java.io.File; -import java.util.ArrayList; import java.util.List; public class KeyguardHostView extends KeyguardViewBase { @@ -68,10 +64,10 @@ public class KeyguardHostView extends KeyguardViewBase { // Use this to debug all of keyguard public static boolean DEBUG = KeyguardViewMediator.DEBUG; - // also referenced in SecuritySettings.java static final int APPWIDGET_HOST_ID = 0x4B455947; private AppWidgetHost mAppWidgetHost; + private AppWidgetManager mAppWidgetManager; private KeyguardWidgetPager mAppWidgetContainer; private KeyguardSecurityViewFlipper mSecurityViewContainer; private KeyguardSelectorView mKeyguardSelectorView; @@ -113,6 +109,7 @@ public class KeyguardHostView extends KeyguardViewBase { mLockPatternUtils = new LockPatternUtils(context); mAppWidgetHost = new AppWidgetHost( context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper()); + mAppWidgetManager = AppWidgetManager.getInstance(mContext); mSecurityModel = new KeyguardSecurityModel(context); // The following enables the MENU key to work for testing automation @@ -153,15 +150,13 @@ public class KeyguardHostView extends KeyguardViewBase { protected void onFinishInflate() { // Grab instances of and make any necessary changes to the main layouts. Create // view state manager and wire up necessary listeners / callbacks. + View deleteDropTarget = findViewById(R.id.keyguard_widget_pager_delete_target); mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container); mAppWidgetContainer.setVisibility(VISIBLE); mAppWidgetContainer.setCallbacks(mWidgetCallbacks); + mAppWidgetContainer.setDeleteDropTarget(deleteDropTarget); mAppWidgetContainer.setMinScale(0.5f); - addDefaultWidgets(); - addWidgetsFromSettings(); - mSwitchPageRunnable.run(); - SlidingChallengeLayout slider = (SlidingChallengeLayout) findViewById(R.id.sliding_layout); if (slider != null) { @@ -183,8 +178,11 @@ public class KeyguardHostView extends KeyguardViewBase { setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK); } - showPrimarySecurityScreen(false); + addDefaultWidgets(); + addWidgetsFromSettings(); + mSwitchPageRunnable.run(); + showPrimarySecurityScreen(false); updateSecurityViews(); } @@ -549,8 +547,6 @@ public class KeyguardHostView extends KeyguardViewBase { }; }; - private KeyguardStatusViewManager mKeyguardStatusViewManager; - // Used to ignore callbacks from methods that are no longer current (e.g. face unlock). // This avoids unwanted asynchronous events from messing with the state. private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() { @@ -590,6 +586,8 @@ public class KeyguardHostView extends KeyguardViewBase { } }; + protected boolean mShowSecurityWhenReturn; + @Override public void reset() { mIsVerifyUnlockOnly = false; @@ -715,6 +713,7 @@ public class KeyguardHostView extends KeyguardViewBase { // biometric unlock to start next time keyguard is shown. KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true); saveStickyWidgetIndex(); + checkAppWidgetConsistency(); showPrimarySecurityScreen(true); getSecurityView(mCurrentSecuritySelection).onPause(); CameraWidgetFrame cameraPage = findCameraPage(); @@ -812,15 +811,16 @@ public class KeyguardHostView extends KeyguardViewBase { } } - private void addWidget(int appId, int pageIndex) { - AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); - AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId); + private boolean addWidget(int appId, int pageIndex) { + AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId); if (appWidgetInfo != null) { AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo); addWidget(view, pageIndex); + return true; } else { Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting"); mLockPatternUtils.removeAppWidget(appId); + return false; } } @@ -842,11 +842,11 @@ public class KeyguardHostView extends KeyguardViewBase { SlidingChallengeLayout slider = locateSlider(); if (slider != null) { slider.setHandleAlpha(1); - slider.showChallenge(true); } + mShowSecurityWhenReturn = true; } - private SlidingChallengeLayout locateSlider() { + public SlidingChallengeLayout locateSlider() { return (SlidingChallengeLayout) findViewById(R.id.sliding_layout); } }; @@ -890,22 +890,7 @@ public class KeyguardHostView extends KeyguardViewBase { @Override public void run() { - int defaultIconId = 0; - Resources res = KeyguardHostView.this.getContext().getResources(); - ComponentName clock = new ComponentName( - res.getString(R.string.widget_default_package_name), - res.getString(R.string.widget_default_class_name)); - try { - ActivityInfo activityInfo = - mContext.getPackageManager().getActivityInfo(clock, 0); - if (activityInfo != null) { - defaultIconId = activityInfo.icon; - } - } catch (PackageManager.NameNotFoundException e) { - defaultIconId = 0; - } - launchPickActivityIntent(R.string.widget_default, defaultIconId, clock, - LockPatternUtils.EXTRA_DEFAULT_WIDGET); + launchPickActivityIntent(); } }); mCallback.dismiss(false); @@ -916,8 +901,7 @@ public class KeyguardHostView extends KeyguardViewBase { initializeTransportControl(); } - private void launchPickActivityIntent(int defaultLabelId, int defaultIconId, - ComponentName defaultComponentName, String defaultTag) { + private void launchPickActivityIntent() { // Create intent to pick widget Intent pickIntent = new Intent(AppWidgetManager.ACTION_KEYGUARD_APPWIDGET_PICK); @@ -928,22 +912,6 @@ public class KeyguardHostView extends KeyguardViewBase { pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER, AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD); - // Add an custom entry for the default - AppWidgetProviderInfo defaultInfo = new AppWidgetProviderInfo(); - ArrayList<AppWidgetProviderInfo> extraInfos = new ArrayList<AppWidgetProviderInfo>(); - defaultInfo.label = getResources().getString(defaultLabelId); - defaultInfo.icon = defaultIconId; - defaultInfo.provider = defaultComponentName; - extraInfos.add(defaultInfo); - - ArrayList<Bundle> extraExtras = new ArrayList<Bundle>(); - Bundle b = new Bundle(); - b.putBoolean(defaultTag, true); - extraExtras.add(b); - - // Launch the widget picker - pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_INFO, extraInfos); - pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS, extraExtras); pickIntent.putExtra(Intent.EXTRA_INTENT, getBaseIntent()); pickIntent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK @@ -1024,6 +992,22 @@ public class KeyguardHostView extends KeyguardViewBase { } } + private int getAddPageIndex() { + 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; + } + return addPageIndex; + } + + private void addDefaultStatusWidget(int index) { + LayoutInflater inflater = LayoutInflater.from(mContext); + View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true); + mAppWidgetContainer.addWidget(statusWidget, index); + } + private void addWidgetsFromSettings() { DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); @@ -1036,23 +1020,17 @@ public class KeyguardHostView extends KeyguardViewBase { } } - 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; - } + int addPageIndex = getAddPageIndex(); // Add user-selected widget 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); + addDefaultStatusWidget(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 @@ -1061,6 +1039,42 @@ public class KeyguardHostView extends KeyguardViewBase { } } } + checkAppWidgetConsistency(); + } + + public void checkAppWidgetConsistency() { + final int childCount = mAppWidgetContainer.getChildCount(); + boolean widgetPageExists = false; + for (int i = 0; i < childCount; i++) { + if (isWidgetPage(i)) { + widgetPageExists = true; + break; + } + } + if (!widgetPageExists) { + final int addPageIndex = getAddPageIndex(); + + Resources res = getContext().getResources(); + ComponentName defaultAppWidget = new ComponentName( + res.getString(R.string.widget_default_package_name), + res.getString(R.string.widget_default_class_name)); + + // Note: we don't support configuring the widget + int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); + boolean bindSuccessful = false; + try { + mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget); + bindSuccessful = true; + } catch (IllegalArgumentException e) { + Log.e(TAG, "Error when trying to bind default AppWidget: " + e); + } + // Use the built-in status/clock view if we can't inflate the default widget + if (!(bindSuccessful && addWidget(appWidgetId, addPageIndex + 1))) { + addDefaultStatusWidget(addPageIndex + 1); + } + mAppWidgetContainer.onAddView( + mAppWidgetContainer.getChildAt(addPageIndex + 1), addPageIndex + 1); + } } Runnable mSwitchPageRunnable = new Runnable() { @@ -1129,6 +1143,14 @@ public class KeyguardHostView extends KeyguardViewBase { if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused")); if (!hasWindowFocus) { saveStickyWidgetIndex(); + } else if (mShowSecurityWhenReturn) { + SlidingChallengeLayout slider = + (SlidingChallengeLayout) findViewById(R.id.sliding_layout); + if (slider != null) { + slider.setHandleAlpha(1); + slider.showChallenge(true); + } + mShowSecurityWhenReturn = false; } } @@ -1155,6 +1177,15 @@ public class KeyguardHostView extends KeyguardViewBase { return null; } + private boolean isWidgetPage(int pageIndex) { + View v = mAppWidgetContainer.getChildAt(pageIndex); + if (v != null && v instanceof KeyguardWidgetFrame) { + KeyguardWidgetFrame kwf = (KeyguardWidgetFrame) v; + return kwf.getContentAppWidgetId() != AppWidgetManager.INVALID_APPWIDGET_ID; + } + return false; + } + private boolean isCameraPage(int pageIndex) { View v = mAppWidgetContainer.getChildAt(pageIndex); return v != null && v instanceof CameraWidgetFrame; diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java index 5e331e1..f6f3fab 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java @@ -43,7 +43,6 @@ class KeyguardMessageArea extends TextView { static final int SECURITY_MESSAGE_DURATION = 5000; protected static final int FADE_DURATION = 750; - static final String SEPARATOR = " "; // are we showing battery information? boolean mShowingBatteryInfo = false; @@ -143,6 +142,8 @@ class KeyguardMessageArea extends TextView { } }; + private CharSequence mSeparator; + public KeyguardMessageArea(Context context) { this(context, null); } @@ -158,6 +159,8 @@ class KeyguardMessageArea extends TextView { mUpdateMonitor.registerCallback(mInfoCallback); mHandler = new Handler(Looper.myLooper()); + mSeparator = getResources().getString(R.string.kg_text_message_separator); + update(); } @@ -186,23 +189,23 @@ class KeyguardMessageArea extends TextView { setText(status); } - - private CharSequence concat(Object... args) { + private CharSequence concat(CharSequence... 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); + if (!TextUtils.isEmpty(args[0])) { + b.append(args[0]); + } + for (int i = 1; i < args.length; i++) { + CharSequence text = args[i]; + if (!TextUtils.isEmpty(text)) { + if (b.length() > 0) { + b.append(mSeparator); + } + b.append(text); } } return b.toString(); } - CharSequence getCurrentMessage() { return mShowingMessage ? mMessage : null; } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java index b7d9990..fa80352 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java @@ -65,10 +65,12 @@ public class KeyguardPINView extends KeyguardAbsKeyInputView @Override public void onClick(View v) { doHapticKeyClick(); - verifyPasswordAndUnlock(); + if (mPasswordEntry.isEnabled()) { + verifyPasswordAndUnlock(); + } } }); - ok.setOnHoverListener(new NumPadKey.LiftToActivateListener(getContext())); + ok.setOnHoverListener(new LiftToActivateListener(getContext())); } // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts, @@ -78,16 +80,22 @@ public class KeyguardPINView extends KeyguardAbsKeyInputView 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)); + // check for time-based lockouts + if (mPasswordEntry.isEnabled()) { + 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(""); + // check for time-based lockouts + if (mPasswordEntry.isEnabled()) { + mPasswordEntry.setText(""); + } doHapticKeyClick(); return true; } @@ -104,4 +112,9 @@ public class KeyguardPINView extends KeyguardAbsKeyInputView @Override public void showUsabilityHint() { } + + @Override + public int getWrongPasswordStringId() { + return R.string.kg_wrong_pin; + } } 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 64bbdd3..23ea2e9 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java @@ -195,4 +195,9 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView @Override public void showUsabilityHint() { } + + @Override + public int getWrongPasswordStringId() { + return R.string.kg_wrong_password; + } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java index 04ab0a2..7b11507 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java @@ -1,11 +1,13 @@ package com.android.internal.policy.impl.keyguard; import android.animation.Animator; +import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.view.View; import android.widget.FrameLayout; import com.android.internal.R; @@ -51,18 +53,42 @@ public class KeyguardSecurityContainer extends FrameLayout { } public void showBouncer(int duration) { - SecurityMessageDisplay message = new KeyguardMessageArea.Helper(this); + SecurityMessageDisplay message = new KeyguardMessageArea.Helper(getSecurityView()); message.showBouncer(duration); - Animator anim = ObjectAnimator.ofFloat(this, "BackgroundAlpha", 1f); + AnimatorSet anim = new AnimatorSet(); + anim.playTogether(ObjectAnimator.ofFloat(this, "backgroundAlpha", 1f), getEcaAnim(0f)); anim.setDuration(duration); anim.start(); } public void hideBouncer(int duration) { - SecurityMessageDisplay message = new KeyguardMessageArea.Helper(this); + SecurityMessageDisplay message = new KeyguardMessageArea.Helper(getSecurityView()); message.hideBouncer(duration); - Animator anim = ObjectAnimator.ofFloat(this, "BackgroundAlpha", 0f); + AnimatorSet anim = new AnimatorSet(); + anim.playTogether(ObjectAnimator.ofFloat(this, "backgroundAlpha", 0f), getEcaAnim(1f)); anim.setDuration(duration); anim.start(); } + + View getSecurityView() { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof KeyguardSecurityViewFlipper) { + return (View) (((KeyguardSecurityViewFlipper) child).getSecurityView()); + } + } + return null; + } + + Animator getEcaAnim(float alpha) { + Animator anim = null; + View securityView = getSecurityView(); + if (securityView != null) { + View ecaView = securityView.findViewById(R.id.keyguard_selector_fade_container); + if (ecaView != null) { + anim = ObjectAnimator.ofFloat(ecaView, "alpha", alpha); + } + } + return anim; + } } 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 fcf45ff..ab364ee 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java @@ -16,47 +16,32 @@ package com.android.internal.policy.impl.keyguard; +import com.android.internal.telephony.ITelephony; + +import android.content.Context; import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; -import android.content.Context; -import android.graphics.Rect; import android.os.RemoteException; import android.os.ServiceManager; - -import com.android.internal.telephony.ITelephony; -import com.android.internal.widget.LockPatternUtils; -import com.android.internal.widget.PasswordEntryKeyboardHelper; -import com.android.internal.widget.PasswordEntryKeyboardView; -import com.android.internal.R; - import android.text.Editable; +import android.text.InputType; import android.text.TextWatcher; +import android.text.method.DigitsKeyListener; import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; +import com.android.internal.R; + /** - * Displays a dialer like interface to unlock the SIM PIN. + * Displays a PIN pad for unlocking. */ -public class KeyguardSimPinView extends LinearLayout +public class KeyguardSimPinView extends KeyguardAbsKeyInputView implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { - private EditText mPinEntry; private ProgressDialog mSimUnlockProgressDialog = null; - private KeyguardSecurityCallback mCallback; - private PasswordEntryKeyboardView mKeyboardView; - private PasswordEntryKeyboardHelper mKeyboardHelper; - private LockPatternUtils mLockPatternUtils; - private SecurityMessageDisplay mSecurityMessageDisplay; - private volatile boolean mSimCheckInProgress; public KeyguardSimPinView(Context context) { @@ -65,68 +50,69 @@ public class KeyguardSimPinView extends LinearLayout public KeyguardSimPinView(Context context, AttributeSet attrs) { super(context, attrs); - mLockPatternUtils = new LockPatternUtils(getContext()); } - public void setKeyguardCallback(KeyguardSecurityCallback callback) { - mCallback = callback; + public void resetState() { + mSecurityMessageDisplay.setMessage(R.string.kg_sim_pin_instructions, true); + mPasswordEntry.setEnabled(true); + } + + @Override + protected int getPasswordTextViewId() { + return R.id.pinEntry; } @Override protected void onFinishInflate() { super.onFinishInflate(); - mPinEntry = (EditText) findViewById(R.id.sim_pin_entry); - mPinEntry.setOnEditorActionListener(this); - mPinEntry.addTextChangedListener(this); - - mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard); - 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.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC); - mKeyboardHelper.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled()); - - final View deleteButton = findViewById(R.id.delete_button); - if (deleteButton != null) { - deleteButton.setOnClickListener(new OnClickListener() { + final View ok = findViewById(R.id.key_enter); + if (ok != null) { + ok.setOnClickListener(new View.OnClickListener() { + @Override public void onClick(View v) { - mKeyboardHelper.handleBackspace(); + doHapticKeyClick(); + verifyPasswordAndUnlock(); } }); } - mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); - mSecurityMessageDisplay.setTimeout(0); - reset(); - } - - @Override - protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { - return mPinEntry.requestFocus(direction, previouslyFocusedRect); - } + // 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; + } + }); + } - public void reset() { - // start fresh - mSecurityMessageDisplay.setMessage(R.string.kg_sim_pin_instructions, true); + mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance()); + mPasswordEntry.setInputType(InputType.TYPE_CLASS_NUMBER + | InputType.TYPE_NUMBER_VARIATION_PASSWORD); - // make sure that the number of entered digits is consistent when we - // erase the SIM unlock code, including orientation changes. - mPinEntry.setText(""); - mPinEntry.requestFocus(); + mPasswordEntry.requestFocus(); } @Override public void showUsabilityHint() { } - /** {@inheritDoc} */ - public void cleanUp() { + @Override + public void onPause() { // dismiss the dialog. if (mSimUnlockProgressDialog != null) { mSimUnlockProgressDialog.dismiss(); @@ -167,19 +153,6 @@ public class KeyguardSimPinView extends LinearLayout } } - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - // Check if this was the result of hitting the enter key - mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS); - if (event.getAction() == MotionEvent.ACTION_DOWN && ( - actionId == EditorInfo.IME_NULL - || actionId == EditorInfo.IME_ACTION_DONE - || actionId == EditorInfo.IME_ACTION_NEXT)) { - checkPin(); - return true; - } - return false; - } - private Dialog getSimUnlockProgressDialog() { if (mSimUnlockProgressDialog == null) { mSimUnlockProgressDialog = new ProgressDialog(mContext); @@ -195,11 +168,14 @@ public class KeyguardSimPinView extends LinearLayout return mSimUnlockProgressDialog; } - private void checkPin() { - if (mPinEntry.getText().length() < 4) { + @Override + protected void verifyPasswordAndUnlock() { + String entry = mPasswordEntry.getText().toString(); + + if (entry.length() < 4) { // otherwise, display a message to the user, and don't submit. mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint, true); - mPinEntry.setText(""); + mPasswordEntry.setText(""); mCallback.userActivity(0); return; } @@ -208,7 +184,7 @@ public class KeyguardSimPinView extends LinearLayout if (!mSimCheckInProgress) { mSimCheckInProgress = true; // there should be only one - new CheckSimPin(mPinEntry.getText().toString()) { + new CheckSimPin(mPasswordEntry.getText().toString()) { void onSimCheckResponse(final boolean success) { post(new Runnable() { public void run() { @@ -223,7 +199,7 @@ public class KeyguardSimPinView extends LinearLayout } else { mSecurityMessageDisplay.setMessage (R.string.kg_password_wrong_pin_code, true); - mPinEntry.setText(""); + mPasswordEntry.setText(""); } mCallback.userActivity(0); mSimCheckInProgress = false; @@ -233,40 +209,5 @@ public class KeyguardSimPinView extends LinearLayout }.start(); } } - - public void setLockPatternUtils(LockPatternUtils utils) { - mLockPatternUtils = utils; - } - - public boolean needsInput() { - return false; // This view provides its own keypad - } - - public void onPause() { - - } - - public void onResume() { - reset(); - } - - 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) { - } - } + 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 0465805..e5b4b73 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java @@ -19,49 +19,30 @@ import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; -import android.graphics.Rect; import android.os.RemoteException; import android.os.ServiceManager; import android.text.Editable; +import android.text.InputType; import android.text.TextWatcher; +import android.text.method.DigitsKeyListener; import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; -import android.widget.LinearLayout; -import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import com.android.internal.telephony.ITelephony; -import com.android.internal.widget.LockPatternUtils; -import com.android.internal.widget.PasswordEntryKeyboardHelper; -import com.android.internal.widget.PasswordEntryKeyboardView; -import com.android.internal.R; -public class KeyguardSimPukView extends LinearLayout implements View.OnClickListener, - KeyguardSecurityView, OnEditorActionListener, TextWatcher { +import com.android.internal.R; - private View mDeleteButton; +/** + * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier. + */ +public class KeyguardSimPukView extends KeyguardAbsKeyInputView + implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { private ProgressDialog mSimUnlockProgressDialog = null; - private KeyguardSecurityCallback mCallback; - - private SecurityMessageDisplay mSecurityMessageDisplay; - - private PasswordEntryKeyboardView mKeyboardView; - - private PasswordEntryKeyboardHelper mKeyboardHelper; - - private LockPatternUtils mLockPatternUtils; - private volatile boolean mCheckInProgress; - - private TextView mSimPinEntry; - private String mPukText; - private String mPinText; private StateMachine mStateMachine = new StateMachine(); @@ -95,10 +76,11 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList com.android.internal.R.string.lockscreen_sim_unlock_progress_dialog_message; updateSim(); } else { + state = ENTER_PIN; // try again? msg = R.string.kg_invalid_confirm_pin_hint; } } - mSimPinEntry.setText(null); + mPasswordEntry.setText(null); if (msg != 0) { mSecurityMessageDisplay.setMessage(msg, true); } @@ -109,7 +91,7 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList mPukText=""; state = ENTER_PUK; mSecurityMessageDisplay.setMessage(R.string.kg_puk_enter_puk_hint, true); - mSimPinEntry.requestFocus(); + mPasswordEntry.requestFocus(); } } @@ -119,62 +101,71 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList public KeyguardSimPukView(Context context, AttributeSet attrs) { super(context, attrs); - mLockPatternUtils = new LockPatternUtils(getContext()); } - public void setKeyguardCallback(KeyguardSecurityCallback callback) { - mCallback = callback; - mLockPatternUtils = new LockPatternUtils(getContext()); + public void resetState() { + mStateMachine.reset(); + mPasswordEntry.setEnabled(true); } @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mSimPinEntry = (TextView) findViewById(R.id.sim_pin_entry); - mSimPinEntry.setOnEditorActionListener(this); - mSimPinEntry.addTextChangedListener(this); - mDeleteButton = findViewById(R.id.delete_button); - mDeleteButton.setOnClickListener(this); - mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard); - 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.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(); + protected int getPasswordTextViewId() { + return R.id.pinEntry; } @Override - protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { - return mSimPinEntry.requestFocus(direction, previouslyFocusedRect); - } + protected void onFinishInflate() { + super.onFinishInflate(); - public boolean needsInput() { - return false; // This view provides its own keypad - } + final View ok = findViewById(R.id.key_enter); + if (ok != null) { + ok.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + doHapticKeyClick(); + verifyPasswordAndUnlock(); + } + }); + } - public void onPause() { + // 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(); - public void onResume() { - reset(); + mSecurityMessageDisplay.setTimeout(0); // don't show ownerinfo/charging status by default } @Override public void showUsabilityHint() { } - /** {@inheritDoc} */ - public void cleanUp() { + @Override + public void onPause() { // dismiss the dialog. if (mSimUnlockProgressDialog != null) { mSimUnlockProgressDialog.dismiss(); @@ -218,23 +209,11 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList } } - public void onClick(View v) { - if (v == mDeleteButton) { - mSimPinEntry.requestFocus(); - final Editable digits = mSimPinEntry.getEditableText(); - final int len = digits.length(); - if (len > 0) { - digits.delete(len-1, len); - } - } - mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS); - } - private Dialog getSimUnlockProgressDialog() { if (mSimUnlockProgressDialog == null) { mSimUnlockProgressDialog = new ProgressDialog(mContext); - mSimUnlockProgressDialog.setMessage(mContext.getString( - R.string.kg_sim_unlock_progress_dialog_message)); + mSimUnlockProgressDialog.setMessage( + mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); mSimUnlockProgressDialog.setIndeterminate(true); mSimUnlockProgressDialog.setCancelable(false); if (!(mContext instanceof Activity)) { @@ -247,8 +226,8 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList private boolean checkPuk() { // make sure the puk is at least 8 digits long. - if (mSimPinEntry.getText().length() >= 8) { - mPukText = mSimPinEntry.getText().toString(); + if (mPasswordEntry.getText().length() >= 8) { + mPukText = mPasswordEntry.getText().toString(); return true; } return false; @@ -256,16 +235,16 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList private boolean checkPin() { // make sure the PIN is between 4 and 8 digits - int length = mSimPinEntry.getText().length(); + int length = mPasswordEntry.getText().length(); if (length >= 4 && length <= 8) { - mPinText = mSimPinEntry.getText().toString(); + mPinText = mPasswordEntry.getText().toString(); return true; } return false; } public boolean confirmPin() { - return mPinText.equals(mSimPinEntry.getText().toString()); + return mPinText.equals(mPasswordEntry.getText().toString()); } private void updateSim() { @@ -295,46 +274,9 @@ public class KeyguardSimPukView extends LinearLayout implements View.OnClickList } @Override - public boolean onEditorAction(TextView view, int actionId, KeyEvent event) { - // Check if this was the result of hitting the enter key - mCallback.userActivity(KeyguardViewManager.DIGIT_PRESS_WAKE_MILLIS); - if (event.getAction() == MotionEvent.ACTION_DOWN) { - if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE - || actionId == EditorInfo.IME_ACTION_NEXT) { - mStateMachine.next(); - return true; - } - } - return false; - } - - @Override - public void setLockPatternUtils(LockPatternUtils utils) { - mLockPatternUtils = utils; - } - - @Override - public void reset() { - mStateMachine.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); - } + protected void verifyPasswordAndUnlock() { + mStateMachine.next(); } +} - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - @Override - public void afterTextChanged(Editable s) { - } -} 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 fe4ac5b..d0fa81e 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java @@ -520,8 +520,22 @@ public class KeyguardViewMediator { mSystemReady = true; mUpdateMonitor.registerCallback(mUpdateCallback); - // Disable alternate unlock right after boot until things have settled. - mUpdateMonitor.setAlternateUnlockEnabled(false); + // Suppress biometric unlock right after boot until things have settled if it is the + // selected security method, otherwise unsuppress it. It must be unsuppressed if it is + // not the selected security method for the following reason: if the user starts + // without a screen lock selected, the biometric unlock would be suppressed the first + // time they try to use it. + // + // Note that the biometric unlock will still not show if it is not the selected method. + // Calling setAlternateUnlockEnabled(true) simply says don't suppress it if it is the + // selected method. + if (mLockPatternUtils.usingBiometricWeak() + && mLockPatternUtils.isBiometricWeakInstalled()) { + if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot"); + mUpdateMonitor.setAlternateUnlockEnabled(false); + } else { + mUpdateMonitor.setAlternateUnlockEnabled(true); + } doKeyguardLocked(); } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java index c89e880..ddded8e 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java @@ -101,10 +101,21 @@ public class KeyguardViewStateManager implements SlidingChallengeLayout.OnChalle ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration); } - public void onPageSwitch(View newPage, int newPageIndex) { + public void onPageSwitching(View newPage, int newPageIndex) { + if (mPagedView != null && mChallengeLayout instanceof SlidingChallengeLayout) { + boolean isCameraPage = newPage instanceof CameraWidgetFrame; + ((SlidingChallengeLayout) mChallengeLayout).setChallengeInteractive(!isCameraPage); + } + } + + public void onPageSwitched(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 the page hasn't switched, don't bother with any of this + if (mCurrentPage != newPageIndex) return; + if (mPagedView != null && mChallengeLayout != null) { KeyguardWidgetFrame prevPage = mPagedView.getWidgetPageAt(mCurrentPage); if (prevPage != null && mCurrentPage != mPageListeningToSlider) { @@ -162,7 +173,6 @@ public class KeyguardViewStateManager implements SlidingChallengeLayout.OnChalle if (!challengeOverlapping) { frame.resetSize(); } - frame.onChallengeActive(mChallengeLayout.isChallengeShowing()); frame.hideFrame(this); if (challengeOverlapping) { @@ -196,8 +206,6 @@ public class KeyguardViewStateManager implements SlidingChallengeLayout.OnChalle } // View is on the move. Pause the security view until it completes. mKeyguardSecurityContainer.onPause(); - - frame.onChallengeActive(true); } mLastScrollState = scrollState; } 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 b1ff049..9ffabf8 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.appwidget.AppWidgetHostView; +import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; @@ -42,6 +43,10 @@ public class KeyguardWidgetFrame extends FrameLayout { new PorterDuffXfermode(PorterDuff.Mode.ADD); static final float OUTLINE_ALPHA_MULTIPLIER = 0.6f; + static final int HOVER_OVER_DELETE_DROP_TARGET_OVERLAY_COLOR = 0x99FF0000; + + // Temporarily disable this for the time being until we know why the gfx is messing up + static final boolean ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY = true; private int mGradientColor; private LinearGradient mForegroundGradient; @@ -62,7 +67,20 @@ public class KeyguardWidgetFrame extends FrameLayout { private float mBackgroundAlphaMultiplier = 1.0f; private Drawable mBackgroundDrawable; private Rect mBackgroundRect = new Rect(); + private int mLastMeasuredWidth = -1; + private int mLastMeasuredHeight = 1; + + // These variables are all needed in order to size things properly before we're actually + // measured. private int mSmallWidgetHeight; + private int mSmallFrameHeight; + private boolean mWidgetLockedSmall = false; + private int mMaxChallengeTop = -1; + + // This will hold the width value before we've actually been measured + private int mFrameHeight; + + private boolean mIsHoveringOverDeleteDropTarget; // 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. @@ -98,8 +116,13 @@ public class KeyguardWidgetFrame extends FrameLayout { cancelLongPress(); } - public void setMaxChallengeTop(int top) { - mSmallWidgetHeight = top - getPaddingTop(); + void setIsHoveringOverDeleteDropTarget(boolean isHovering) { + if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) { + if (mIsHoveringOverDeleteDropTarget != isHovering) { + mIsHoveringOverDeleteDropTarget = isHovering; + invalidate(); + } + } } @Override @@ -163,6 +186,12 @@ public class KeyguardWidgetFrame extends FrameLayout { c.drawRect(mForegroundRect, mGradientPaint); } + private void drawHoveringOverDeleteOverlay(Canvas c) { + if (mIsHoveringOverDeleteDropTarget) { + c.drawColor(HOVER_OVER_DELETE_DROP_TARGET_OVERLAY_COLOR); + } + } + protected void drawBg(Canvas canvas) { if (mBackgroundAlpha > 0.0f) { Drawable bg = mBackgroundDrawable; @@ -175,9 +204,16 @@ public class KeyguardWidgetFrame extends FrameLayout { @Override protected void dispatchDraw(Canvas canvas) { + if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) { + canvas.save(); + } drawBg(canvas); super.dispatchDraw(canvas); drawGradientOverlay(canvas); + if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) { + drawHoveringOverDeleteOverlay(canvas); + canvas.restore(); + } } /** @@ -220,8 +256,10 @@ public class KeyguardWidgetFrame extends FrameLayout { View content = getContent(); if (content instanceof AppWidgetHostView) { return ((AppWidgetHostView) content).getAppWidgetId(); - } else { + } else if (content instanceof KeyguardStatusView) { return ((KeyguardStatusView) content).getAppWidgetId(); + } else { + return AppWidgetManager.INVALID_APPWIDGET_ID; } } @@ -260,22 +298,6 @@ public class KeyguardWidgetFrame extends FrameLayout { } /** - * 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 @@ -295,28 +317,51 @@ public class KeyguardWidgetFrame extends FrameLayout { } } + public void setMaxChallengeTop(int top) { + boolean dirty = mMaxChallengeTop != top; + mSmallWidgetHeight = top - getPaddingTop(); + mSmallFrameHeight = top + getPaddingBottom(); + if (dirty && mIsSmall) { + setWidgetHeight(mSmallWidgetHeight); + setFrameHeight(mSmallFrameHeight); + } else if (dirty && mWidgetLockedSmall) { + setWidgetHeight(mSmallWidgetHeight); + } + } + public boolean isSmall() { return mIsSmall; } public void adjustFrame(int challengeTop) { - setChallengeTop(challengeTop, false); + int frameHeight = challengeTop + getPaddingBottom(); + setFrameHeight(frameHeight); } public void shrinkWidget() { mIsSmall = true; - setChallengeTop(mSmallWidgetHeight, true); + setWidgetHeight(mSmallWidgetHeight); + setFrameHeight(mSmallFrameHeight); + } + + public void setWidgetLockedSmall(boolean locked) { + if (locked) { + setWidgetHeight(mSmallWidgetHeight); + } + mWidgetLockedSmall = locked; } public void resetSize() { mIsSmall = false; + if (!mWidgetLockedSmall) { + setWidgetHeight(LayoutParams.MATCH_PARENT); + } setFrameHeight(getMeasuredHeight()); - setWidgetHeight(LayoutParams.MATCH_PARENT); } public void setFrameHeight(int height) { - height = Math.min(height, getMeasuredHeight()); - mBackgroundRect.set(0, 0, getMeasuredWidth(), height); + mFrameHeight = height; + mBackgroundRect.set(0, 0, getMeasuredWidth(), Math.min(mFrameHeight, getMeasuredHeight())); invalidate(); } @@ -356,10 +401,38 @@ 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); + + if (!mIsSmall) { + mFrameHeight = h; + } + + mBackgroundRect.set(0, 0, getMeasuredWidth(), Math.min(h, mFrameHeight)); invalidate(); } + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + performAppWidgetSizeCallbacksIfNecessary(); + } + + private void performAppWidgetSizeCallbacksIfNecessary() { + View content = getContent(); + if (!(content instanceof AppWidgetHostView)) return; + + boolean sizeDirty = content.getMeasuredWidth() != mLastMeasuredWidth || + content.getMeasuredHeight() != mLastMeasuredHeight; + if (sizeDirty) { + + } + + AppWidgetHostView awhv = (AppWidgetHostView) content; + float density = getResources().getDisplayMetrics().density; + + int width = (int) (content.getMeasuredWidth() / density); + int height = (int) (content.getMeasuredHeight() / density); + awhv.updateAppWidgetSize(null, width, height, width, height, true); + } + void setOverScrollAmount(float r, boolean left) { if (Float.compare(mOverScrollAmount, r) != 0) { mOverScrollAmount = r; @@ -373,12 +446,8 @@ public class KeyguardWidgetFrame extends FrameLayout { // hook for subclasses } - public boolean onUserInteraction(int action) { + public boolean onUserInteraction(MotionEvent event) { // 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 f04c4df..acb2913 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java @@ -21,6 +21,8 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; +import android.appwidget.AppWidgetHostView; +import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.content.res.Resources; import android.os.Handler; @@ -31,9 +33,9 @@ import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.View.OnLongClickListener; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; -import android.view.ViewGroup; import android.widget.FrameLayout; import com.android.internal.R; @@ -117,7 +119,14 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit } @Override - public void onPageSwitch(View newPage, int newPageIndex) { + public void onPageSwitching(View newPage, int newPageIndex) { + if (mViewStateManager != null) { + mViewStateManager.onPageSwitching(newPage, newPageIndex); + } + } + + @Override + public void onPageSwitched(View newPage, int newPageIndex) { boolean showingStatusWidget = false; if (newPage instanceof ViewGroup) { ViewGroup vg = (ViewGroup) newPage; @@ -156,7 +165,7 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit } } if (mViewStateManager != null) { - mViewStateManager.onPageSwitch(newPage, newPageIndex); + mViewStateManager.onPageSwitched(newPage, newPageIndex); } } @@ -177,7 +186,7 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit @Override public boolean onTouchEvent(MotionEvent ev) { KeyguardWidgetFrame currentWidgetPage = getWidgetPageAt(getCurrentPage()); - if (currentWidgetPage != null && currentWidgetPage.onUserInteraction(ev.getAction())) { + if (currentWidgetPage != null && currentWidgetPage.onUserInteraction(ev)) { return true; } return super.onTouchEvent(ev); @@ -250,10 +259,23 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit 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); frame.addView(widget, lp); + + // We set whether or not this widget supports vertical resizing. + if (widget instanceof AppWidgetHostView) { + AppWidgetHostView awhv = (AppWidgetHostView) widget; + AppWidgetProviderInfo info = awhv.getAppWidgetInfo(); + if ((info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) { + frame.setWidgetLockedSmall(false); + } else { + // Lock the widget to be small. + frame.setWidgetLockedSmall(true); + } + } } else { frame = (KeyguardWidgetFrame) widget; } @@ -549,20 +571,20 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit // 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(); + 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); + mHasMeasure = true; } void animateOutlinesAndSidePages(final boolean show) { @@ -695,4 +717,10 @@ public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwit return indexOfChild((KeyguardWidgetFrame)view.getParent()); } } + + @Override + protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) { + KeyguardWidgetFrame child = getWidgetPageAt(viewIndex); + child.setIsHoveringOverDeleteDropTarget(isHovering); + } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/LiftToActivateListener.java b/policy/src/com/android/internal/policy/impl/keyguard/LiftToActivateListener.java new file mode 100644 index 0000000..818108c --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/keyguard/LiftToActivateListener.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2013 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.view.MotionEvent; +import android.view.View; +import android.view.accessibility.AccessibilityManager; + +/** + * Hover listener that implements lift-to-activate interaction for + * accessibility. May be added to multiple views. + */ +class LiftToActivateListener implements View.OnHoverListener { + /** Manager used to query accessibility enabled state. */ + private final AccessibilityManager mAccessibilityManager; + + private boolean mCachedClickableState; + + public LiftToActivateListener(Context context) { + mAccessibilityManager = (AccessibilityManager) context.getSystemService( + Context.ACCESSIBILITY_SERVICE); + } + + @Override + public boolean onHover(View v, MotionEvent event) { + // When touch exploration is turned on, lifting a finger while + // inside the view bounds should perform a click action. + if (mAccessibilityManager.isEnabled() + && mAccessibilityManager.isTouchExplorationEnabled()) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_HOVER_ENTER: + // Lift-to-type temporarily disables double-tap + // activation by setting the view as not clickable. + mCachedClickableState = v.isClickable(); + v.setClickable(false); + break; + case MotionEvent.ACTION_HOVER_EXIT: + final int x = (int) event.getX(); + final int y = (int) event.getY(); + if ((x > v.getPaddingLeft()) && (y > v.getPaddingTop()) + && (x < v.getWidth() - v.getPaddingRight()) + && (y < v.getHeight() - v.getPaddingBottom())) { + v.performClick(); + } + v.setClickable(mCachedClickableState); + break; + } + } + + // Pass the event to View.onHoverEvent() to handle accessibility. + v.onHoverEvent(event); + + // Consume the event so it doesn't fall through to other views. + return true; + } +}
\ No newline at end of file diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java index b38eb28..3bc39eb 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java @@ -47,6 +47,7 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo private OnBouncerStateChangedListener mBouncerListener; private final Rect mTempRect = new Rect(); + private final Context mContext; private final OnClickListener mScrimClickListener = new OnClickListener() { @Override @@ -66,6 +67,8 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo public MultiPaneChallengeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + mContext = context; + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiPaneChallengeLayout, defStyleAttr, 0); mOrientation = a.getInt(R.styleable.MultiPaneChallengeLayout_orientation, @@ -173,6 +176,8 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo throw new IllegalArgumentException( "MultiPaneChallengeLayout must be measured with an exact size"); } + float squashedLayoutThreshold = + mContext.getResources().getDimension(R.dimen.kg_squashed_layout_threshold); final int width = MeasureSpec.getSize(widthSpec); final int height = MeasureSpec.getSize(heightSpec); @@ -208,28 +213,32 @@ public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayo 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; + if (height < squashedLayoutThreshold) { + int zero = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY); + measureChild(child, zero, zero); + } else { + 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); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java index ca36007..a0038bc 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java @@ -22,9 +22,7 @@ import android.text.SpannableStringBuilder; import android.text.style.TextAppearanceSpan; import android.util.AttributeSet; import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; import android.view.View; -import android.view.accessibility.AccessibilityManager; import android.widget.Button; import android.widget.TextView; @@ -51,7 +49,8 @@ public class NumPadKey extends Button { } } } - if (mTextView != null) { + // check for time-based lockouts + if (mTextView != null && mTextView.isEnabled()) { mTextView.append(String.valueOf(mDigit)); } doHapticKeyClick(); @@ -75,6 +74,7 @@ public class NumPadKey extends Button { setOnClickListener(mListener); setOnHoverListener(new LiftToActivateListener(context)); + setAccessibilityDelegate(new ObscureSpeechDelegate(context)); mEnableHaptics = new LockPatternUtils(context).isTactileFeedbackEnabled(); @@ -89,6 +89,7 @@ public class NumPadKey extends Button { final String extra = sKlondike[mDigit]; final int extraLen = extra.length(); if (extraLen > 0) { + builder.append(" "); builder.append(extra); builder.setSpan( new TextAppearanceSpan(context, R.style.TextAppearance_NumPadKey_Klondike), @@ -99,6 +100,14 @@ public class NumPadKey extends Button { setText(builder); } + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + // Reset the "announced headset" flag when detached. + ObscureSpeechDelegate.sAnnouncedHeadset = false; + } + public void setTextView(TextView tv) { mTextView = tv; } @@ -116,45 +125,4 @@ public class NumPadKey extends Button { | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); } } - - /** - * Hover listener that implements lift-to-activate interaction for - * accessibility. May be added to multiple views. - */ - static class LiftToActivateListener implements View.OnHoverListener { - /** Manager used to query accessibility enabled state. */ - private final AccessibilityManager mAccessibilityManager; - - public LiftToActivateListener(Context context) { - mAccessibilityManager = (AccessibilityManager) context.getSystemService( - Context.ACCESSIBILITY_SERVICE); - } - - @Override - public boolean onHover(View v, MotionEvent event) { - // When touch exploration is turned on, lifting a finger while - // inside the view bounds should perform a click action. - if (mAccessibilityManager.isEnabled() - && mAccessibilityManager.isTouchExplorationEnabled()) { - switch (event.getActionMasked()) { - case MotionEvent.ACTION_HOVER_ENTER: - // Lift-to-type temporarily disables double-tap - // activation. - v.setClickable(false); - break; - case MotionEvent.ACTION_HOVER_EXIT: - final int x = (int) event.getX(); - final int y = (int) event.getY(); - if ((x > v.getPaddingLeft()) && (y > v.getPaddingTop()) - && (x < v.getWidth() - v.getPaddingRight()) - && (y < v.getHeight() - v.getPaddingBottom())) { - v.performClick(); - } - v.setClickable(true); - break; - } - } - return false; - } - } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/ObscureSpeechDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/ObscureSpeechDelegate.java new file mode 100644 index 0000000..af043ab --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/keyguard/ObscureSpeechDelegate.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2013 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.media.AudioManager; +import android.provider.Settings; +import android.view.View; +import android.view.View.AccessibilityDelegate; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.android.internal.R; + +/** + * Accessibility delegate that obscures speech for a view when the user has + * not turned on the "speak passwords" preference and is not listening + * through headphones. + */ +class ObscureSpeechDelegate extends AccessibilityDelegate { + /** Whether any client has announced the "headset" notification. */ + static boolean sAnnouncedHeadset = false; + + private final ContentResolver mContentResolver; + private final AudioManager mAudioManager; + + public ObscureSpeechDelegate(Context context) { + mContentResolver = context.getContentResolver(); + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + } + + @Override + public void sendAccessibilityEvent(View host, int eventType) { + super.sendAccessibilityEvent(host, eventType); + + // Play the "headset required" announcement the first time the user + // places accessibility focus on a key. + if ((eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) + && !sAnnouncedHeadset && shouldObscureSpeech()) { + sAnnouncedHeadset = true; + host.announceForAccessibility(host.getContext().getString( + R.string.keyboard_headset_required_to_hear_password)); + } + } + + @Override + public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(host, event); + + if ((event.getEventType() != AccessibilityEvent.TYPE_ANNOUNCEMENT) + && shouldObscureSpeech()) { + event.getText().clear(); + event.setContentDescription(host.getContext().getString( + R.string.keyboard_password_character_no_headset)); + } + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + + if (shouldObscureSpeech()) { + final Context ctx = host.getContext(); + info.setText(null); + info.setContentDescription( + ctx.getString(R.string.keyboard_password_character_no_headset)); + } + } + + @SuppressWarnings("deprecation") + private boolean shouldObscureSpeech() { + // The user can optionally force speaking passwords. + if (Settings.Secure.getInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0) { + return false; + } + + // Always speak if the user is listening through headphones. + if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn()) { + return false; + } + + // Don't speak since this key is used to type a password. + return true; + } +}
\ No newline at end of file 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 6eeada5..00a0aed 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java @@ -48,6 +48,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; import android.widget.Scroller; import com.android.internal.R; @@ -205,6 +206,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc 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 long REORDERING_DELETE_DROP_TARGET_FADE_DURATION = 150; private float mMinScale = 1f; protected View mDragView; private AnimatorSet mZoomInOutAnim; @@ -228,18 +230,25 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Convenience/caching private Matrix mTmpInvMatrix = new Matrix(); private float[] mTmpPoint = new float[2]; + private Rect mTmpRect = new Rect(); // 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; + private float FLING_TO_DELETE_MAX_FLING_DEGREES = 65f; protected int mFlingToDeleteThresholdVelocity = -1400; - private boolean mIsFlingingToDelete = false; + // Drag to delete + private boolean mDeferringForDelete = false; + private int DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250; + private int DRAG_TO_DELETE_FADE_OUT_DURATION = 350; + + // Drop to delete + private View mDeleteDropTarget; public interface PageSwitchListener { - void onPageSwitch(View newPage, int newPageIndex); + void onPageSwitching(View newPage, int newPageIndex); + void onPageSwitched(View newPage, int newPageIndex); } public PagedView(Context context) { @@ -293,19 +302,23 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc setOnHierarchyChangeListener(this); } + void setDeleteDropTarget(View v) { + mDeleteDropTarget = v; + } + // Convenience methods to map points from self to parent and vice versa - float[] mapPointFromSelfToParent(float x, float y) { + float[] mapPointFromViewToParent(View v, float x, float y) { mTmpPoint[0] = x; mTmpPoint[1] = y; - getMatrix().mapPoints(mTmpPoint); - mTmpPoint[0] += getLeft(); - mTmpPoint[1] += getTop(); + v.getMatrix().mapPoints(mTmpPoint); + mTmpPoint[0] += v.getLeft(); + mTmpPoint[1] += v.getTop(); return mTmpPoint; } - float[] mapPointFromParentToSelf(float x, float y) { - mTmpPoint[0] = x - getLeft(); - mTmpPoint[1] = y - getTop(); - getMatrix().invert(mTmpInvMatrix); + float[] mapPointFromParentToView(View v, float x, float y) { + mTmpPoint[0] = x - v.getLeft(); + mTmpPoint[1] = y - v.getTop(); + v.getMatrix().invert(mTmpInvMatrix); mTmpInvMatrix.mapPoints(mTmpPoint); return mTmpPoint; } @@ -328,7 +341,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc public void setScaleX(float scaleX) { super.setScaleX(scaleX); if (isReordering(true)) { - float[] p = mapPointFromParentToSelf(mParentDownMotionX, mParentDownMotionY); + float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY); mLastMotionX = p[0]; mLastMotionY = p[1]; updateDragViewTranslationDuringDrag(); @@ -356,7 +369,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc public void setPageSwitchListener(PageSwitchListener pageSwitchListener) { mPageSwitchListener = pageSwitchListener; if (mPageSwitchListener != null) { - mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); + mPageSwitchListener.onPageSwitched(getPageAt(mCurrentPage), mCurrentPage); } } @@ -415,6 +428,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc * Sets the current page. */ void setCurrentPage(int currentPage) { + notifyPageSwitching(currentPage); if (!mScroller.isFinished()) { mScroller.abortAnimation(); } @@ -428,7 +442,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); updateCurrentPageScroll(); updateScrollingIndicator(); - notifyPageSwitchListener(); + notifyPageSwitched(); invalidate(); } @@ -436,9 +450,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mOnlyAllowEdgeSwipes = enable; } - protected void notifyPageSwitchListener() { + protected void notifyPageSwitching(int whichPage) { if (mPageSwitchListener != null) { - mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage); + mPageSwitchListener.onPageSwitching(getPageAt(whichPage), whichPage); + } + } + + protected void notifyPageSwitched() { + if (mPageSwitchListener != null) { + mPageSwitchListener.onPageSwitched(getPageAt(mCurrentPage), mCurrentPage); } } @@ -511,7 +531,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Update the last motion events when scrolling if (isReordering(true)) { - float[] p = mapPointFromParentToSelf(mParentDownMotionX, mParentDownMotionY); + float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY); mLastMotionX = p[0]; mLastMotionY = p[1]; updateDragViewTranslationDuringDrag(); @@ -532,13 +552,14 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } else if (mNextPage != INVALID_PAGE) { mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); mNextPage = INVALID_PAGE; - notifyPageSwitchListener(); + notifyPageSwitched(); // We don't want to trigger a page end moving unless the page has settled // and the user has stopped scrolling if (mTouchState == TOUCH_STATE_REST) { pageEndMoving(); } + onPostReorderingAnimationCompleted(); return true; } @@ -632,7 +653,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // ensure that the cache is filled with good values. invalidateCachedOffsets(); - if (mChildCountOnLastMeasure != getChildCount() && !mIsFlingingToDelete) { + if (mChildCountOnLastMeasure != getChildCount() && !mDeferringForDelete) { setCurrentPage(mCurrentPage); } mChildCountOnLastMeasure = getChildCount(); @@ -856,7 +877,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc 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) { @@ -1038,7 +1058,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mDownScrollX = getScrollX(); mLastMotionX = x; mLastMotionY = y; - float[] p = mapPointFromSelfToParent(x, y); + float[] p = mapPointFromViewToParent(this, x, y); mParentDownMotionX = p[0]; mParentDownMotionY = p[1]; mLastMotionXRemainder = 0; @@ -1270,7 +1290,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mDownMotionX = mLastMotionX = ev.getX(); mDownMotionY = mLastMotionY = ev.getY(); mDownScrollX = getScrollX(); - float[] p = mapPointFromSelfToParent(mLastMotionX, mLastMotionY); + float[] p = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY); mParentDownMotionX = p[0]; mParentDownMotionY = p[1]; mLastMotionXRemainder = 0; @@ -1322,7 +1342,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Update the parent down so that our zoom animations take this new movement into // account - float[] pt = mapPointFromSelfToParent(mLastMotionX, mLastMotionY); + float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY); mParentDownMotionX = pt[0]; mParentDownMotionY = pt[1]; updateDragViewTranslationDuringDrag(); @@ -1331,11 +1351,16 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc final int dragViewIndex = indexOfChild(mDragView); int bufferSize = (int) (REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE * getViewportWidth()); - int leftBufferEdge = (int) (mapPointFromSelfToParent(mViewport.left, 0)[0] + int leftBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.left, 0)[0] + bufferSize); - int rightBufferEdge = (int) (mapPointFromSelfToParent(mViewport.right, 0)[0] + int rightBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.right, 0)[0] - bufferSize); + // Change the drag view if we are hovering over the drop target + boolean isHoveringOverDelete = isHoveringOverDeleteDropTarget( + (int) mParentDownMotionX, (int) mParentDownMotionY); + setPageHoveringOverDeleteDropTarget(dragViewIndex, isHoveringOverDelete); + if (DEBUG) Log.d(TAG, "leftBufferEdge: " + leftBufferEdge); if (DEBUG) Log.d(TAG, "rightBufferEdge: " + rightBufferEdge); if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX); @@ -1352,7 +1377,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } final int pageUnderPointIndex = pageIndexToSnapTo; - if (pageUnderPointIndex > -1) { + if (pageUnderPointIndex > -1 && !isHoveringOverDelete) { mTempVisiblePagesRange[0] = 0; mTempVisiblePagesRange[1] = getPageCount() - 1; boundByReorderablePages(true, mTempVisiblePagesRange); @@ -1485,13 +1510,29 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc snapToDestination(); } } 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 = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY); + mParentDownMotionX = pt[0]; + mParentDownMotionY = pt[1]; + updateDragViewTranslationDuringDrag(); + boolean handledFling = false; if (!DISABLE_FLING_TO_DELETE) { // Check the velocity and see if we are flinging-to-delete PointF flingToDeleteVector = isFlingingToDelete(); if (flingToDeleteVector != null) { onFlingToDelete(flingToDeleteVector); + handledFling = true; } } + if (!handledFling && isHoveringOverDeleteDropTarget((int) mParentDownMotionX, + (int) mParentDownMotionY)) { + onDropToDelete(); + } } else { onUnhandledTap(ev); } @@ -1743,7 +1784,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } protected void snapToPage(int whichPage, int delta, int duration, boolean immediate) { mNextPage = whichPage; - + notifyPageSwitching(whichPage); View focusedChild = getFocusedChild(); if (focusedChild != null && whichPage != mCurrentPage && focusedChild == getPageAt(mCurrentPage)) { @@ -1761,13 +1802,14 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (!mScroller.isFinished()) mScroller.abortAnimation(); mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); - notifyPageSwitchListener(); + notifyPageSwitched(); // Trigger a compute() to finish switching pages if necessary if (immediate) { computeScroll(); } + mForceScreenScrolled = true; invalidate(); } @@ -1991,6 +2033,23 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mZoomInOutAnim.playTogether( ObjectAnimator.ofFloat(this, "scaleX", mMinScale), ObjectAnimator.ofFloat(this, "scaleY", mMinScale)); + mZoomInOutAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + // Show the delete drop target + if (mDeleteDropTarget != null) { + mDeleteDropTarget.setVisibility(View.VISIBLE); + mDeleteDropTarget.animate().alpha(1f) + .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mDeleteDropTarget.setAlpha(0f); + } + }); + } + } + }); mZoomInOutAnim.start(); return true; } @@ -2007,6 +2066,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mTouchState = TOUCH_STATE_REORDERING; mIsReordering = true; + // Mark all the non-widget pages as invisible + getVisiblePages(mTempVisiblePagesRange); + boundByReorderablePages(true, mTempVisiblePagesRange); + for (int i = 0; i < getPageCount(); ++i) { + if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) { + getPageAt(i).setAlpha(0f); + } + } + // We must invalidate to trigger a redraw to update the layers such that the drag view // is always drawn on top invalidate(); @@ -2028,6 +2096,15 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc R.string.keyguard_accessibility_widget_reorder_end)); } mIsReordering = false; + + // Mark all the non-widget pages as visible again + getVisiblePages(mTempVisiblePagesRange); + boundByReorderablePages(true, mTempVisiblePagesRange); + for (int i = 0; i < getPageCount(); ++i) { + if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) { + getPageAt(i).setAlpha(1f); + } + } } public boolean startReordering() { @@ -2072,7 +2149,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc onEndReordering(); } }; - if (!mIsFlingingToDelete) { + if (!mDeferringForDelete) { mPostReorderingPreZoomInRunnable = new Runnable() { public void run() { zoomIn(onCompleteRunnable); @@ -2086,7 +2163,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Animate the drag view back to the front position animateDragViewToOriginalPosition(); } else { - zoomIn(onCompleteRunnable); + // Handled in post-delete-animation-callbacks } } @@ -2103,6 +2180,20 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc ObjectAnimator.ofFloat(this, "scaleY", 1f)); mZoomInOutAnim.addListener(new AnimatorListenerAdapter() { @Override + public void onAnimationStart(Animator animation) { + // Hide the delete drop target + if (mDeleteDropTarget != null) { + mDeleteDropTarget.animate().alpha(0f) + .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mDeleteDropTarget.setVisibility(View.GONE); + } + }); + } + } + @Override public void onAnimationCancel(Animator animation) { mDragView = null; } @@ -2186,6 +2277,97 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } }; + private Runnable createPostDeleteAnimationRunnable(final View dragView) { + return new Runnable() { + @Override + public void run() { + int dragViewIndex = indexOfChild(dragView); + + // 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 + getVisiblePages(mTempVisiblePagesRange); + boundByReorderablePages(true, mTempVisiblePagesRange); + boolean isLastWidgetPage = (mTempVisiblePagesRange[0] == mTempVisiblePagesRange[1]); + boolean slideFromLeft = (isLastWidgetPage || + dragViewIndex > mTempVisiblePagesRange[0]); + + // Setup the scroll to the correct page before we swap the views + if (slideFromLeft) { + snapToPageImmediately(dragViewIndex - 1); + } + + int firstIndex = (isLastWidgetPage ? 0 : mTempVisiblePagesRange[0]); + int lastIndex = Math.min(mTempVisiblePagesRange[1], getPageCount() - 1); + int lowerIndex = (slideFromLeft ? firstIndex : dragViewIndex + 1 ); + int upperIndex = (slideFromLeft ? dragViewIndex - 1 : lastIndex); + ArrayList<Animator> animations = new ArrayList<Animator>(); + 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; + int newX = 0; + if (slideFromLeft) { + if (i == 0) { + // Simulate the page being offscreen with the page spacing + oldX = getViewportOffsetX() + getChildOffset(i) - getChildWidth(i) + - mPageSpacing; + } else { + oldX = getViewportOffsetX() + getChildOffset(i - 1); + } + newX = getViewportOffsetX() + getChildOffset(i); + } else { + oldX = getChildOffset(i) - getChildOffset(i - 1); + newX = 0; + } + + // Animate the view translation from its old position to its new + // position + AnimatorSet anim = (AnimatorSet) v.getTag(); + if (anim != null) { + anim.cancel(); + } + + // Note: Hacky, but we want to skip any optimizations to not draw completely + // hidden views + v.setAlpha(Math.max(v.getAlpha(), 0.01f)); + v.setTranslationX(oldX - newX); + anim = new AnimatorSet(); + anim.playTogether( + ObjectAnimator.ofFloat(v, "translationX", 0f), + ObjectAnimator.ofFloat(v, "alpha", 1f)); + animations.add(anim); + v.setTag(anim); + } + + AnimatorSet slideAnimations = new AnimatorSet(); + slideAnimations.playTogether(animations); + slideAnimations.setDuration(DELETE_SLIDE_IN_SIDE_PAGE_DURATION); + slideAnimations.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + final Runnable onCompleteRunnable = new Runnable() { + @Override + public void run() { + mDeferringForDelete = false; + onEndReordering(); + } + }; + zoomIn(onCompleteRunnable); + } + }); + slideAnimations.start(); + + removeView(dragView); + onRemoveView(dragView); + } + }; + } + public void onFlingToDelete(PointF vel) { final long startTime = AnimationUtils.currentAnimationTimeMillis(); @@ -2222,59 +2404,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc 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); - } - }; + final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView); // Create and start the animation ValueAnimator mDropAnim = new ValueAnimator(); @@ -2288,11 +2418,57 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } }); mDropAnim.start(); - mIsFlingingToDelete = true; + mDeferringForDelete = true; } - /* Accessibility */ + /* Drag to delete */ + private boolean isHoveringOverDeleteDropTarget(int x, int y) { + if (mDeleteDropTarget != null) { + mDeleteDropTarget.getGlobalVisibleRect(mTmpRect); + return mTmpRect.contains(x, y); + } + return false; + } + + protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) {} + + private void onDropToDelete() { + final View dragView = mDragView; + final float toScale = 0f; + final float toAlpha = 0f; + + // Create and start the complex animation + ArrayList<Animator> animations = new ArrayList<Animator>(); + AnimatorSet motionAnim = new AnimatorSet(); + motionAnim.setInterpolator(new DecelerateInterpolator(2)); + motionAnim.playTogether( + ObjectAnimator.ofFloat(dragView, "scaleX", toScale), + ObjectAnimator.ofFloat(dragView, "scaleY", toScale)); + animations.add(motionAnim); + + AnimatorSet alphaAnim = new AnimatorSet(); + alphaAnim.setInterpolator(new LinearInterpolator()); + alphaAnim.playTogether( + ObjectAnimator.ofFloat(dragView, "alpha", toAlpha)); + animations.add(alphaAnim); + + final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView); + + AnimatorSet anim = new AnimatorSet(); + anim.playTogether(animations); + anim.setDuration(DRAG_TO_DELETE_FADE_OUT_DURATION); + anim.addListener(new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + onAnimationEndRunnable.run(); + } + }); + anim.start(); + + mDeferringForDelete = true; + } + + /* Accessibility */ @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java index 6156143..16e2f9e 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java @@ -16,6 +16,8 @@ package com.android.internal.policy.impl.keyguard; +import com.android.internal.R; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -29,7 +31,6 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Log; -import android.util.MathUtils; import android.util.Property; import android.view.MotionEvent; import android.view.VelocityTracker; @@ -40,8 +41,6 @@ import android.view.accessibility.AccessibilityManager; 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. @@ -53,7 +52,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout // 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_ABOVE = 8; // 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 @@ -67,7 +66,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout // Initialized during measurement from child layoutparams private View mExpandChallengeView; - private View mChallengeView; + private KeyguardSecurityContainer mChallengeView; private View mScrimView; private View mWidgetsView; @@ -123,6 +122,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout private final Rect mTempRect = new Rect(); private boolean mHasGlowpad; + private boolean mChallengeInteractive = true; static final Property<SlidingChallengeLayout, Float> HANDLE_ALPHA = new FloatProperty<SlidingChallengeLayout>("handleAlpha") { @@ -276,6 +276,13 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout } } + public void setChallengeInteractive(boolean interactive) { + mChallengeInteractive = interactive; + if (mExpandChallengeView != null) { + mExpandChallengeView.setEnabled(interactive); + } + } + void animateHandle(boolean visible) { if (mHandleAnimation != null) { mHandleAnimation.cancel(); @@ -504,7 +511,9 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout if (mScrimView != null) { mScrimView.setVisibility(VISIBLE); } - + if (mChallengeView != null) { + mChallengeView.showBouncer(HANDLE_ANIMATE_DURATION); + } // Mess with padding/margin to inset the bouncer frame. // We have more space available to us otherwise. if (mChallengeView != null) { @@ -533,6 +542,9 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout if (mScrimView != null) { mScrimView.setVisibility(GONE); } + if (mChallengeView != null) { + mChallengeView.hideBouncer(HANDLE_ANIMATE_DURATION); + } animateFrame(false, true); if (mBouncerListener != null) { mBouncerListener.onBouncerStateChanged(false); @@ -580,19 +592,17 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout for (int i = 0; i < count; i++) { final float x = ev.getX(i); final float y = ev.getY(i); - if (!mIsBouncing && mActivePointerId == INVALID_POINTER - && ((isInDragHandle(x, y) && MathUtils.sq(x - mGestureStartX) - + MathUtils.sq(y - mGestureStartY) > mTouchSlopSquare) - || crossedDragHandle(x, y, mGestureStartY) + if (!mIsBouncing && mChallengeInteractive && mActivePointerId == INVALID_POINTER + && (crossedDragHandle(x, y, mGestureStartY) || (isInChallengeView(x, y) && - mScrollState == SCROLL_STATE_SETTLING))) { + mScrollState == SCROLL_STATE_SETTLING))) { mActivePointerId = ev.getPointerId(i); mGestureStartX = x; mGestureStartY = y; mGestureStartChallengeBottom = getChallengeBottom(); mDragging = true; mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null); - } else if (isInChallengeView(x, y)) { + } else if (mChallengeShowing && isInChallengeView(x, y)) { mBlockDrag = true; } } @@ -630,7 +640,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout break; case MotionEvent.ACTION_CANCEL: - if (mDragging) { + if (mDragging && mChallengeInteractive) { showChallenge(0); } resetTouch(); @@ -641,7 +651,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout break; } case MotionEvent.ACTION_UP: - if (mDragging) { + if (mDragging && mChallengeInteractive) { mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity); showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId)); } @@ -657,7 +667,8 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) || (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) - && mActivePointerId == INVALID_POINTER) { + && mActivePointerId == INVALID_POINTER + && mChallengeInteractive) { mGestureStartX = x; mGestureStartY = y; mActivePointerId = ev.getPointerId(i); @@ -767,11 +778,19 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout } 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(); + final boolean horizOk = x >= 0 && x < getWidth(); + + final boolean vertOk; + if (mChallengeShowing) { + vertOk = initialY < (challengeTop - getDragHandleSizeAbove()) && + y > challengeTop + getDragHandleSizeBelow(); + } else { + vertOk = initialY > challengeTop + getDragHandleSizeBelow() && + y < challengeTop - getDragHandleSizeAbove(); + } + return horizOk && vertOk; } @Override @@ -803,7 +822,11 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout throw new IllegalStateException( "There may only be one child with layout_isChallenge=\"true\""); } - mChallengeView = child; + if (!(child instanceof KeyguardSecurityContainer)) { + throw new IllegalArgumentException( + "Challenge must be a KeyguardSecurityContainer"); + } + mChallengeView = (KeyguardSecurityContainer) child; if (mChallengeView != oldChallengeView) { mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE); } diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java index d0dd9cf..daa82f2 100644 --- a/services/java/com/android/server/AppWidgetServiceImpl.java +++ b/services/java/com/android/server/AppWidgetServiceImpl.java @@ -599,7 +599,7 @@ class AppWidgetServiceImpl { } public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) { - mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider); bindAppWidgetIdImpl(appWidgetId, provider, options); } @@ -607,7 +607,7 @@ class AppWidgetServiceImpl { public boolean bindAppWidgetIdIfAllowed( String packageName, int appWidgetId, ComponentName provider, Bundle options) { try { - mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null); + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, null); } catch (SecurityException se) { if (!callerHasBindAppWidgetPermission(packageName)) { return false; |