Merge "Fix bug 3391818 - Keyboard covers the "Show in chat list" selection in talk > Friend Info" into honeycomb
diff --git a/api/11.xml b/api/11.xml
index 2033787..6a672d1 100644
--- a/api/11.xml
+++ b/api/11.xml
@@ -70101,7 +70101,7 @@
 </parameter>
 </method>
 <method name="getAttachedDbs"
- return="java.util.ArrayList&lt;android.util.Pair&lt;java.lang.String, java.lang.String&gt;&gt;"
+ return="java.util.List&lt;android.util.Pair&lt;java.lang.String, java.lang.String&gt;&gt;"
  abstract="false"
  native="false"
  synchronized="false"
diff --git a/api/current.xml b/api/current.xml
index 248a1e2..28efc7e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -70123,7 +70123,7 @@
 </parameter>
 </method>
 <method name="getAttachedDbs"
- return="java.util.ArrayList&lt;android.util.Pair&lt;java.lang.String, java.lang.String&gt;&gt;"
+ return="java.util.List&lt;android.util.Pair&lt;java.lang.String, java.lang.String&gt;&gt;"
  abstract="false"
  native="false"
  synchronized="false"
diff --git a/core/java/android/database/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java
index 61337dd..a9e39c3 100644
--- a/core/java/android/database/DefaultDatabaseErrorHandler.java
+++ b/core/java/android/database/DefaultDatabaseErrorHandler.java
@@ -16,7 +16,7 @@
 package android.database;
 
 import java.io.File;
-import java.util.ArrayList;
+import java.util.List;
 
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
@@ -65,7 +65,7 @@
             return;
         }
 
