blob: 412a3c6dc30187cb7286a6857f5cd2b3a9b2ed20 [file] [log] [blame]
/*
* Copyright (C) 2013 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.example.android.apis.content;
import android.app.Activity;
import android.app.LoaderManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.Loader;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
/**
* Demonstrates selecting contacts that have changed since a certain time.
*/
public class ChangedContacts extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String CLASS = ChangedContacts.class.getSimpleName();
private static final String PREF_KEY_CHANGE = "timestamp_change";
private static final String PREF_KEY_DELETE = "timestamp_delete";
private static final int ID_CHANGE_LOADER = 1;
private static final int ID_DELETE_LOADER = 2;
/**
* To see this in action, "clear data" for the contacts storage app in the system settings.
* Then come into this app and hit any of the delta buttons. This will cause the contacts
* database to be re-created.
*/
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Toast toast = Toast.makeText(context, "Contacts database created.", Toast.LENGTH_SHORT);
toast.show();
}
};
private DeleteAdapter mDeleteAdapter;
private ChangeAdapter mChangeAdapter;
private long mSearchTime;
private TextView mDisplayView;
private ListView mList;
private Button mDeleteButton;
private Button mChangeButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDeleteAdapter = new DeleteAdapter(this, null, 0);
mChangeAdapter = new ChangeAdapter(this, null, 0);
LinearLayout main = new LinearLayout(this);
main.setOrientation(LinearLayout.VERTICAL);
mChangeButton = new Button(this);
mChangeButton.setText("Changed since " + getLastTimestamp(0, PREF_KEY_CHANGE));
mChangeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeClick();
}
});
mDeleteButton = new Button(this);
mDeleteButton.setText("Deleted since " + getLastTimestamp(0, PREF_KEY_DELETE));
mDeleteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
deleteClick();
}
});
main.addView(mChangeButton);
main.addView(mDeleteButton);
mDisplayView = new TextView(this);
mDisplayView.setPadding(5, 5, 5, 5);
main.addView(mDisplayView);
mList = new ListView(this);
mList.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
main.addView(mList);
setContentView(main);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(ContactsContract.Intents.CONTACTS_DATABASE_CREATED);
registerReceiver(mReceiver, filter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mReceiver);
}
private void changeClick() {
mChangeAdapter.swapCursor(null);
LoaderManager manager = getLoaderManager();
manager.destroyLoader(ID_DELETE_LOADER);
manager.restartLoader(ID_CHANGE_LOADER, null, this);
}
private void deleteClick() {
mChangeAdapter.swapCursor(null);
LoaderManager manager = getLoaderManager();
manager.destroyLoader(ID_CHANGE_LOADER);
manager.restartLoader(ID_DELETE_LOADER, null, this);
}
private void saveLastTimestamp(long time, String key) {
SharedPreferences pref = getSharedPreferences(CLASS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
editor.putLong(key, time);
editor.commit();
}
private long getLastTimestamp(long time, String key) {
SharedPreferences pref = getSharedPreferences(CLASS, Context.MODE_PRIVATE);
return pref.getLong(key, time);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch(id) {
case ID_CHANGE_LOADER:
return getChangeLoader();
case ID_DELETE_LOADER:
return getDeleteLoader();
}
return null;
}
private CursorLoader getChangeLoader() {
String[] projection = new String[]{
ContactsContract.Data._ID,
ContactsContract.Data.CONTACT_ID,
ContactsContract.Data.DISPLAY_NAME,
ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP
};
mSearchTime = getLastTimestamp(0, PREF_KEY_CHANGE);
String selection = ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
String[] bindArgs = new String[]{mSearchTime + ""};
return new CursorLoader(this, ContactsContract.Data.CONTENT_URI, projection,
selection, bindArgs, ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP
+ " desc, " + ContactsContract.Data.CONTACT_ID + " desc");
}
private CursorLoader getDeleteLoader() {
String[] projection = new String[]{
ContactsContract.DeletedContacts.CONTACT_ID,
ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP
};
mSearchTime = getLastTimestamp(0, PREF_KEY_DELETE);
String selection = ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP + " > ?";
String[] bindArgs = new String[]{mSearchTime + ""};
return new CursorLoader(this, ContactsContract.DeletedContacts.CONTENT_URI, projection,
selection, bindArgs, ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP +
" desc");
}
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor data) {
long timestamp = 0;
switch (cursorLoader.getId()) {
case ID_CHANGE_LOADER:
mDisplayView.setText(data.getCount() + " change(s) since " + mSearchTime);
mList.setAdapter(mChangeAdapter);
mChangeAdapter.swapCursor(data);
// Save the largest timestamp returned. Only need the first one due to the sort
// order.
if (data.moveToNext()) {
timestamp = data.getLong(data.getColumnIndex(
ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP));
data.moveToPrevious();
}
if (timestamp > 0) {
saveLastTimestamp(timestamp, PREF_KEY_CHANGE);
mChangeButton.setText("Changed since " + timestamp);
}
break;
case ID_DELETE_LOADER:
mDisplayView.setText(data.getCount() + " delete(s) since " + mSearchTime);
mList.setAdapter(mDeleteAdapter);
mDeleteAdapter.swapCursor(new DeleteCursorWrapper(data));
if (data.moveToNext()) {
timestamp = data.getLong(data.getColumnIndex(
ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP));
data.moveToPrevious();
}
if (timestamp > 0) {
saveLastTimestamp(timestamp, PREF_KEY_DELETE);
mDeleteButton.setText("Deleted since " + timestamp);
}
break;
}
}
@Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
mDisplayView.setText("");
switch (cursorLoader.getId()) {
case ID_CHANGE_LOADER:
mChangeAdapter.swapCursor(null);
break;
case ID_DELETE_LOADER:
mDeleteAdapter.swapCursor(null);
break;
}
}
private class DeleteCursorWrapper extends CursorWrapper {
/**
* Creates a cursor wrapper.
*
* @param cursor The underlying cursor to wrap.
*/
public DeleteCursorWrapper(Cursor cursor) {
super(cursor);
}
@Override
public int getColumnIndexOrThrow(String columnName) {
if (columnName.equals("_id")) {
return super.getColumnIndex(ContactsContract.DeletedContacts.CONTACT_ID);
}
return super.getColumnIndex(columnName);
}
}
private static class DeleteAdapter extends CursorAdapter {
private Context mContext;
public DeleteAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
this.mContext = context;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LinearLayout item = new LinearLayout(mContext);
item.addView(buildText(context));
item.addView(buildText(context));
return item;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
LinearLayout item = (LinearLayout) view;
String id = cursor.getString(cursor.getColumnIndex(
ContactsContract.DeletedContacts.CONTACT_ID));
String timestamp = cursor.getString(cursor.getColumnIndex(
ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP));
setText(item.getChildAt(0), id);
setText(item.getChildAt(1), timestamp);
}
}
private static class ChangeAdapter extends CursorAdapter {
private Context mContext;
public ChangeAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mContext = context;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LinearLayout item = new LinearLayout(mContext);
item.addView(buildText(context));
item.addView(buildText(context));
item.addView(buildText(context));
return item;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
LinearLayout item = (LinearLayout) view;
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.CONTACT_ID));
String name = cursor.getString(cursor.getColumnIndex(
ContactsContract.Data.DISPLAY_NAME));
String timestamp = cursor.getString(cursor.getColumnIndex(
ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP));
setText(item.getChildAt(0), id);
setText(item.getChildAt(1), name);
setText(item.getChildAt(2), timestamp);
}
}
private static void setText(View view, String value) {
TextView text = (TextView) view;
text.setText(value);
}
private static TextView buildText(Context context) {
TextView view = new TextView(context);
view.setPadding(3, 3, 3, 3);
return view;
}
}