1c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki/*
2c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * Copyright (C) 2012 The Android Open Source Project
3c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki *
4c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * Licensed under the Apache License, Version 2.0 (the "License");
5c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * you may not use this file except in compliance with the License.
6c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * You may obtain a copy of the License at
7c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki *
8c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki *      http://www.apache.org/licenses/LICENSE-2.0
9c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki *
10c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * Unless required by applicable law or agreed to in writing, software
11c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * distributed under the License is distributed on an "AS IS" BASIS,
12c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * See the License for the specific language governing permissions and
14c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * limitations under the License.
15c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki */
16c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukipackage com.android.contacts.list;
17c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
18c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukiimport android.content.ContentValues;
19c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukiimport android.content.Context;
20c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukiimport android.database.ContentObserver;
21c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukiimport android.database.Cursor;
22c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukiimport android.net.Uri;
23c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukiimport android.os.AsyncTask;
24c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukiimport android.os.Handler;
25c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukiimport android.provider.ContactsContract.ProviderStatus;
26c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukiimport android.util.Log;
27c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
28aac0e66fb100b329d6010637998849048efadca9Wenyi Wangimport com.android.contacts.compat.ProviderStatusCompat;
292795a88a8e89a44f7dc4334cc2ae00bbd19dc2c1Wenyi Wang
30e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.google.common.collect.Lists;
31e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng
32c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukiimport java.util.ArrayList;
33c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
34c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki/**
35c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * A singleton that keeps track of the last known provider status.
36c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki *
37c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * All methods must be called on the UI thread unless noted otherwise.
38c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki *
39c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki * All members must be set on the UI thread unless noted otherwise.
40c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki */
41c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onukipublic class ProviderStatusWatcher extends ContentObserver {
42c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private static final String TAG = "ProviderStatusWatcher";
43c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private static final boolean DEBUG = false;
44c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
45c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    /**
46c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * Callback interface invoked when the provider status changes.
47c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     */
48c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    public interface ProviderStatusListener {
49c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        public void onProviderStatusChange();
50c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
51c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
52c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private static final String[] PROJECTION = new String[] {
53c3d202ccbaf93ddd8291672027e59f549c32eee3Brian Attwell        ProviderStatus.STATUS
54c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    };
55c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
56c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    /**
57c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * We'll wait for this amount of time on the UI thread if the load hasn't finished.
58c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     */
59c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private static final int LOAD_WAIT_TIMEOUT_MS = 1000;
60c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
61c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private static ProviderStatusWatcher sInstance;
62c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
63c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private final Context mContext;
64c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private final Handler mHandler = new Handler();
65c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
66c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private final Object mSignal = new Object();
67c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
68c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private int mStartRequestedCount;
69c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
70c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private LoaderTask mLoaderTask;
71c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
72c3d202ccbaf93ddd8291672027e59f549c32eee3Brian Attwell    /** Last known provider status.  This can be changed on a worker thread.
73c3d202ccbaf93ddd8291672027e59f549c32eee3Brian Attwell     *  See {@link ProviderStatus#STATUS} */
74c3d202ccbaf93ddd8291672027e59f549c32eee3Brian Attwell    private Integer mProviderStatus;
75c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
76c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private final ArrayList<ProviderStatusListener> mListeners = Lists.newArrayList();
77c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
78c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private final Runnable mStartLoadingRunnable = new Runnable() {
79c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        @Override
80c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        public void run() {
81c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            startLoading();
82c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
83c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    };
84c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
85c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    /**
86c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * Returns the singleton instance.
87c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     */
88c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    public synchronized static ProviderStatusWatcher getInstance(Context context) {
89c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        if (sInstance == null) {
90c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            sInstance = new ProviderStatusWatcher(context);
91c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
92c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        return sInstance;
93c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
94c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
95c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private ProviderStatusWatcher(Context context) {
96c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        super(null);
97c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        mContext = context;
98c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
99c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
100c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    /** Add a listener. */
101c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    public void addListener(ProviderStatusListener listener) {
102c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        mListeners.add(listener);
103c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
104c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
105c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    /** Remove a listener */
106c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    public void removeListener(ProviderStatusListener listener) {
107c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        mListeners.remove(listener);
108c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
109c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
110c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private void notifyListeners() {
111c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        if (DEBUG) {
112c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            Log.d(TAG, "notifyListeners: " + mListeners.size());
113c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
114c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        if (isStarted()) {
115c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            for (ProviderStatusListener listener : mListeners) {
116c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                listener.onProviderStatusChange();
117c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            }
118c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
119c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
120c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
121c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private boolean isStarted() {
122c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        return mStartRequestedCount > 0;
123c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
124c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
125c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    /**
126c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * Starts watching the provider status.  {@link #start()} and {@link #stop()} calls can be
127c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * nested.
128c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     */
129c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    public void start() {
130c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        if (++mStartRequestedCount == 1) {
131c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            mContext.getContentResolver()
132c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                .registerContentObserver(ProviderStatus.CONTENT_URI, false, this);
133c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            startLoading();
134c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
135c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            if (DEBUG) {
136c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                Log.d(TAG, "Start observing");
137c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            }
138c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
139c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
140c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
141c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    /**
142c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * Stops watching the provider status.
143c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     */
144c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    public void stop() {
145c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        if (!isStarted()) {
146c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            Log.e(TAG, "Already stopped");
147c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            return;
148c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
149c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        if (--mStartRequestedCount == 0) {
150c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
151c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            mHandler.removeCallbacks(mStartLoadingRunnable);
152c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
153c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            mContext.getContentResolver().unregisterContentObserver(this);
154c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            if (DEBUG) {
155c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                Log.d(TAG, "Stop observing");
156c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            }
157c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
158c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
159c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
160c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    /**
161c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * @return last known provider status.
162c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     *
163c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * If this method is called when we haven't started the status query or the query is still in
164c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * progress, it will start a query in a worker thread if necessary, and *wait for the result*.
165c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     *
166c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * This means this method is essentially a blocking {@link ProviderStatus#CONTENT_URI} query.
167c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * This URI is not backed by the file system, so is usually fast enough to perform on the main
168c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * thread, but in extreme cases (when the system takes a while to bring up the contacts
169c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * provider?) this may still cause ANRs.
170c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     *
171c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * In order to avoid that, if we can't load the status within {@link #LOAD_WAIT_TIMEOUT_MS},
1722795a88a8e89a44f7dc4334cc2ae00bbd19dc2c1Wenyi Wang     * we'll give up and just returns {@link ProviderStatusCompat#STATUS_BUSY} in order to unblock
173c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * the UI thread.  The actual result will be delivered later via {@link ProviderStatusListener}.
1742795a88a8e89a44f7dc4334cc2ae00bbd19dc2c1Wenyi Wang     * (If {@link ProviderStatusCompat#STATUS_BUSY} is returned, the app (should) shows an according
175c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * message, like "contacts are being updated".)
176c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     */
177c3d202ccbaf93ddd8291672027e59f549c32eee3Brian Attwell    public int getProviderStatus() {
178c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        waitForLoaded();
179c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
180d8fa716e5428791f79f54437087df3005cc907bfMakoto Onuki        if (mProviderStatus == null) {
1812795a88a8e89a44f7dc4334cc2ae00bbd19dc2c1Wenyi Wang            return ProviderStatusCompat.STATUS_BUSY;
182c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
183c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
184c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        return mProviderStatus;
185c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
186c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
187c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private void waitForLoaded() {
188d8fa716e5428791f79f54437087df3005cc907bfMakoto Onuki        if (mProviderStatus == null) {
189c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            if (mLoaderTask == null) {
190c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                // For some reason the loader couldn't load the status.  Let's start it again.
191c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                startLoading();
192c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            }
193c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            synchronized (mSignal) {
194c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                try {
195c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                    mSignal.wait(LOAD_WAIT_TIMEOUT_MS);
196c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                } catch (InterruptedException ignore) {
197c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                }
198c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            }
199c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
200c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
201c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
202c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private void startLoading() {
203c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        if (mLoaderTask != null) {
204c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            return; // Task already running.
205c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
206c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
207c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        if (DEBUG) {
208c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            Log.d(TAG, "Start loading");
209c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
210c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
211c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        mLoaderTask = new LoaderTask();
212c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        mLoaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
213c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
214c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
215c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    private class LoaderTask extends AsyncTask<Void, Void, Boolean> {
216c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        @Override
217c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        protected Boolean doInBackground(Void... params) {
218c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            try {
219c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                Cursor cursor = mContext.getContentResolver().query(ProviderStatus.CONTENT_URI,
220c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                        PROJECTION, null, null, null);
221c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                if (cursor != null) {
222c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                    try {
223c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                        if (cursor.moveToFirst()) {
224d8fa716e5428791f79f54437087df3005cc907bfMakoto Onuki                            // Note here we can't just say "Status", as AsyncTask has the "Status"
225d8fa716e5428791f79f54437087df3005cc907bfMakoto Onuki                            // enum too.
226c3d202ccbaf93ddd8291672027e59f549c32eee3Brian Attwell                            mProviderStatus = cursor.getInt(0);
227c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                            return true;
228c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                        }
229c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                    } finally {
230c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                        cursor.close();
231c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                    }
232c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                }
233c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                return false;
234c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            } finally {
235c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                synchronized (mSignal) {
236c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                    mSignal.notifyAll();
237c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                }
238c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            }
239c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
240c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
241c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        @Override
242c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        protected void onCancelled(Boolean result) {
243c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            cleanUp();
244c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
245c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
246c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        @Override
247c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        protected void onPostExecute(Boolean loaded) {
248c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            cleanUp();
249c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            if (loaded != null && loaded) {
250c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki                notifyListeners();
251c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            }
252c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
253c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
254c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        private void cleanUp() {
255c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki            mLoaderTask = null;
256c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        }
257c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
258c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
259c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    /**
260c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * Called when provider status may has changed.
261c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     *
262c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     * This method will be called on a worker thread by the framework.
263c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki     */
264c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    @Override
265c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    public void onChange(boolean selfChange, Uri uri) {
266c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        if (!ProviderStatus.CONTENT_URI.equals(uri)) return;
267c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
268c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        // Provider status change is rare, so okay to log.
269c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        Log.i(TAG, "Provider status changed.");
270c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki
271c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        mHandler.removeCallbacks(mStartLoadingRunnable); // Remove one in the queue, if any.
272c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki        mHandler.post(mStartLoadingRunnable);
273c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki    }
274c2bd6138e19fdcf734843eb55c83d6ffe00e91daMakoto Onuki}
275