Merge "Change to quickcontact landscape style on 7"" into jb-dev
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 0502d4d..075c59f 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -150,7 +150,7 @@
 
     private ContactsUnavailableFragment mContactsUnavailableFragment;
     private ProviderStatusWatcher mProviderStatusWatcher;
-    private int mProviderStatus;
+    private ProviderStatusWatcher.Status mProviderStatus;
 
     private boolean mOptionsMenuContactsAvailable;
 
@@ -217,7 +217,8 @@
     }
 
     public boolean areContactsAvailable() {
-        return mProviderStatus == ProviderStatus.STATUS_NORMAL;
+        return (mProviderStatus != null)
+                && mProviderStatus.status == ProviderStatus.STATUS_NORMAL;
     }
 
     private boolean areContactWritableAccountsAvailable() {
@@ -991,14 +992,15 @@
     }
 
     private void updateViewConfiguration(boolean forceUpdate) {
-        int providerStatus = mProviderStatusWatcher.getProviderStatus();
-        if (!forceUpdate && (providerStatus == mProviderStatus)) return;
+        ProviderStatusWatcher.Status providerStatus = mProviderStatusWatcher.getProviderStatus();
+        if (!forceUpdate && (mProviderStatus != null)
+                && (providerStatus.status == mProviderStatus.status)) return;
         mProviderStatus = providerStatus;
 
         View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view);
         View mainView = findViewById(R.id.main_view);
 
-        if (mProviderStatus == ProviderStatus.STATUS_NORMAL) {
+        if (mProviderStatus.status == ProviderStatus.STATUS_NORMAL) {
             // Ensure that the mTabPager is visible; we may have made it invisible below.
             contactsUnavailableView.setVisibility(View.GONE);
             if (mTabPager != null) {
@@ -1033,9 +1035,8 @@
                 getFragmentManager().beginTransaction()
                         .replace(R.id.contacts_unavailable_container, mContactsUnavailableFragment)
                         .commitAllowingStateLoss();
-            } else {
-                mContactsUnavailableFragment.update();
             }
+            mContactsUnavailableFragment.updateStatus(mProviderStatus);
 
             // Show the contactsUnavailableView, and hide the mTabPager so that we don't
             // see it sliding in underneath the contactsUnavailableView at the edges.
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index 89585e8..745c044 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -73,6 +73,7 @@
 import com.android.contacts.activities.DialtactsActivity;
 import com.android.contacts.util.Constants;
 import com.android.contacts.util.PhoneNumberFormatter;
+import com.android.contacts.util.StopWatch;
 import com.android.internal.telephony.ITelephony;
 import com.android.phone.CallLogAsync;
 import com.android.phone.HapticFeedback;
@@ -468,17 +469,25 @@
     public void onResume() {
         super.onResume();
 
+        final StopWatch stopWatch = StopWatch.start("Dialpad.onResume");
+
         // Query the last dialed number. Do it first because hitting
         // the DB is 'slow'. This call is asynchronous.
         queryLastOutgoingCall();
 
+        stopWatch.lap("qloc");
+
         // retrieve the DTMF tone play back setting.
         mDTMFToneEnabled = Settings.System.getInt(getActivity().getContentResolver(),
                 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
 
+        stopWatch.lap("dtwd");
+
         // Retrieve the haptic feedback setting.
         mHaptic.checkSystemSetting();
 
+        stopWatch.lap("hptc");
+
         // if the mToneGenerator creation fails, just continue without it.  It is
         // a local audio signal, and is not as important as the dtmf tone itself.
         synchronized (mToneGeneratorLock) {
@@ -491,6 +500,7 @@
                 }
             }
         }
+        stopWatch.lap("tg");
         // Prevent unnecessary confusion. Reset the press count anyway.
         mDialpadPressCount = 0;
 
@@ -501,6 +511,8 @@
             fillDigitsIfNecessary(parent.getIntent());
         }
 
+        stopWatch.lap("fdin");
+
         // While we're in the foreground, listen for phone state changes,
         // purely so that we can take down the "dialpad chooser" if the
         // phone becomes idle while the chooser UI is visible.
@@ -508,6 +520,8 @@
                 (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
         telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
 
+        stopWatch.lap("tm");
+
         // Potentially show hint text in the mDigits field when the user
         // hasn't typed any digits yet.  (If there's already an active call,
         // this hint text will remind the user that he's about to add a new
@@ -531,7 +545,13 @@
             showDialpadChooser(false);
         }
 
+        stopWatch.lap("hnt");
+
         updateDialAndDeleteButtonEnabledState();
+
+        stopWatch.lap("bes");
+
+        stopWatch.stopAndLog(TAG, 50);
     }
 
     @Override
