Merge "Fix issue #3362484: Can't dismiss activity picker by tapping outside dialog" into honeycomb
diff --git a/api/current.xml b/api/current.xml
index 4f566f6..76be208 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -10894,6 +10894,17 @@
  visibility="public"
 >
 </field>
+<field name="windowCloseOnTouchOutside"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843611"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="windowContentOverlay"
  type="int"
  transient="false"
@@ -24300,6 +24311,19 @@
 <parameter name="uri" type="android.net.Uri">
 </parameter>
 </method>
+<method name="setFinishOnTouchOutside"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="finish" type="boolean">
+</parameter>
+</method>
 <method name="setIntent"
  return="void"
  abstract="false"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 93ad17e..0a64070 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1808,6 +1808,14 @@
     }
 
     /**
+     * Sets whether this activity is finished when touched outside its window's
+     * bounds.
+     */
+    public void setFinishOnTouchOutside(boolean finish) {
+        mWindow.setCloseOnTouchOutside(finish);
+    }
+    
+    /**
      * Use with {@link #setDefaultKeyMode} to turn off default handling of
      * keys.
      * 
@@ -2063,6 +2071,11 @@
      * The default implementation always returns false.
      */
     public boolean onTouchEvent(MotionEvent event) {
+        if (mWindow.shouldCloseOnTouch(this, event)) {
+            finish();
+            return true;
+        }
+        
         return false;
     }
     
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 70e3616..428f4e3 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -64,11 +64,13 @@
 
     protected AlertDialog(Context context, int theme) {
         super(context, theme == 0 ? getDefaultDialogTheme(context) : theme);
+        mWindow.alwaysReadCloseOnTouchAttr();
         mAlert = new AlertController(context, this, getWindow());
     }
 
     protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
         super(context, getDefaultDialogTheme(context));
+        mWindow.alwaysReadCloseOnTouchAttr();
         setCancelable(cancelable);
         setOnCancelListener(cancelListener);
         mAlert = new AlertController(context, this, getWindow());
@@ -81,25 +83,6 @@
         return outValue.resourceId;
     }
 
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (mCancelable) {
-            final View decor = mWindow.getDecorView();
-            final int width = decor.getWidth();
-            final int height = decor.getHeight();
-            final float x = ev.getX();
-            final float y = ev.getY();
-
-            if (mCancelable && (x < 0 || x > width || y < 0 || y > height)
-                    &&  mDecor != null && isShowing()) {
-                cancel();
-                return true;
-            }
-        }
-
-        return super.onTouchEvent(ev);
-    }
-
     /**
      * Gets one of the buttons used in the dialog.
      * <p>
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 6791400..7365670 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -41,7 +41,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnCreateContextMenuListener;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.Window;
@@ -90,12 +89,6 @@
     private Message mDismissMessage;
     private Message mShowMessage;
 
-    /**
-     * Whether to cancel the dialog when a touch is received outside of the
-     * window's bounds.
-     */
-    private boolean mCanceledOnTouchOutside = false;
-    
     private OnKeyListener mOnKeyListener;
 
     private boolean mCreated = false;
