Add a dummy loader to calllog to defer other fragments.

- Also introduced debug flags to control Loder/Fragment debug logs, and removed
constants that used to controll them.

Bug 5547725

Change-Id: I5b523e6824edc4848ab82cbf13420d3db822c562
diff --git a/src/com/android/contacts/ContactsApplication.java b/src/com/android/contacts/ContactsApplication.java
index f806c1d..16f9e57 100644
--- a/src/com/android/contacts/ContactsApplication.java
+++ b/src/com/android/contacts/ContactsApplication.java
@@ -23,6 +23,8 @@
 import com.google.common.annotations.VisibleForTesting;
 
 import android.app.Application;
+import android.app.FragmentManager;
+import android.app.LoaderManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
@@ -31,6 +33,9 @@
 import android.util.Log;
 
 public final class ContactsApplication extends Application {
+    private static final boolean ENABLE_LOADER_LOG = false; // Don't submit with true
+    private static final boolean ENABLE_FRAGMENT_LOG = false; // Don't submit with true
+
     private static InjectedServices sInjectedServices;
     private AccountTypeManager mAccountTypeManager;
     private ContactPhotoManager mContactPhotoManager;
@@ -118,6 +123,8 @@
         Context context = getApplicationContext();
         PreferenceManager.getDefaultSharedPreferences(context);
         AccountTypeManager.getInstance(context);
+        if (ENABLE_FRAGMENT_LOG) FragmentManager.enableDebugLogging(true);
+        if (ENABLE_LOADER_LOG) LoaderManager.enableDebugLogging(true);
 
         if (Log.isLoggable(Constants.STRICT_MODE_TAG, Log.DEBUG)) {
             StrictMode.setThreadPolicy(
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index 96136a2..88aab4d 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -20,6 +20,7 @@
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
 import com.android.contacts.activities.DialtactsActivity.ViewPagerVisibilityListener;
+import com.android.contacts.util.EmptyLoader;
 import com.android.contacts.voicemail.VoicemailStatusHelper;
 import com.android.contacts.voicemail.VoicemailStatusHelper.StatusMessage;
 import com.android.contacts.voicemail.VoicemailStatusHelperImpl;
@@ -59,6 +60,11 @@
         CallLogQueryHandler.Listener, CallLogAdapter.CallFetcher {
     private static final String TAG = "CallLogFragment";
 
+    /**
+     * ID of the empty loader to defer other fragments.
+     */
+    private static final int EMPTY_LOADER_ID = 0;
+
     private CallLogAdapter mAdapter;
     private CallLogQueryHandler mCallLogQueryHandler;
     private boolean mScrollToTop;
@@ -75,6 +81,10 @@
     private TextView mStatusMessageAction;
     private KeyguardManager mKeyguardManager;
 
+    private boolean mEmptyLoaderRunning;
+    private boolean mCallLogFetched;
+    private boolean mVoicemailStatusFetched;
+
     @Override
     public void onCreate(Bundle state) {
         super.onCreate(state);
@@ -103,6 +113,8 @@
             listView.smoothScrollToPosition(0);
             mScrollToTop = false;
         }
+        mCallLogFetched = true;
+        destroyEmptyLoaderIfAllDataFetched();
     }
 
     /**
@@ -118,6 +130,15 @@
         int activeSources = mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor);
         setVoicemailSourcesAvailable(activeSources != 0);
         MoreCloseables.closeQuietly(statusCursor);
+        mVoicemailStatusFetched = true;
+        destroyEmptyLoaderIfAllDataFetched();
+    }
+
+    private void destroyEmptyLoaderIfAllDataFetched() {
+        if (mCallLogFetched && mVoicemailStatusFetched && mEmptyLoaderRunning) {
+            mEmptyLoaderRunning = false;
+            getLoaderManager().destroyLoader(EMPTY_LOADER_ID);
+        }
     }
 
     /** Sets whether there are any voicemail sources available in the platform. */
@@ -155,6 +176,12 @@
     @Override
     public void onStart() {
         mScrollToTop = true;
+
+        // Start the empty loader now to defer other fragments.  We destroy it when both calllog
+        // and the voicemail status are fetched.
+        getLoaderManager().initLoader(EMPTY_LOADER_ID, null,
+                new EmptyLoader.Callback(getActivity()));
+        mEmptyLoaderRunning = true;
         super.onStart();
     }
 
diff --git a/src/com/android/contacts/util/Constants.java b/src/com/android/contacts/util/Constants.java
index 3a43c40..eb68f5a 100644
--- a/src/com/android/contacts/util/Constants.java
+++ b/src/com/android/contacts/util/Constants.java
@@ -32,18 +32,6 @@
     public static final String PERFORMANCE_TAG = "ContactsPerf";
 
     /**
-     * Log tag for enabling/disabling LoaderManager log.
-     * To enable: adb shell setprop log.tag.ContactsLoaderManager DEBUG
-     */
-    public static final String LOADER_MANAGER_TAG = "ContactsLoaderManager";
-
-    /**
-     * Log tag for enabling/disabling FragmentManager log.
-     * To enable: adb shell setprop log.tag.ContactsFragmentManager DEBUG
-     */
-    public static final String FRAGMENT_MANAGER_TAG = "ContactsFragmentManager";
-
-    /**
      * Log tag for enabling/disabling StrictMode violation log.
      * To enable: adb shell setprop log.tag.ContactsStrictMode DEBUG
      */
diff --git a/src/com/android/contacts/util/EmptyLoader.java b/src/com/android/contacts/util/EmptyLoader.java
new file mode 100644
index 0000000..97478bd
--- /dev/null
+++ b/src/com/android/contacts/util/EmptyLoader.java
@@ -0,0 +1,60 @@
+/*
+ * 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 com.android.contacts.util;
+
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.Loader;
+import android.os.Bundle;
+
+/**
+ * A {@link Loader} only used to make use of the {@link android.app.Fragment#setStartDeferred}
+ * feature from an old-style fragment which doesn't use {@link Loader}s to load data.
+ *
+ * This loader never delivers results.  A caller fragment must destroy it when deferred fragments
+ * should be started.
+ */
+public class EmptyLoader extends Loader<Object> {
+    public EmptyLoader(Context context) {
+        super(context);
+    }
+
+    /**
+     * {@link LoaderCallbacks} which just generates {@link EmptyLoader}.  {@link #onLoadFinished}
+     * and {@link #onLoaderReset} are no-op.
+     */
+    public static class Callback implements LoaderCallbacks<Object> {
+        private final Context mContext;
+
+        public Callback(Context context) {
+            mContext = context.getApplicationContext();
+        }
+
+        @Override
+        public Loader<Object> onCreateLoader(int id, Bundle args) {
+            return new EmptyLoader(mContext);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Object> loader, Object data) {
+        }
+
+        @Override
+        public void onLoaderReset(Loader<Object> loader) {
+        }
+    }
+}