diff --git a/src/com/android/contacts/list/ContactsUnavailableFragment.java b/src/com/android/contacts/list/ContactsUnavailableFragment.java
index becc704..b645425 100644
--- a/src/com/android/contacts/list/ContactsUnavailableFragment.java
+++ b/src/com/android/contacts/list/ContactsUnavailableFragment.java
@@ -18,6 +18,7 @@
 import com.android.contacts.R;
 
 import android.app.Fragment;
+import android.content.Context;
 import android.os.Bundle;
 import android.provider.ContactsContract.ProviderStatus;
 import android.view.Gravity;
@@ -35,8 +36,6 @@
  */
 public class ContactsUnavailableFragment extends Fragment implements OnClickListener {
 
-    private ProviderStatusWatcher mProviderStatusWatcher;
-
     private View mView;
     private TextView mMessageView;
     private TextView mSecondaryMessageView;
@@ -51,10 +50,11 @@
 
     private OnContactsUnavailableActionListener mListener;
 
+    private ProviderStatusWatcher.Status mProviderStatus;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mProviderStatusWatcher = ProviderStatusWatcher.getInstance(getActivity());
     }
 
     @Override
@@ -74,7 +74,11 @@
         mRetryUpgradeButton = (Button) mView.findViewById(R.id.import_failure_retry_button);
         mRetryUpgradeButton.setOnClickListener(this);
         mProgress = (ProgressBar) mView.findViewById(R.id.progress);
-        update();
+
+        if (mProviderStatus != null) {
+            updateStatus(mProviderStatus);
+        }
+
         return mView;
     }
 
@@ -83,9 +87,13 @@
         mListener = listener;
     }
 