@@ -597,8 +590,7 @@
      *         happens outside of the window bounds.
      */
     public boolean onTouchEvent(MotionEvent event) {
-        if (mCancelable && mCanceledOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
-                && isOutOfBounds(event) && mDecor != null && mShowing) {
+        if (mCancelable && mShowing && mWindow.shouldCloseOnTouch(mContext, event)) {
             cancel();
             return true;
         }
@@ -606,16 +598,6 @@
         return false;
     }
 
-    private boolean isOutOfBounds(MotionEvent event) {
-        final int x = (int) event.getX();
-        final int y = (int) event.getY();
-        final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop();
-        final View decorView = getWindow().getDecorView();
-        return (x < -slop) || (y < -slop)
-                || (x > (decorView.getWidth()+slop))
-                || (y > (decorView.getHeight()+slop));
-    }
-    
     /**
      * Called when the trackball was moved and not handled by any of the
      * views inside of the activity.  So, for example, if the trackball moves
@@ -1021,7 +1003,7 @@
             mCancelable = true;
         }
         
-        mCanceledOnTouchOutside = cancel;
+        mWindow.setCloseOnTouchOutside(cancel);
     }
     
     /**
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 2f27935..f1883dc 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -116,6 +116,8 @@
     private Window mActiveChild;
     private boolean mIsActive = false;
     private boolean mHasChildren = false;
+    private boolean mCloseOnTouchOutside = false;
+    private boolean mSetCloseOnTouchOutside = false;
     private int mForcedWindowFlags = 0;
 
     private int mFeatures = DEFAULT_FEATURES;
@@ -786,6 +788,39 @@
         return mHasSoftInputMode;
     }
     
+    /** @hide */
+    public void setCloseOnTouchOutside(boolean close) {
+        mCloseOnTouchOutside = close;
+        mSetCloseOnTouchOutside = true;
+    }
+    
+    /** @hide */
+    public boolean hasSetCloseOnTouchOutside() {
+        return mSetCloseOnTouchOutside;
+    }
+    
+    /** @hide */
+    public abstract void alwaysReadCloseOnTouchAttr();
+    
+    /** @hide */
+    public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
+        if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
+                && isOutOfBounds(context, event) && peekDecorView() != null) {
+            return true;
+        }
+        return false;
+    }
+    
+    private boolean isOutOfBounds(Context context, MotionEvent event) {
+        final int x = (int) event.getX();
+        final int y = (int) event.getY();
+        final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
+        final View decorView = getDecorView();
+        return (x < -slop) || (y < -slop)
+                || (x > (decorView.getWidth()+slop))
+                || (y > (decorView.getHeight()+slop));
+    }
+    
     /**
      * Enable extended screen features.  This must be called before
      * setContentView().  May be called as many times as desired as long as it
diff --git a/core/res/res/values-large/config.xml b/core/res/res/values-large/config.xml
index 05dd050..4449fd0 100644
--- a/core/res/res/values-large/config.xml
+++ b/core/res/res/values-large/config.xml
@@ -22,4 +22,6 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- see comment in values/config.xml -->
     <dimen name="config_prefDialogWidth">440dp</dimen>
+    <!-- see comment in values/config.xml -->
+    <bool name="config_closeDialogWhenTouchOutside">true</bool>
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6ca42e3..de233c8 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -362,6 +362,15 @@
              pointer will go until that pointers go up thereby enabling touches
              with multiple pointers to be split across multiple windows. -->
         <attr name="windowEnableSplitTouch" format="boolean" />
+        
+        <!-- Control whether a container should automatically close itself if
+             the user touches outside of it.  This only applies to activities
+             and dialogs.
+             
+             <p>Note: this attribute will only be respected for applications
+             that are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+             or later. -->
+        <attr name="windowCloseOnTouchOutside" format="boolean" />
 
         <!-- ============ -->
         <!-- Alert Dialog styles -->
@@ -1342,6 +1351,7 @@
         <attr name="windowActionModeOverlay" />
         <attr name="windowActionBarOverlay" />
         <attr name="windowEnableSplitTouch" />
+        <attr name="windowCloseOnTouchOutside" />
         <!-- The minimum width the window is allowed to be, along the major
              axis of the screen.  That is, when in landscape.  Can be either
              an absolute dimension or a fraction of the screen size in that
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 25db21d..552c2f7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -81,6 +81,11 @@
          specified for -large and -xlarge configurations. -->
     <dimen name="config_prefDialogWidth">0px</dimen>
     
+    <!-- Whether dialogs should close automatically when the user touches outside
+         of them.  This should not normally be modified; the default value will
+         pick the correct behavior depending on the screen size. -->
+    <bool name="config_closeDialogWhenTouchOutside">false</bool>
+    
     <!-- The duration (in milliseconds) that the radio will scan for a signal
          when there's no network connection. If the scan doesn't timeout, use zero -->
     <integer name="config_radioScanningTimeout">0</integer>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index eabd457..1b47b54 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1425,6 +1425,7 @@
   <public type="attr" name="queryHint" />
   <public type="attr" name="fastScrollTextColor" />
   <public type="attr" name="largeHeap" />
+  <public type="attr" name="windowCloseOnTouchOutside" />
 
   <!-- A simple fade-in animation. -->
   <public type="animator" name="fade_in" id="0x010b0000" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 506dd07..b257a73 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -138,6 +138,7 @@
         <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
         <item name="windowActionBar">false</item>
         <item name="windowActionModeOverlay">false</item>
+        <item name="windowCloseOnTouchOutside">false</item>
 
         <!-- Dialog attributes -->
         <item name="alertDialogStyle">@android:style/AlertDialog</item>
@@ -506,6 +507,7 @@
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
         <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
+        <item name="android:windowCloseOnTouchOutside">@bool/config_closeDialogWhenTouchOutside</item>
 
         <item name="android:colorBackgroundCacheHint">@null</item>
         
@@ -547,6 +549,7 @@
         <item name="android:backgroundDimEnabled">false</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowNoTitle">true</item>
+        <item name="android:windowCloseOnTouchOutside">false</item>
     </style>
 
     <!-- Default theme for alert dialog windows, which is used by the
@@ -687,6 +690,7 @@
         <item name="windowIsFloating">true</item>
         <item name="windowContentOverlay">@null</item>
         <item name="textAppearance">@style/TextAppearance.Theme.Dialog.AppError</item>
+        <item name="android:windowCloseOnTouchOutside">false</item>
     </style>
     
     <!-- Special theme for the recent apps dialog, to allow customization
@@ -696,6 +700,7 @@
         <item name="windowBackground">@android:color/transparent</item>
         <item name="android:windowAnimationStyle">@android:style/Animation.RecentApplications</item>
         <item name="android:textColor">@android:color/secondary_text_nofocus</item>
+        <item name="android:windowCloseOnTouchOutside">false</item>
     </style>
 
     <!-- Default theme for window that looks like a toast. -->
@@ -703,6 +708,7 @@
         <item name="android:windowBackground">@android:drawable/toast_frame</item>
         <item name="android:windowAnimationStyle">@android:style/Animation.Toast</item>
         <item name="android:backgroundDimEnabled">false</item>
+        <item name="android:windowCloseOnTouchOutside">false</item>
     </style>
 
     <!-- Default theme with an Action Bar. -->
@@ -1291,6 +1297,7 @@
         <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
         <item name="android:windowActionBar">false</item>
         <item name="android:windowActionModeOverlay">true</item>
+        <item name="android:windowCloseOnTouchOutside">@bool/config_closeDialogWhenTouchOutside</item>
 
         <item name="android:colorBackgroundCacheHint">@null</item>
 
@@ -1331,6 +1338,7 @@
         <item name="android:backgroundDimEnabled">false</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowNoTitle">true</item>
+        <item name="android:windowCloseOnTouchOutside">false</item>
     </style>
 
     <!-- Holo theme for alert dialog windows, which is used by the
@@ -1377,6 +1385,7 @@
         <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
         <item name="android:windowActionBar">false</item>
         <item name="android:windowActionModeOverlay">true</item>
+        <item name="android:windowCloseOnTouchOutside">@bool/config_closeDialogWhenTouchOutside</item>
 
         <item name="android:colorBackgroundCacheHint">@null</item>
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 68c1453..fb20e81 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -166,6 +166,8 @@
 
     private int mTitleColor = 0;
 
+    private boolean mAlwaysReadCloseOnTouchAttr = false;
+    
     private ContextMenuBuilder mContextMenu;
     private MenuDialogHelper mContextMenuHelper;
     private ActionButtonSubmenu mActionButtonPopup;
@@ -2326,6 +2328,17 @@
             addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
         }
         
+        if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion
+                >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+            if (!hasSetCloseOnTouchOutside()) {
+                if (a.getBoolean(
+                        com.android.internal.R.styleable.Window_windowCloseOnTouchOutside,
+                        false)) {
+                    setCloseOnTouchOutside(true);
+                }
+            }
+        }
+        
         WindowManager.LayoutParams params = getAttributes();
 
         if (!hasSoftInputMode()) {
@@ -2479,6 +2492,11 @@
         return contentParent;
     }
 
+    /** @hide */
+    public void alwaysReadCloseOnTouchAttr() {
+        mAlwaysReadCloseOnTouchAttr = true;
+    }
+
     private void installDecor() {
         if (mDecor == null) {
             mDecor = generateDecor();