blob: ad8e3696391e0040302c7df54521bed0e9882c1f [file] [log] [blame]
/* Copyright (C) 2010 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.exchange.provider;
import com.android.email.EmailAddressAdapter;
import com.android.email.R;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.HostAuth;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.net.Uri;
import android.util.Log;
import android.view.View;
import android.widget.AutoCompleteTextView;
import android.widget.TextView;
public class GalEmailAddressAdapter extends EmailAddressAdapter {
private static final String TAG = "GalAdapter";
// Don't run GAL query until there are 3 characters typed
private static final int MINIMUM_GAL_CONSTRAINT_LENGTH = 3;
// Tag in the placeholder
public static final String SEARCHING_TAG = "_SEARCHING_";
Activity mActivity;
AutoCompleteTextView mAutoCompleteTextView;
Account mAccount;
boolean mAccountHasGal;
public GalEmailAddressAdapter(Activity activity, AutoCompleteTextView actv) {
super(activity);
mActivity = activity;
mAutoCompleteTextView = actv;
mAccount = null;
mAccountHasGal = false;
}
/**
* Set the account ID when known. Not used for generic contacts lookup; Use when
* linking lookup to specific account.
*/
@Override
public void setAccount(Account account) {
mAccount = account;
mAccountHasGal = false;
}
/**
* TODO: This should be the general purpose placeholder
* TODO: String (possibly with account name)
*/
public static Cursor getProgressCursor() {
MatrixCursor c = new MatrixCursor(ExchangeProvider.GAL_PROJECTION);
c.newRow().add(ExchangeProvider.GAL_START_ID).add("Searching ").add(SEARCHING_TAG);
return c;
}
/**
* Sniff the provided account and if it's EAS, record "mAccounthHasGal". If not,
* clear mAccount so we just ignore it.
*/
private void checkGalAccount(Account account) {
HostAuth ha = HostAuth.restoreHostAuthWithId(mActivity, account.mHostAuthKeyRecv);
if (ha != null) {
if ("eas".equalsIgnoreCase(ha.mProtocol)) {
mAccountHasGal = true;
return;
}
}
// for any reason, we could not identify a GAL account, so clear mAccount
// and we'll never check this again
mAccount = null;
mAccountHasGal = false;
}
@Override
public Cursor runQueryOnBackgroundThread(final CharSequence constraint) {
// One time (and not in the UI thread) - check the account and see if it support GAL
// If not, clear it so we never bother again
if (mAccount != null && mAccountHasGal == false) {
checkGalAccount(mAccount);
}
// Get the cursor from ContactsProvider
Cursor initialCursor = super.runQueryOnBackgroundThread(constraint);
// If we don't have an account or we don't have a constraint that's long enough, just return
if (mAccountHasGal &&
constraint != null && constraint.length() >= MINIMUM_GAL_CONSTRAINT_LENGTH) {
// Note that the "progress" line could (should) be implemented as a header rather than
// as a row in the list. The current implementation is placeholder.
MergeCursor mc =
new MergeCursor(new Cursor[] {initialCursor, getProgressCursor()});
// We need another copy of the original cursor for our MergeCursor
// because changeCursor closes the original!
// TODO: Avoid this - getting the contacts cursor twice is bad news.
// We're probably not handling the filter UI properly.
final Cursor contactsCursor = super.runQueryOnBackgroundThread(constraint);
new Thread(new Runnable() {
public void run() {
// Uri format is account/constraint/timestamp
Uri galUri =
ExchangeProvider.GAL_URI.buildUpon()
.appendPath(Long.toString(mAccount.mId))
.appendPath(constraint.toString())
.appendPath(((Long)System.currentTimeMillis()).toString()).build();
Log.d(TAG, "Query: " + galUri);
// Use ExchangeProvider to get the results of the GAL query
final Cursor galCursor =
mContentResolver.query(galUri, ExchangeProvider.GAL_PROJECTION,
null, null, null);
// A null cursor means that our query has been superceded by a later one
if (galCursor == null) return;
// We need to change cursors on the UI thread
mActivity.runOnUiThread(new Runnable() {
public void run() {
// Create a new cursor putting together local and GAL results and
// use it in the adapter
MergeCursor mergeCursor =
new MergeCursor(new Cursor[] {contactsCursor, galCursor});
changeCursor(mergeCursor);
// Call AutoCompleteTextView's onFilterComplete method with count
mAutoCompleteTextView.onFilterComplete(mergeCursor.getCount());
}});
}}).start();
return mc;
}
// Return right away with the ContactsProvider result
return initialCursor;
}
// TODO - we cannot assume that contacts ID's will not overlap with GAL_START_ID
// need to do this in a different way, based on more direct knowledge of our cursor
@Override
public final void bindView(View view, Context context, Cursor cursor) {
if (cursor.getLong(ID_INDEX) == ExchangeProvider.GAL_START_ID) {
((TextView)view.findViewById(R.id.account)).setText(cursor.getString(NAME_INDEX));
view.findViewById(R.id.status_divider).setVisibility(View.VISIBLE);
view.findViewById(R.id.address).setVisibility(View.GONE);
view.findViewById(R.id.progress).setVisibility(
cursor.getString(DATA_INDEX).equals(SEARCHING_TAG) ?
View.VISIBLE : View.GONE);
} else {
((TextView)view.findViewById(R.id.text1)).setText(cursor.getString(NAME_INDEX));
((TextView)view.findViewById(R.id.text2)).setText(cursor.getString(DATA_INDEX));
view.findViewById(R.id.address).setVisibility(View.VISIBLE);
view.findViewById(R.id.status_divider).setVisibility(View.GONE);
}
}
}