-    public void update() {
-        int providerStatus = mProviderStatusWatcher.getProviderStatus();
-        switch (providerStatus) {
+    public void updateStatus(ProviderStatusWatcher.Status providerStatus) {
+        mProviderStatus = providerStatus;
+        if (mView == null) {
+            // The view hasn't been inflated yet.
+            return;
+        }
+        switch (providerStatus.status) {
             case ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS:
                 setMessageText(mNoContactsMsgResId, mNSecNoContactsMsgResId);
                 mCreateContactButton.setVisibility(View.VISIBLE);
@@ -122,7 +130,7 @@
 
             case ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY:
                 String message = getResources().getString(R.string.upgrade_out_of_memory,
-                        new Object[] { mProviderStatusWatcher.getProviderStatusData() });
+                        new Object[] { providerStatus.data});
                 mMessageView.setText(message);
                 mMessageView.setGravity(Gravity.LEFT);
                 mMessageView.setVisibility(View.VISIBLE);
@@ -155,7 +163,10 @@
                 mListener.onFreeInternalStorageAction();
                 break;
             case R.id.import_failure_retry_button:
-                mProviderStatusWatcher.retryUpgrade();
+                final Context context = getActivity();
+                if (context != null) { // Just in case.
+                    ProviderStatusWatcher.retryUpgrade(context);
+                }
                 break;
         }
     }
@@ -167,9 +178,8 @@
     public void setMessageText(int resId, int secResId) {
         mNoContactsMsgResId = resId;
         mNSecNoContactsMsgResId = secResId;
-        if (mMessageView != null &&
-                mProviderStatusWatcher.getProviderStatus() ==
-                    ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
+        if ((mMessageView != null) && (mProviderStatus != null) &&
+                (mProviderStatus.status == ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS)) {
             if (resId != -1) {
                 mMessageView.setText(mNoContactsMsgResId);
                 mMessageView.setGravity(Gravity.CENTER_HORIZONTAL);
diff --git a/src/com/android/contacts/list/ProviderStatusWatcher.java b/src/com/android/contacts/list/ProviderStatusWatcher.java
index 3ce4b78..ae0b779 100644
--- a/src/com/android/contacts/list/ProviderStatusWatcher.java
+++ b/src/com/android/contacts/list/ProviderStatusWatcher.java
@@ -47,6 +47,19 @@
         public void onProviderStatusChange();
     }
 
+    public static class Status {
+        /** See {@link ProviderStatus#STATUS} */
+        public final int status;
+
+        /** See {@link ProviderStatus#DATA1} */
+        public final String data;
+
+        public Status(int status, String data) {
+            this.status = status;
+            this.data = data;
+        }
+    }
+
     private static final String[] PROJECTION = new String[] {
         ProviderStatus.STATUS,
         ProviderStatus.DATA1
@@ -57,8 +70,6 @@
      */
     private static final int LOAD_WAIT_TIMEOUT_MS = 1000;
 
-    private static final int STATUS_UNKNOWN = -1;
-
     private static ProviderStatusWatcher sInstance;
 
     private final Context mContext;
@@ -71,10 +82,7 @@
     private LoaderTask mLoaderTask;
 
     /** Last known provider status.  This can be changed on a worker thread. */
-    private int mProviderStatus = STATUS_UNKNOWN;
-
-    /** Last known provider status data.  This can be changed on a worker thread. */
-    private String mProviderData;
+    private Status mProviderStatus;
 
     private final ArrayList<ProviderStatusListener> mListeners = Lists.newArrayList();
 
@@ -177,32 +185,18 @@
      * (If {@link ProviderStatus#STATUS_UPGRADING} is returned, the app (should) shows an according
      * message, like "contacts are being updated".)
      */
-    public int getProviderStatus() {
+    public Status getProviderStatus() {
         waitForLoaded();
 
-        if (mProviderStatus == STATUS_UNKNOWN) {
-            return ProviderStatus.STATUS_UPGRADING;
+        if (mProviderStatus == null) {
+            return new Status(ProviderStatus.STATUS_UPGRADING, null);
         }
 
         return mProviderStatus;
     }
 
-    /**
-     * @return last known provider status data.  See also {@link #getProviderStatus()}.
-     */
-    public String getProviderStatusData() {
-        waitForLoaded();
-
-        if (mProviderStatus == STATUS_UNKNOWN) {
-            // STATUS_UPGRADING has no data.
-            return "";
-        }
-
-        return mProviderData;
-    }
-
     private void waitForLoaded() {
-        if (mProviderStatus == STATUS_UNKNOWN) {
+        if (mProviderStatus == null) {
             if (mLoaderTask == null) {
                 // For some reason the loader couldn't load the status.  Let's start it again.
                 startLoading();
@@ -238,8 +232,10 @@
                 if (cursor != null) {
                     try {
                         if (cursor.moveToFirst()) {
-                            mProviderStatus = cursor.getInt(0);
-                            mProviderData = cursor.getString(1);
+                            // Note here we can't just say "Status", as AsyncTask has the "Status"
+                            // enum too.
+                            mProviderStatus = new ProviderStatusWatcher.Status(
+                                    cursor.getInt(0), cursor.getString(1));
                             return true;
                         }
                     } finally {
@@ -291,14 +287,14 @@
     /**
      * Sends a provider status update, which will trigger a retry of database upgrade
      */
-    public void retryUpgrade() {
+    public static void retryUpgrade(final Context context) {
         Log.i(TAG, "retryUpgrade");
         final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
                 ContentValues values = new ContentValues();
                 values.put(ProviderStatus.STATUS, ProviderStatus.STATUS_UPGRADING);
-                mContext.getContentResolver().update(ProviderStatus.CONTENT_URI, values,
+                context.getContentResolver().update(ProviderStatus.CONTENT_URI, values,
                         null, null);
                 return null;
             }
diff --git a/src/com/android/contacts/util/StopWatch.java b/src/com/android/contacts/util/StopWatch.java
new file mode 100644
index 0000000..0657621
--- /dev/null
+++ b/src/com/android/contacts/util/StopWatch.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.contacts.util;
+
+import com.google.android.collect.Lists;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * A {@link StopWatch} records start, laps and stop, and print them to logcat.
+ */
+public class StopWatch {
+
+    private final String mLabel;
+
+    private final ArrayList<Long> mTimes = Lists.newArrayList();
+    private final ArrayList<String> mLapLabels = Lists.newArrayList();
+
+    private StopWatch(String label) {
+        mLabel = label;
+        lap("");
+    }
+
+    /**
+     * Create a new instance and start it.
+     */
+    public static StopWatch start(String label) {
+        return new StopWatch(label);
+    }
+
+    /**
+     * Record a lap.
+     */
+    public void lap(String lapLabel) {
+        mTimes.add(System.currentTimeMillis());
+        mLapLabels.add(lapLabel);
+    }
+
+    /**
+     * Stop it and log the result, if the total time >= {@code timeThresholdToLog}.
+     */
+    public void stopAndLog(String TAG, int timeThresholdToLog) {
+
+        lap("");
+
+        final long start = mTimes.get(0);
+        final long stop = mTimes.get(mTimes.size() - 1);
+
+        final long total = stop - start;
+        if (total < timeThresholdToLog) return;
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append(mLabel);
+        sb.append(",");
+        sb.append(total);
+        sb.append(": ");
+
+        long last = start;
+        for (int i = 1; i < mTimes.size(); i++) {
+            final long current = mTimes.get(i);
+            sb.append(mLapLabels.get(i));
+            sb.append(",");
+            sb.append((current - last));
+            sb.append(" ");
+            last = current;
+        }
+        Log.v(TAG, sb.toString());
+    }
+}