-        ArrayList<Pair<String, String>> attachedDbs = null;
+        List<Pair<String, String>> attachedDbs = null;
         try {
             // Close the database, which will cause subsequent operations to fail.
             // before that, get the attached database list first.
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 1a43b30..b3fd914 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -40,6 +40,7 @@
 import java.io.File;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -2504,7 +2505,7 @@
                 String lastnode = path.substring((indx != -1) ? ++indx : 0);
 
                 // get list of attached dbs and for each db, get its size and pagesize
-                ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs();
+                List<Pair<String, String>> attachedDbs = db.getAttachedDbs();
                 if (attachedDbs == null) {
                     continue;
                 }
@@ -2560,7 +2561,7 @@
      * @return ArrayList of pairs of (database name, database file path) or null if the database
      * is not open.
      */
-    public ArrayList<Pair<String, String>> getAttachedDbs() {
+    public List<Pair<String, String>> getAttachedDbs() {
         if (!isOpen()) {
             return null;
         }
@@ -2613,7 +2614,7 @@
      */
     public boolean isDatabaseIntegrityOk() {
         verifyDbIsOpen();
-        ArrayList<Pair<String, String>> attachedDbs = null;
+        List<Pair<String, String>> attachedDbs = null;
         try {
             attachedDbs = getAttachedDbs();
             if (attachedDbs == null) {
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index ec3c329..b7ffd14 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -88,6 +88,9 @@
     // Attached Javascript interfaces
     private Map<String, Object> mJSInterfaceMap;
 
+    // Key store handler when Chromium HTTP stack is used.
+    private KeyStoreHandler mKeyStoreHandler = null;
+
     // message ids
     // a message posted when a frame loading is completed
     static final int FRAME_COMPLETED = 1001;
@@ -1173,8 +1176,27 @@
         }
         mimeType = MimeTypeMap.getSingleton().remapGenericMimeType(
                 mimeType, url, contentDisposition);
-        mCallbackProxy.onDownloadStart(url, userAgent,
+
+        if (CertTool.getCertType(mimeType) != null) {
+            mKeyStoreHandler = new KeyStoreHandler(mimeType);
+        } else {
+            mCallbackProxy.onDownloadStart(url, userAgent,
                 contentDisposition, mimeType, contentLength);
+        }
+    }
+
+    /**
+     * Called by JNI for Chrome HTTP stack when the Java side needs to access the data.
+     */
+    private void didReceiveData(byte data[], int size) {
+        if (mKeyStoreHandler != null) mKeyStoreHandler.didReceiveData(data, size);
+    }
+
+    private void didFinishLoading() {
+      if (mKeyStoreHandler != null) {
+          mKeyStoreHandler.installCert(mContext);
+          mKeyStoreHandler = null;
+      }
     }
 
     /**
diff --git a/core/java/android/webkit/CertTool.java b/core/java/android/webkit/CertTool.java
index d25d970..4c534f9 100644
--- a/core/java/android/webkit/CertTool.java
+++ b/core/java/android/webkit/CertTool.java
@@ -29,6 +29,7 @@
 
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
+import java.util.HashMap;
 
 class CertTool {
     private static final String LOGTAG = "CertTool";
@@ -39,6 +40,14 @@
     static final String CERT = Credentials.CERTIFICATE;
     static final String PKCS12 = Credentials.PKCS12;
 
+    private static HashMap<String, String> sCertificateTypeMap;
+    static {
+        sCertificateTypeMap = new HashMap<String, String>();
+        sCertificateTypeMap.put("application/x-x509-ca-cert", CertTool.CERT);
+        sCertificateTypeMap.put("application/x-x509-user-cert", CertTool.CERT);
+        sCertificateTypeMap.put("application/x-pkcs12", CertTool.PKCS12);
+    }
+
     static String[] getKeyStrengthList() {
         return new String[] {"High Grade", "Medium Grade"};
     }
@@ -66,5 +75,9 @@
         Credentials.getInstance().install(context, type, value);
     }
 
+    static String getCertType(String mimeType) {
+        return sCertificateTypeMap.get(mimeType);
+  }
+
     private CertTool() {}
 }
diff --git a/core/java/android/webkit/KeyStoreHandler.java b/core/java/android/webkit/KeyStoreHandler.java
new file mode 100644
index 0000000..849007e
--- /dev/null
+++ b/core/java/android/webkit/KeyStoreHandler.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.Log;
+
+/**
+ * KeyStoreHandler: class responsible for certificate installation to
+ * the system key store. It reads the certificates file from network
+ * then pass the bytes to class CertTool.
+ * This class is only needed if the Chromium HTTP stack is used.
+ */
+class KeyStoreHandler extends Handler {
+    private static final String LOGTAG = "KeyStoreHandler";
+
+    private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder();
+
+    private String mMimeType;
+
+    public KeyStoreHandler(String mimeType) {
+      mMimeType = mimeType;
+    }
+
+    /**
+     * Add data to the internal collection of data.
+     * @param data A byte array containing the content.
+     * @param length The length of data.
+     */
+    public void didReceiveData(byte[] data, int length) {
+        synchronized (mDataBuilder) {
+            mDataBuilder.append(data, 0, length);
+        }
+    }
+
+    public void installCert(Context context) {
+        String type = CertTool.getCertType(mMimeType);
+        if (type == null) return;
+
+        // This must be synchronized so that no more data can be added
+        // after getByteSize returns.
+        synchronized (mDataBuilder) {
+            // In the case of downloading certificate, we will save it
+            // to the KeyStore and stop the current loading so that it
+            // will not generate a new history page
+            byte[] cert = new byte[mDataBuilder.getByteSize()];
+            int offset = 0;
+            while (true) {
+                ByteArrayBuilder.Chunk c = mDataBuilder.getFirstChunk();
+                if (c == null) break;
+
+                if (c.mLength != 0) {
+                    System.arraycopy(c.mArray, 0, cert, offset, c.mLength);
+                    offset += c.mLength;
+                }
+                c.release();
+            }
+            CertTool.addCertificate(context, type, cert);
+            return;
+        }
+    }
+}
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index 6d1d39a..04af738 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -75,14 +75,6 @@
     private static final int HTTP_NOT_FOUND = 404;
     private static final int HTTP_PROXY_AUTH = 407;
 
-    private static HashMap<String, String> sCertificateTypeMap;
-    static {
-        sCertificateTypeMap = new HashMap<String, String>();
-        sCertificateTypeMap.put("application/x-x509-ca-cert", CertTool.CERT);
-        sCertificateTypeMap.put("application/x-x509-user-cert", CertTool.CERT);
-        sCertificateTypeMap.put("application/x-pkcs12", CertTool.PKCS12);
-    }
-
     private static int sNativeLoaderCount;
 
     private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder();
@@ -1053,7 +1045,7 @@
 
     // This commits the headers without checking the response status code.
     private void commitHeaders() {
-        if (mIsMainPageLoader && sCertificateTypeMap.containsKey(mMimeType)) {
+        if (mIsMainPageLoader && CertTool.getCertType(mMimeType) != null) {
             // In the case of downloading certificate, we will save it to the
             // KeyStore in commitLoad. Do not call webcore.
             return;
@@ -1114,7 +1106,7 @@
         }
 
         if (mIsMainPageLoader) {
-            String type = sCertificateTypeMap.get(mMimeType);
+            String type = CertTool.getCertType(mMimeType);
             if (type != null) {
                 // This must be synchronized so that no more data can be added
                 // after getByteSize returns.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index b024dcb..b217052 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3320,7 +3320,7 @@
 
             } else if (actionCode == EditorInfo.IME_ACTION_DONE) {
                 InputMethodManager imm = InputMethodManager.peekInstance();
-                if (imm != null) {
+                if (imm != null && imm.isActive(this)) {
                     imm.hideSoftInputFromWindow(getWindowToken(), 0);
                 }
                 return;
@@ -4822,9 +4822,8 @@
                     if (mOnClickListener == null) {
                         if (mMovement != null && mText instanceof Editable
                                 && mLayout != null && onCheckIsTextEditor()) {
-                            InputMethodManager imm = (InputMethodManager)
-                                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-                            imm.showSoftInput(this, 0);
+                            InputMethodManager imm = InputMethodManager.peekInstance();
+                            if (imm != null) imm.showSoftInput(this, 0);
                         }
                     }
                 }
@@ -4877,7 +4876,7 @@
                                 // No target for next focus, but make sure the IME
                                 // if this came from it.
                                 InputMethodManager imm = InputMethodManager.peekInstance();
-                                if (imm != null) {
+                                if (imm != null && imm.isActive(this)) {
                                     imm.hideSoftInputFromWindow(getWindowToken(), 0);
                                 }
                             }
@@ -7149,10 +7148,8 @@
         // the IME. Showing the IME while focus is moved using the D-Pad is a bad idea, however this
         // does not happen in that case (using the arrows on a bluetooth keyboard).
         if (focused && isTextEditable()) {
-            final InputMethodManager imm = (InputMethodManager)
-            getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-
-            imm.showSoftInput(this, 0, null);
+            final InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) imm.showSoftInput(this, 0, null);
         }
     }
 
@@ -7346,10 +7343,8 @@
 
                     // Show the IME, except when selecting in read-only text.
                     if (!mTextIsSelectable) {
-                        final InputMethodManager imm = (InputMethodManager)
-                                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-
-                        handled |= imm.showSoftInput(this, 0, csr) && (csr != null);
+                        final InputMethodManager imm = InputMethodManager.peekInstance();
+                        handled |= imm != null && imm.showSoftInput(this, 0, csr) && (csr != null);
                     }
 
                     stopSelectionActionMode();
@@ -8247,16 +8242,17 @@
             selectCurrentWord();
         }
 
-        if (!mTextIsSelectable) {
-            // Show the IME, except when selection non editable text.
-            final InputMethodManager imm = (InputMethodManager)
-                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-            imm.showSoftInput(this, 0, null);
-        }
-
         ActionMode.Callback actionModeCallback = new SelectionActionModeCallback();
         mSelectionActionMode = startActionMode(actionModeCallback);
-        return mSelectionActionMode != null;
+        final boolean selectionStarted = mSelectionActionMode != null;
+
+        if (selectionStarted && !mTextIsSelectable) {
+            // Show the IME to be able to replace text, except when selecting non editable text.
+            final InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) imm.showSoftInput(this, 0, null);
+        }
+
+        return selectionStarted;
     }
 
     /**
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim0.png b/core/res/res/drawable-hdpi/stat_sys_download_anim0.png
index 8858a75b..943e620 100644
--- a/core/res/res/drawable-hdpi/stat_sys_download_anim0.png
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim1.png b/core/res/res/drawable-hdpi/stat_sys_download_anim1.png
index 5418785..28edae1 100644
--- a/core/res/res/drawable-hdpi/stat_sys_download_anim1.png
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim2.png b/core/res/res/drawable-hdpi/stat_sys_download_anim2.png
index f55887e..556a46f 100644
--- a/core/res/res/drawable-hdpi/stat_sys_download_anim2.png
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim3.png b/core/res/res/drawable-hdpi/stat_sys_download_anim3.png
index 50fcc36..422d72b 100644
--- a/core/res/res/drawable-hdpi/stat_sys_download_anim3.png
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim4.png b/core/res/res/drawable-hdpi/stat_sys_download_anim4.png
index 65ac698..24a0db5 100644
--- a/core/res/res/drawable-hdpi/stat_sys_download_anim4.png
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim5.png b/core/res/res/drawable-hdpi/stat_sys_download_anim5.png
index 7fb6032..97e5a6b 100644
--- a/core/res/res/drawable-hdpi/stat_sys_download_anim5.png
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim5.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim0.png b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
index a326868..39e4ccb 100644
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim1.png b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
index 42e5c2a..429ebd7 100644
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim2.png b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
index 0c27622..b3de461 100644
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim3.png b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
index b1b84d4..195498e 100644
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim4.png b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
index 75cc99c..8fc3e86 100644
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim5.png b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
index c21b6ac..c6f2f4e 100644
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
Binary files differ
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
index cd38bf07..6786700 100644
--- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -37,6 +37,7 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 
 public class DatabaseGeneralTest extends AndroidTestCase implements PerformanceTestCase {
@@ -1130,7 +1131,7 @@
         assertTrue(new File(attachedDb1File).exists());
         assertNotNull(dbObj);
         assertTrue(dbObj.isOpen());
-        ArrayList<Pair<String, String>> attachedDbs = dbObj.getAttachedDbs();
+        List<Pair<String, String>> attachedDbs = dbObj.getAttachedDbs();
         try {
             errorHandler.onCorruption(dbObj);
             assertFalse(dbfile.exists());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
index 1004e18..372aa90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
@@ -60,6 +60,9 @@
     View mSettingsView;
     ViewGroup mContentParent;
 
+    // amount to slide mContentParent down by when mContentFrame is missing
+    float mContentFrameMissingTranslation;
+
     Choreographer mChoreo = new Choreographer();
 
     public NotificationPanel(Context context, AttributeSet attrs) {
@@ -87,11 +90,17 @@
 
         mNotificationScroller = findViewById(R.id.notification_scroller);
         mContentFrame = (ViewGroup)findViewById(R.id.content_frame);
+        mContentFrameMissingTranslation =
+            mContentFrame.getBackground().getMinimumHeight() + 10;
+
+        mShowing = false;
+
+        setContentFrameVisible(mNotificationCount > 0, false);
     }
 
     public void show(boolean show, boolean animate) {
         if (show && !mShowing) {
-            setContentFrameVisible(mNotificationCount > 0, false);
+            setContentFrameVisible(mSettingsView != null || mNotificationCount > 0, false);
         }
 
         if (animate) {
@@ -120,7 +129,7 @@
     public void onVisibilityChanged(View v, int vis) {
         super.onVisibilityChanged(v, vis);
         // when we hide, put back the notifications
-        if (!isShown()) {
+        if (vis != View.VISIBLE) {
             if (mSettingsView != null) removeSettingsView();
             mNotificationScroller.setVisibility(View.VISIBLE);
             mNotificationScroller.setAlpha(1f);
@@ -161,10 +170,8 @@
             setContentFrameVisible(n > 0, false);
         } else if (mSettingsView == null) {
             // we're looking at the notifications; time to maybe make some changes
-            if (mNotificationCount == 0 && n > 0) {
-                setContentFrameVisible(true, true);
-            } else if (mNotificationCount > 0 && n == 0) {
-                setContentFrameVisible(false, true);
+            if (mNotificationCount != n) {
+                setContentFrameVisible(n > 0, true);
             }
         }
         mNotificationCount = n;
@@ -173,22 +180,35 @@
     public void setContentFrameVisible(final boolean showing, boolean animate) {
         if (!animate) {
             mContentFrame.setVisibility(showing ? View.VISIBLE : View.GONE);
-            mContentParent.setTranslationY(showing ? 0f : 100f);
+            mContentFrame.setAlpha(1f);
+            // the translation will be patched up when the window is slid into place
             return;
         }
 
-        mContentFrame.setVisibility(showing ? View.VISIBLE : View.GONE);
+        if (showing) {
+            mContentFrame.setVisibility(View.VISIBLE);
+        }
         AnimatorSet set = new AnimatorSet();
-        float adjust = mContentFrame.getBackground().getMinimumHeight() + 8; // fudge factor
         set.play(ObjectAnimator.ofFloat(
                 mContentFrame, "alpha",
                 showing ? 0f : 1f,
                 showing ? 1f : 0f))
             .with(ObjectAnimator.ofFloat(
                 mContentParent, "translationY",
-                showing ? adjust : 0f,
-                showing ? 0f : adjust));
+                showing ? mContentFrameMissingTranslation : 0f,
+                showing ? 0f : mContentFrameMissingTranslation))
+              ;
+
         set.setDuration(200);
+        if (!showing) {
+            set.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator _a) {
+                    mContentFrame.setVisibility(View.GONE);
+                    mContentFrame.setAlpha(1f);
+                }
+            });
+        }
         set.start();
     }
 
@@ -238,10 +258,15 @@
     }
 
     public boolean isInContentArea(int x, int y) {
-        mContentArea.left = mContentFrame.getLeft() + mContentFrame.getPaddingLeft();
-        mContentArea.top = mTitleArea.getTop() + mTitleArea.getPaddingTop();
-        mContentArea.right = mContentFrame.getRight() - mContentFrame.getPaddingRight();
-        mContentArea.bottom = mContentFrame.getBottom() - mContentFrame.getPaddingBottom();
+        mContentArea.left = mTitleArea.getLeft() + mTitleArea.getPaddingLeft();
+        mContentArea.top = mTitleArea.getTop() + mTitleArea.getPaddingTop() 
+            + (int)mContentParent.getTranslationY(); // account for any adjustment
+        mContentArea.right = mTitleArea.getRight() - mTitleArea.getPaddingRight();
+
+        View theBottom = (mContentFrame.getVisibility() == View.VISIBLE)
+            ? mContentFrame : mTitleArea;
+        mContentArea.bottom = theBottom.getBottom() - theBottom.getPaddingBottom();
+
         offsetDescendantRectToMyCoords(mContentParent, mContentArea);
         return mContentArea.contains(x, y);
     }
@@ -291,12 +316,16 @@
             if (appearing) {
                 // we want to go from near-the-top to the top, unless we're half-open in the right
                 // general vicinity
-                start = (y < HYPERSPACE_OFFRAMP) ? y : HYPERSPACE_OFFRAMP;
                 end = 0;
+                if (mNotificationCount == 0) {
+                    end += mContentFrameMissingTranslation;
+                }
+                start = (y < (HYPERSPACE_OFFRAMP+end)) ? y : (HYPERSPACE_OFFRAMP+end);
             } else {
                 start = y;
                 end = y + HYPERSPACE_OFFRAMP;
             }
+
             Animator posAnim = ObjectAnimator.ofFloat(mContentParent, "translationY",
                     start, end);
             posAnim.setInterpolator(appearing