Merge "Ensuring that the db reflects all package added/removed/updated broadcasts." into jb-ub-gel-agar
diff --git a/src/com/android/launcher3/MemoryDumpActivity.java b/src/com/android/launcher3/MemoryDumpActivity.java
index 447f0dd..d79be80 100644
--- a/src/com/android/launcher3/MemoryDumpActivity.java
+++ b/src/com/android/launcher3/MemoryDumpActivity.java
@@ -28,6 +28,7 @@
 
 import java.io.*;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
@@ -85,7 +86,9 @@
         final ArrayList<String> paths = new ArrayList<String>();
         final int myPid = android.os.Process.myPid();
 
-        for (int pid : tracker.getTrackedProcesses()) {
+        final int[] pids_orig = tracker.getTrackedProcesses();
+        final int[] pids_copy = Arrays.copyOf(pids_orig, pids_orig.length);
+        for (int pid : pids_copy) {
             MemoryTracker.ProcessMemInfo info = tracker.getMemInfo(pid);
             if (info != null) {
                 body.append("pid ").append(pid).append(":")
diff --git a/src/com/android/launcher3/MemoryTracker.java b/src/com/android/launcher3/MemoryTracker.java
index 87a73f3..d3bb77c 100644
--- a/src/com/android/launcher3/MemoryTracker.java
+++ b/src/com/android/launcher3/MemoryTracker.java
@@ -26,6 +26,7 @@
 import android.util.LongSparseArray;
 
 import java.util.ArrayList;
+import java.util.List;
 
 public class MemoryTracker extends Service {
     public static final String TAG = MemoryTracker.class.getSimpleName();
@@ -47,10 +48,10 @@
             //= new Meminfo[(int) (30 * 60 / (UPDATE_RATE / 1000))]; // 30 minutes
         public long max = 1;
         public int head = 0;
-        public ProcessMemInfo(int pid, String name) {
+        public ProcessMemInfo(int pid, String name, long start) {
             this.pid = pid;
             this.name = name;
-            this.startTime = System.currentTimeMillis();
+            this.startTime = start;
         }
         public long getUptime() {
             return System.currentTimeMillis() - startTime;
@@ -99,7 +100,7 @@
         return mPidsArray;
     }
 
-    public void startTrackingProcess(int pid, String name) {
+    public void startTrackingProcess(int pid, String name, long start) {
         synchronized (mLock) {
             final Long lpid = new Long(pid);
 
@@ -114,7 +115,7 @@
                 mPidsArray[i] = p;
                 sb.append(p); sb.append(" ");
             }
-            mData.put(pid, new ProcessMemInfo(pid, name));
+            mData.put(pid, new ProcessMemInfo(pid, name, start));
             Log.v(TAG, sb.toString());
         }
     }
@@ -149,6 +150,22 @@
     @Override
     public void onCreate() {
         mAm = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+
+        // catch up in case we crashed but other processes are still running
+        List<ActivityManager.RunningServiceInfo> svcs = mAm.getRunningServices(256);
+        for (ActivityManager.RunningServiceInfo svc : svcs) {
+            if (svc.service.getPackageName().equals(getPackageName())) {
+                startTrackingProcess(svc.pid, svc.process, System.currentTimeMillis() - (SystemClock.elapsedRealtime() - svc.activeSince));
+            }
+        }
+
+        List<ActivityManager.RunningAppProcessInfo> procs = mAm.getRunningAppProcesses();
+        for (ActivityManager.RunningAppProcessInfo proc : procs) {
+            final String pname = proc.processName;
+            if (pname.startsWith(getPackageName())) {
+                startTrackingProcess(proc.pid, pname, System.currentTimeMillis());
+            }
+        }
     }
 
     @Override
@@ -164,7 +181,8 @@
             if (ACTION_START_TRACKING.equals(intent.getAction())) {
                 final int pid = intent.getIntExtra("pid", -1);
                 final String name = intent.getStringExtra("name");
-                startTrackingProcess(pid, name);
+                final long start = intent.getLongExtra("start", System.currentTimeMillis());
+                startTrackingProcess(pid, name, start);
             }
         }
 
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 0a20724..c2b9bd7 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -454,9 +454,13 @@
      * the previous tab page.
      */
     protected void updateCurrentPageScroll() {
-        int offset = getChildOffset(mCurrentPage);
-        int relOffset = getRelativeChildOffset(mCurrentPage);
-        int newX = offset - relOffset;
+        // If the current page is invalid, just reset the scroll position to zero
+        int newX = 0;
+        if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
+            int offset = getChildOffset(mCurrentPage);
+            int relOffset = getRelativeChildOffset(mCurrentPage);
+            newX = offset - relOffset;
+        }
         scrollTo(newX, 0);
         mScroller.setFinalX(newX);
         mScroller.forceFinished(true);
@@ -553,17 +557,28 @@
 
     @Override
     public void scrollTo(int x, int y) {
+        final boolean isRtl = isLayoutRtl();
         mUnboundedScrollX = x;
 
-        if (x < 0) {
+        boolean isXBeforeFirstPage = isRtl ? (x > mMaxScrollX) : (x < 0);
+        boolean isXAfterLastPage = isRtl ? (x < 0) : (x > mMaxScrollX);
+        if (isXBeforeFirstPage) {
             super.scrollTo(0, y);
             if (mAllowOverScroll) {
-                overScroll(x);
+                if (isRtl) {
+                    overScroll(x - mMaxScrollX);
+                } else {
+                    overScroll(x);
+                }
             }
-        } else if (x > mMaxScrollX) {
+        } else if (isXAfterLastPage) {
             super.scrollTo(mMaxScrollX, y);
             if (mAllowOverScroll) {
-                overScroll(x - mMaxScrollX);
+                if (isRtl) {
+                    overScroll(x);
+                } else {
+                    overScroll(x - mMaxScrollX);
+                }
             }
         } else {
             mOverScrollX = x;
@@ -605,6 +620,15 @@
             }
 
             onPostReorderingAnimationCompleted();
+            // Notify the user when the page changes
+            AccessibilityManager accessibilityManager = (AccessibilityManager)
+                    getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+            if (accessibilityManager.isEnabled()) {
+                AccessibilityEvent ev =
+                    AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+                ev.getText().add(getCurrentPageDescription());
+                sendAccessibilityEventUnchecked(ev);
+            }
             return true;
         }
         return false;
@@ -729,7 +753,8 @@
         updateScrollingIndicatorPosition();
 
         if (childCount > 0) {
-            mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1);
+            final int index = isLayoutRtl() ? 0 : childCount - 1;
+            mMaxScrollX = getChildOffset(index) - getRelativeChildOffset(index);
         } else {
             mMaxScrollX = 0;
         }
@@ -755,9 +780,15 @@
         // Update the viewport offsets
         mViewport.offset(offsetX,  offsetY);
 
+        final boolean isRtl = isLayoutRtl();
+
+        final int startIndex = isRtl ? childCount - 1 : 0;
+        final int endIndex = isRtl ? -1 : childCount;
+        final int delta = isRtl ? -1 : 1;
+
         int verticalPadding = getPaddingTop() + getPaddingBottom();
-        int childLeft = offsetX + getRelativeChildOffset(0);
-        for (int i = 0; i < childCount; i++) {
+        int childLeft = offsetX + getRelativeChildOffset(startIndex);
+        for (int i = startIndex; i != endIndex; i += delta) {
             final View child = getPageAt(i);
             int childTop = offsetY + getPaddingTop();
             if (mCenterPagesVertically) {
@@ -840,6 +871,7 @@
     protected int getChildOffset(int index) {
         if (index < 0 || index > getChildCount() - 1) return 0;
 
+        final boolean isRtl = isLayoutRtl();
         int[] childOffsets = Float.compare(mLayoutScale, 1f) == 0 ?
                 mChildOffsets : mChildOffsetsWithLayoutScale;
 
@@ -849,8 +881,13 @@
             if (getChildCount() == 0)
                 return 0;
 
-            int offset = getRelativeChildOffset(0);
-            for (int i = 0; i < index; ++i) {
+
+            final int startIndex = isRtl ? getChildCount() - 1 : 0;
+            final int endIndex = isRtl ? index : index;
+            final int delta = isRtl ? -1 : 1;
+
+            int offset = getRelativeChildOffset(startIndex);
+            for (int i = startIndex; i != endIndex; i += delta) {
                 offset += getScaledMeasuredWidth(getPageAt(i)) + mPageSpacing;
             }
             if (childOffsets != null) {
@@ -1007,6 +1044,7 @@
 
     @Override
     public boolean dispatchUnhandledMove(View focused, int direction) {
+        // XXX-RTL: This will be fixed in a future CL
         if (direction == View.FOCUS_LEFT) {
             if (getCurrentPage() > 0) {
                 snapToPage(getCurrentPage() - 1);
@@ -1023,6 +1061,7 @@
 
     @Override
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+        // XXX-RTL: This will be fixed in a future CL
         if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
             getPageAt(mCurrentPage).addFocusables(views, direction, focusableMode);
         }
@@ -1083,6 +1122,10 @@
      * Return true if a tap at (x, y) should trigger a flip to the previous page.
      */
     protected boolean hitsPreviousPage(float x, float y) {
+        if (isLayoutRtl()) {
+            return (x > (getViewportOffsetX() + getViewportWidth() -
+                    getRelativeChildOffset(mCurrentPage) + mPageSpacing));
+        }
         return (x < getViewportOffsetX() + getRelativeChildOffset(mCurrentPage) - mPageSpacing);
     }
 
@@ -1090,7 +1133,11 @@
      * Return true if a tap at (x, y) should trigger a flip to the next page.
      */
     protected boolean hitsNextPage(float x, float y) {
-        return  (x > (getViewportOffsetX() + getViewportWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
+        if (isLayoutRtl()) {
+            return (x < getViewportOffsetX() + getRelativeChildOffset(mCurrentPage) - mPageSpacing);
+        }
+        return  (x > (getViewportOffsetX() + getViewportWidth() -
+                getRelativeChildOffset(mCurrentPage) + mPageSpacing));
     }
 
     /** Returns whether x and y originated within the buffered viewport */
@@ -1602,19 +1649,21 @@
                 // We give flings precedence over large moves, which is why we short-circuit our
                 // test for a large move if a fling has been registered. That is, a large
                 // move to the left and fling to the right will register as a fling to the right.
-                if (((isSignificantMove && deltaX > 0 && !isFling) ||
-                        (isFling && velocityX > 0)) && mCurrentPage > 0) {
+                final boolean isRtl = isLayoutRtl();
+                boolean isDeltaXLeft = isRtl ? deltaX > 0 : deltaX < 0;
+                boolean isVelocityXLeft = isRtl ? velocityX > 0 : velocityX < 0;
+                if (((isSignificantMove && !isDeltaXLeft && !isFling) ||
+                        (isFling && !isVelocityXLeft)) && mCurrentPage > 0) {
                     finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
                     snapToPageWithVelocity(finalPage, velocityX);
-                } else if (((isSignificantMove && deltaX < 0 && !isFling) ||
-                        (isFling && velocityX < 0)) &&
+                } else if (((isSignificantMove && isDeltaXLeft && !isFling) ||
+                        (isFling && isVelocityXLeft)) &&
                         mCurrentPage < getChildCount() - 1) {
                     finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
                     snapToPageWithVelocity(finalPage, velocityX);
                 } else {
                     snapToDestination();
-                }
-            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
+                }            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
                 // at this point we have not moved beyond the touch slop
                 // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
                 // we can just page
@@ -1714,7 +1763,9 @@
                         hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
                     }
                     if (hscroll != 0 || vscroll != 0) {
-                        if (hscroll > 0 || vscroll > 0) {
+                        boolean isForwardScroll = isLayoutRtl() ? (hscroll < 0 || vscroll < 0)
+                                                         : (hscroll > 0 || vscroll > 0);
+                        if (isForwardScroll) {
                             scrollRight();
                         } else {
                             scrollLeft();
@@ -1770,10 +1821,14 @@
     }
 
     protected int getChildIndexForRelativeOffset(int relativeOffset) {
+        final boolean isRtl = isLayoutRtl();
         final int childCount = getChildCount();
         int left;
         int right;
-        for (int i = 0; i < childCount; ++i) {
+        final int startIndex = isRtl ? childCount - 1 : 0;
+        final int endIndex = isRtl ? -1 : childCount;
+        final int delta = isRtl ? -1 : 1;
+        for (int i = startIndex; i != endIndex; i += delta) {
             left = getRelativeChildOffset(i);
             right = (left + getScaledMeasuredWidth(getPageAt(i)));
             if (left <= relativeOffset && relativeOffset <= right) {
@@ -2234,6 +2289,7 @@
     }
 
     private void updateScrollingIndicatorPosition() {
+        final boolean isRtl = isLayoutRtl();
         if (!isScrollingIndicatorEnabled()) return;
         if (mScrollIndicator == null) return;
         int numPages = getChildCount();
@@ -2244,7 +2300,11 @@
         int indicatorWidth = mScrollIndicator.getMeasuredWidth() -
                 mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight();
 
-        float offset = Math.max(0f, Math.min(1f, (float) getScrollX() / maxScrollX));
+        float scrollPos = isRtl ? mMaxScrollX - getScrollX() : getScrollX();
+        float offset = Math.max(0f, Math.min(1f, (float) scrollPos / mMaxScrollX));
+        if (isRtl) {
+            offset = 1f - offset;
+        }
         int indicatorSpace = trackWidth / numPages;
         int indicatorPos = (int) (offset * (trackWidth - indicatorSpace)) + mScrollIndicatorPaddingLeft;
         if (hasElasticScrollIndicator()) {
@@ -2770,6 +2830,11 @@
         return false;
     }
 
+    protected String getCurrentPageDescription() {
+        return String.format(getContext().getString(R.string.default_scroll_format),
+                getNextPage() + 1, getChildCount());
+    }
+
     @Override
     public boolean onHoverEvent(android.view.MotionEvent event) {
         return true;
diff --git a/src/com/android/launcher3/WeightWatcher.java b/src/com/android/launcher3/WeightWatcher.java
index 2eac063..70b8afe 100644
--- a/src/com/android/launcher3/WeightWatcher.java
+++ b/src/com/android/launcher3/WeightWatcher.java
@@ -213,7 +213,11 @@
         public void update() {
             //Log.v("WeightWatcher.ProcessWatcher",
             //        "MSG_UPDATE pss=" + mMemInfo.currentPss);
-            mText.setText("(" + mMemInfo.name + "/" + mPid + ") up " + getUptimeString()
+            mText.setText("(" + mPid
+                          + (mPid == android.os.Process.myPid()
+                                ? "/A"  // app
+                                : "/S") // service
+                          + ") up " + getUptimeString()
                           + " P=" + mMemInfo.currentPss
                           + " U=" + mMemInfo.currentUss
                           );