diff options
author | Will Haldean Brown <haldean@google.com> | 2014-03-04 15:54:13 -0800 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2014-03-04 15:54:13 -0800 |
commit | 8055195f992f4cdbea6172c18afe2f3809de7a39 (patch) | |
tree | c1da01127cb72eacc5bee724aa08623b34999854 | |
parent | bd4c10f8d7069cfbd8bb9b1c6879f85074c471b9 (diff) | |
parent | 568628dc2cb92b3ec3a87cae9de3203fbdc5968c (diff) | |
download | frameworks_base-8055195f992f4cdbea6172c18afe2f3809de7a39.zip frameworks_base-8055195f992f4cdbea6172c18afe2f3809de7a39.tar.gz frameworks_base-8055195f992f4cdbea6172c18afe2f3809de7a39.tar.bz2 |
am 568628dc: Manually merge commit \'2faf28cf\' into master
* commit '568628dc2cb92b3ec3a87cae9de3203fbdc5968c':
Add swipe-to-dismiss support to PhoneWindow.
-rw-r--r-- | api/current.txt | 5 | ||||
-rw-r--r-- | core/java/android/app/Activity.java | 7 | ||||
-rw-r--r-- | core/java/android/app/Dialog.java | 4 | ||||
-rw-r--r-- | core/java/android/service/dreams/DreamService.java | 4 | ||||
-rw-r--r-- | core/java/android/view/Window.java | 12 | ||||
-rw-r--r-- | core/java/com/android/internal/widget/SwipeDismissLayout.java | 282 | ||||
-rw-r--r-- | core/res/res/anim/swipe_window_enter.xml | 26 | ||||
-rw-r--r-- | core/res/res/anim/swipe_window_exit.xml | 26 | ||||
-rw-r--r-- | core/res/res/layout/screen_swipe_dismiss.xml | 27 | ||||
-rw-r--r-- | core/res/res/values/attrs.xml | 5 | ||||
-rw-r--r-- | core/res/res/values/styles.xml | 8 | ||||
-rw-r--r-- | core/res/res/values/symbols.xml | 1 | ||||
-rw-r--r-- | core/res/res/values/themes_micro.xml | 20 | ||||
-rw-r--r-- | policy/src/com/android/internal/policy/impl/PhoneWindow.java | 69 |
14 files changed, 494 insertions, 2 deletions
diff --git a/api/current.txt b/api/current.txt index 55cb13b..7051d6c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3120,6 +3120,7 @@ package android.app { method public void onUserInteraction(); method protected void onUserLeaveHint(); method public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams); + method public void onWindowDismissed(); method public void onWindowFocusChanged(boolean); method public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback); method public void openContextMenu(android.view.View); @@ -3641,6 +3642,7 @@ package android.app { method public boolean onTouchEvent(android.view.MotionEvent); method public boolean onTrackballEvent(android.view.MotionEvent); method public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams); + method public void onWindowDismissed(); method public void onWindowFocusChanged(boolean); method public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback); method public void openContextMenu(android.view.View); @@ -23741,6 +23743,7 @@ package android.service.dreams { method public boolean onPreparePanel(int, android.view.View, android.view.Menu); method public boolean onSearchRequested(); method public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams); + method public void onWindowDismissed(); method public void onWindowFocusChanged(boolean); method public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback); method public void setContentView(int); @@ -30139,6 +30142,7 @@ package android.view { field public static final int FEATURE_OPTIONS_PANEL = 0; // 0x0 field public static final int FEATURE_PROGRESS = 2; // 0x2 field public static final int FEATURE_RIGHT_ICON = 4; // 0x4 + field public static final int FEATURE_SWIPE_TO_DISMISS = 11; // 0xb field public static final int ID_ANDROID_CONTENT = 16908290; // 0x1020002 field public static final int PROGRESS_END = 10000; // 0x2710 field public static final int PROGRESS_INDETERMINATE_OFF = -4; // 0xfffffffc @@ -30170,6 +30174,7 @@ package android.view { method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu); method public abstract boolean onSearchRequested(); method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams); + method public abstract void onWindowDismissed(); method public abstract void onWindowFocusChanged(boolean); method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 3297fe0..af4a362 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2462,6 +2462,13 @@ public class Activity extends ContextThemeWrapper } return false; } + + /** + * Called when the main window associated with the activity has been dismissed. + */ + public void onWindowDismissed() { + finish(); + } /** * Called to process key events. You can override this to intercept all diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 2559254..fb96d8d 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -707,6 +707,10 @@ public class Dialog implements DialogInterface, Window.Callback, public void onDetachedFromWindow() { } + + public void onWindowDismissed() { + dismiss(); + } /** * Called to process key events. You can override this to intercept all diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 1abb1d7..7647c22 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -300,6 +300,10 @@ public class DreamService extends Service implements Window.Callback { public void onDetachedFromWindow() { } + @Override + public void onWindowDismissed() { + } + /** {@inheritDoc} */ @Override public void onPanelClosed(int featureId, Menu menu) { diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 24b8248..0cd6325 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -98,6 +98,10 @@ public abstract class Window { */ public static final int FEATURE_ACTION_MODE_OVERLAY = 10; /** + * Flag for requesting a decoration-free window that is dismissed by swiping from the left. + */ + public static final int FEATURE_SWIPE_TO_DISMISS = 11; + /** * Flag for requesting that window content changes should be represented * with scenes and transitions. * @@ -105,7 +109,7 @@ public abstract class Window { * * @see #setContentView */ - public static final int FEATURE_CONTENT_TRANSITIONS = 11; + public static final int FEATURE_CONTENT_TRANSITIONS = 12; /** * Max value used as a feature ID @@ -404,6 +408,12 @@ public abstract class Window { * @param mode The mode that was just finished. */ public void onActionModeFinished(ActionMode mode); + + /** + * Called when a window is dismissed. This informs the callback that the + * window is gone, and it should finish itself. + */ + public void onWindowDismissed(); } public Window(Context context) { diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java new file mode 100644 index 0000000..cc8ce2c --- /dev/null +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.animation.TimeInterpolator; +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.FrameLayout; + +/** + * Special layout that finishes its activity when swiped away. + */ +public class SwipeDismissLayout extends FrameLayout { + private static final String TAG = "SwipeDismissLayout"; + + private static final float TRANSLATION_MIN_ALPHA = 0.5f; + + public interface OnDismissedListener { + void onDismissed(SwipeDismissLayout layout); + } + + public interface OnSwipeProgressChangedListener { + /** + * Called when the layout has been swiped and the position of the window should change. + * + * @param progress A number in [-1, 1] representing how far to the left + * or right the window has been swiped. Negative values are swipes + * left, and positives are right. + * @param translate A number in [-w, w], where w is the width of the + * layout. This is equivalent to progress * layout.getWidth(). + */ + void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate); + + void onSwipeCancelled(SwipeDismissLayout layout); + } + + // Cached ViewConfiguration and system-wide constant values + private int mSlop; + private int mMinFlingVelocity; + private int mMaxFlingVelocity; + private long mAnimationTime; + private TimeInterpolator mCancelInterpolator; + private TimeInterpolator mDismissInterpolator; + + // Transient properties + private int mActiveTouchId; + private float mDownX; + private float mDownY; + private boolean mSwiping; + private boolean mDismissed; + private boolean mDiscardIntercept; + private VelocityTracker mVelocityTracker; + private float mTranslationX; + + private OnDismissedListener mDismissedListener; + private OnSwipeProgressChangedListener mProgressListener; + + public SwipeDismissLayout(Context context) { + super(context); + init(context); + } + + public SwipeDismissLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context); + } + + private void init(Context context) { + ViewConfiguration vc = ViewConfiguration.get(getContext()); + mSlop = vc.getScaledTouchSlop(); + mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16; + mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); + mAnimationTime = getContext().getResources().getInteger( + android.R.integer.config_shortAnimTime); + mCancelInterpolator = new DecelerateInterpolator(1.5f); + mDismissInterpolator = new AccelerateInterpolator(1.5f); + } + + public void setOnDismissedListener(OnDismissedListener listener) { + mDismissedListener = listener; + } + + public void setOnSwipeProgressChangedListener(OnSwipeProgressChangedListener listener) { + mProgressListener = listener; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + // offset because the view is translated during swipe + ev.offsetLocation(mTranslationX, 0); + + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + resetMembers(); + mDownX = ev.getRawX(); + mDownY = ev.getRawY(); + mActiveTouchId = ev.getPointerId(0); + mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker.addMovement(ev); + break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + resetMembers(); + break; + + case MotionEvent.ACTION_MOVE: + if (mVelocityTracker == null || mDiscardIntercept) { + break; + } + + int pointerIndex = ev.findPointerIndex(mActiveTouchId); + float dx = ev.getRawX() - mDownX; + float x = ev.getX(pointerIndex); + float y = ev.getY(pointerIndex); + if (dx != 0 && canScroll(this, false, dx, x, y)) { + mDiscardIntercept = true; + break; + } + updateSwiping(ev); + break; + } + + return !mDiscardIntercept && mSwiping; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (mVelocityTracker == null) { + return super.onTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_UP: + updateDismiss(ev); + if (mDismissed) { + dismiss(); + } else if (mSwiping) { + cancel(); + } + resetMembers(); + break; + + case MotionEvent.ACTION_CANCEL: + cancel(); + resetMembers(); + break; + + case MotionEvent.ACTION_MOVE: + mVelocityTracker.addMovement(ev); + updateSwiping(ev); + updateDismiss(ev); + if (mSwiping) { + setProgress(ev.getRawX() - mDownX); + break; + } + } + return true; + } + + private void setProgress(float deltaX) { + mTranslationX = deltaX; + if (mProgressListener != null) { + mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX); + } + } + + private void dismiss() { + if (mDismissedListener != null) { + mDismissedListener.onDismissed(this); + } + } + + protected void cancel() { + if (mProgressListener != null) { + mProgressListener.onSwipeCancelled(this); + } + } + + /** + * Resets internal members when canceling. + */ + private void resetMembers() { + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + } + mVelocityTracker = null; + mTranslationX = 0; + mDownX = 0; + mDownY = 0; + mSwiping = false; + mDismissed = false; + mDiscardIntercept = false; + } + + private void updateSwiping(MotionEvent ev) { + if (!mSwiping) { + float deltaX = ev.getRawX() - mDownX; + float deltaY = ev.getRawY() - mDownY; + mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < mSlop * 2; + } + } + + private void updateDismiss(MotionEvent ev) { + if (!mDismissed) { + mVelocityTracker.addMovement(ev); + mVelocityTracker.computeCurrentVelocity(1000); + + float deltaX = ev.getRawX() - mDownX; + float velocityX = mVelocityTracker.getXVelocity(); + float absVelocityX = Math.abs(velocityX); + float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); + + if (deltaX > getWidth() / 2) { + mDismissed = true; + } else if (absVelocityX >= mMinFlingVelocity + && absVelocityX <= mMaxFlingVelocity + && absVelocityY < absVelocityX / 2 + && velocityX > 0 + && deltaX > 0) { + mDismissed = true; + } + } + } + + /** + * Tests scrollability within child views of v in the direction of dx. + * + * @param v View to test for horizontal scrollability + * @param checkV Whether the view v passed should itself be checked for scrollability (true), + * or just its children (false). + * @param dx Delta scrolled in pixels. Only the sign of this is used. + * @param x X coordinate of the active touch point + * @param y Y coordinate of the active touch point + * @return true if child views of v can be scrolled by delta of dx. + */ + protected boolean canScroll(View v, boolean checkV, float dx, float x, float y) { + if (v instanceof ViewGroup) { + final ViewGroup group = (ViewGroup) v; + final int scrollX = v.getScrollX(); + final int scrollY = v.getScrollY(); + final int count = group.getChildCount(); + for (int i = count - 1; i >= 0; i--) { + final View child = group.getChildAt(i); + if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() && + y + scrollY >= child.getTop() && y + scrollY < child.getBottom() && + canScroll(child, true, dx, x + scrollX - child.getLeft(), + y + scrollY - child.getTop())) { + return true; + } + } + } + + return checkV && v.canScrollHorizontally((int) -dx); + } +} diff --git a/core/res/res/anim/swipe_window_enter.xml b/core/res/res/anim/swipe_window_enter.xml new file mode 100644 index 0000000..e1617e2 --- /dev/null +++ b/core/res/res/anim/swipe_window_enter.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2007, 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@interpolator/decelerate_quad" > + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:fillEnabled="true" android:fillBefore="true" + android:fillAfter="true" + android:duration="@android:integer/config_activityDefaultDur" /> +</set> diff --git a/core/res/res/anim/swipe_window_exit.xml b/core/res/res/anim/swipe_window_exit.xml new file mode 100644 index 0000000..ed0c5d3 --- /dev/null +++ b/core/res/res/anim/swipe_window_exit.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2007, 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@interpolator/decelerate_quad" > + <translate android:fromXDelta="0%" android:toXDelta="100%" + android:fillEnabled="true" android:fillBefore="true" + android:fillAfter="true" + android:duration="400" /> +</set> diff --git a/core/res/res/layout/screen_swipe_dismiss.xml b/core/res/res/layout/screen_swipe_dismiss.xml new file mode 100644 index 0000000..90e970f --- /dev/null +++ b/core/res/res/layout/screen_swipe_dismiss.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- +This is a layout for a window whose resident activity is finished when swiped away. +--> + +<com.android.internal.widget.SwipeDismissLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/content" + android:fitsSystemWindows="true" + android:layout_width="match_parent" + android:layout_height="match_parent" + /> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 039bd07..a78ce02 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -447,6 +447,10 @@ to {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_NAVIGATION}. --> <attr name="windowTranslucentNavigation" format="boolean" /> + <!-- Flag to indicate that a window can be swiped away to be dismissed. + Corresponds to {@link android.view.Window.FEATURE_SWIPE_TO_DISMISS} --> + <attr name="windowSwipeToDismiss" format="boolean" /> + <!-- Flag indicating whether this window requests that content changes be performed as scene changes with transitions. Corresponds to {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS}. --> @@ -1629,6 +1633,7 @@ <attr name="windowCloseOnTouchOutside" /> <attr name="windowTranslucentStatus" /> <attr name="windowTranslucentNavigation" /> + <attr name="windowSwipeToDismiss" /> <attr name="windowContentTransitions" /> <attr name="windowContentTransitionManager" /> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 1a6eacd..e525ef7 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -225,6 +225,14 @@ please see styles_device_defaults.xml. <item name="windowExitAnimation">@anim/fast_fade_out</item> </style> + <!-- Window animations for swipe-dismissable windows. {@hide} --> + <style name="Animation.SwipeDismiss"> + <item name="taskOpenEnterAnimation">@anim/swipe_window_enter</item> + <item name="taskOpenExitAnimation">@anim/swipe_window_exit</item> + <item name="taskCloseEnterAnimation">@anim/swipe_window_enter</item> + <item name="taskCloseExitAnimation">@anim/swipe_window_exit</item> + </style> + <!-- Status Bar Styles --> <style name="TextAppearance.StatusBar"> <item name="android:textAppearance">?android:attr/textAppearanceSmall</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9f368c4..a4f9762 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1370,6 +1370,7 @@ <java-symbol type="layout" name="screen_progress" /> <java-symbol type="layout" name="screen_simple" /> <java-symbol type="layout" name="screen_simple_overlay_action_mode" /> + <java-symbol type="layout" name="screen_swipe_dismiss" /> <java-symbol type="layout" name="screen_title" /> <java-symbol type="layout" name="screen_title_icons" /> <java-symbol type="string" name="system_ui_date_pattern" /> diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml index b174d22..42f64fe 100644 --- a/core/res/res/values/themes_micro.xml +++ b/core/res/res/values/themes_micro.xml @@ -16,22 +16,42 @@ <resources> <style name="Theme.Micro" parent="Theme.Holo"> <item name="numberPickerStyle">@android:style/Widget.Micro.NumberPicker</item> + <item name="windowAnimationStyle">@android:style/Animation.SwipeDismiss</item> + <item name="windowIsFloating">false</item> + <item name="windowIsTranslucent">true</item> + <item name="windowSwipeToDismiss">true</item> </style> <style name="Theme.Micro.NoActionBar" parent="Theme.Holo.NoActionBar"> <item name="textViewStyle">@android:style/Widget.Micro.TextView</item> <item name="numberPickerStyle">@android:style/Widget.Micro.NumberPicker</item> + <item name="windowAnimationStyle">@android:style/Animation.SwipeDismiss</item> + <item name="windowIsFloating">false</item> + <item name="windowIsTranslucent">true</item> + <item name="windowSwipeToDismiss">true</item> </style> <style name="Theme.Micro.Light" parent="Theme.Holo.Light"> <item name="numberPickerStyle">@android:style/Widget.Micro.NumberPicker</item> + <item name="windowAnimationStyle">@android:style/Animation.SwipeDismiss</item> + <item name="windowIsFloating">false</item> + <item name="windowIsTranslucent">true</item> + <item name="windowSwipeToDismiss">true</item> </style> <style name="Theme.Micro.Light.NoActionBar" parent="Theme.Holo.Light.NoActionBar"> <item name="textViewStyle">@android:style/Widget.Micro.TextView</item> <item name="numberPickerStyle">@android:style/Widget.Micro.NumberPicker</item> + <item name="windowAnimationStyle">@android:style/Animation.SwipeDismiss</item> + <item name="windowIsFloating">false</item> + <item name="windowIsTranslucent">true</item> + <item name="windowSwipeToDismiss">true</item> </style> <style name="Theme.Micro.Light.DarkActionBar" parent="Theme.Holo.Light.DarkActionBar"> <item name="textViewStyle">@android:style/Widget.Micro.TextView</item> <item name="numberPickerStyle">@android:style/Widget.Micro.NumberPicker</item> + <item name="windowAnimationStyle">@android:style/Animation.SwipeDismiss</item> + <item name="windowIsFloating">false</item> + <item name="windowIsTranslucent">true</item> + <item name="windowSwipeToDismiss">true</item> </style> </resources> diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 02a2680..977c2e7 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -47,6 +47,7 @@ import com.android.internal.widget.ActionBarContainer; import com.android.internal.widget.ActionBarContextView; import com.android.internal.widget.ActionBarOverlayLayout; import com.android.internal.widget.ActionBarView; +import com.android.internal.widget.SwipeDismissLayout; import android.app.KeyguardManager; import android.content.Context; @@ -295,6 +296,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // Remove the action bar feature if we have no title. No title dominates. removeFeature(FEATURE_ACTION_BAR); } + + if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_SWIPE_TO_DISMISS) { + throw new AndroidRuntimeException( + "You cannot combine swipe dismissal and the action bar."); + } + if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0 && featureId == FEATURE_ACTION_BAR) { + throw new AndroidRuntimeException( + "You cannot combine swipe dismissal and the action bar."); + } return super.requestFeature(featureId); } @@ -2924,6 +2934,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { requestFeature(FEATURE_ACTION_MODE_OVERLAY); } + if (a.getBoolean(com.android.internal.R.styleable.Window_windowSwipeToDismiss, false)) { + requestFeature(FEATURE_SWIPE_TO_DISMISS); + } + if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) { setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags())); } @@ -3053,7 +3067,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { int layoutResource; int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); - if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { + if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { + layoutResource = com.android.internal.R.layout.screen_swipe_dismiss; + } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( @@ -3123,6 +3139,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { + registerSwipeCallbacks(); + } + // Remaining setup -- of background and title -- that only applies // to top-level windows. if (getContainer() == null) { @@ -3493,6 +3513,53 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return (mRightIconView = (ImageView)findViewById(com.android.internal.R.id.right_icon)); } + private void registerSwipeCallbacks() { + SwipeDismissLayout swipeDismiss = + (SwipeDismissLayout) findViewById(com.android.internal.R.id.content); + swipeDismiss.setOnDismissedListener(new SwipeDismissLayout.OnDismissedListener() { + @Override + public void onDismissed(SwipeDismissLayout layout) { + Callback cb = getCallback(); + if (cb != null) { + try { + cb.onWindowDismissed(); + } catch (AbstractMethodError e) { + Log.e(TAG, "onWindowDismissed not implemented in " + + cb.getClass().getSimpleName(), e); + } + } + } + }); + swipeDismiss.setOnSwipeProgressChangedListener( + new SwipeDismissLayout.OnSwipeProgressChangedListener() { + private boolean mIsTranslucent = false; + + @Override + public void onSwipeProgressChanged( + SwipeDismissLayout layout, float progress, float translate) { + WindowManager.LayoutParams newParams = getAttributes(); + newParams.x = (int) translate; + setAttributes(newParams); + + int flags = 0; + if (newParams.x == 0) { + flags = FLAG_FULLSCREEN; + } else { + flags = FLAG_LAYOUT_NO_LIMITS; + } + setFlags(flags, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS); + } + + @Override + public void onSwipeCancelled(SwipeDismissLayout layout) { + WindowManager.LayoutParams newParams = getAttributes(); + newParams.x = 0; + setAttributes(newParams); + setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN | FLAG_LAYOUT_NO_LIMITS); + } + }); + } + /** * Helper method for calling the {@link Callback#onPanelClosed(int, Menu)} * callback. This method will grab whatever extra state is needed for the |