Use the form-based editor in a fragment
Change-Id: I461bc1842f5c9cf1d97f4ef285eb5d88c850134f
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index 1007410..2bac145 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -25,8 +25,8 @@
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
-import android.view.Menu;
import android.view.MenuItem;
+import android.widget.Toast;
public class ContactEditorActivity extends Activity {
private static final String TAG = "ContactEditorActivity";
@@ -41,7 +41,8 @@
mFragment = (ContactEditorFragment) findFragmentById(R.id.contact_editor_fragment);
mFragment.setListener(mFragmentListener);
- mFragment.loadUri(getIntent().getData());
+ mFragment.load(getIntent().getAction(), getIntent().getData(),
+ getIntent().resolveType(getContentResolver()), getIntent().getExtras());
Log.i(TAG, getIntent().getData().toString());
}
@@ -77,22 +78,44 @@
private final ContactEditorFragment.Listener mFragmentListener =
new ContactEditorFragment.Listener() {
- public void onContactNotFound() {
- // TODO: Show error
- finish();
+ @Override
+ public void closeAfterDelete() {
+ Toast.makeText(ContactEditorActivity.this, "closeAfterDelete",
+ Toast.LENGTH_LONG).show();
}
- public void onError() {
- // TODO: Show error message
- finish();
+ @Override
+ public void closeAfterRevert() {
+ Toast.makeText(ContactEditorActivity.this, "closeAfterRevert",
+ Toast.LENGTH_LONG).show();
}
- public void onEditorRequested(Intent intent) {
- startActivity(intent);
+ @Override
+ public void closeAfterSaving(int resultCode, Intent resultIntent) {
+ Toast.makeText(ContactEditorActivity.this, "closeAfterSaving",
+ Toast.LENGTH_LONG).show();
}
- public void onDialogRequested(int id, Bundle bundle) {
- showDialog(id, bundle);
+ @Override
+ public void closeAfterSplit() {
+ Toast.makeText(ContactEditorActivity.this, "closeAfterSplit", Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void closeBecauseAccountSelectorAborted() {
+ Toast.makeText(ContactEditorActivity.this, "closeBecauseAccountSelectorAborted",
+ Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void closeBecauseContactNotFound() {
+ Toast.makeText(ContactEditorActivity.this, "closeBecauseContactNotFound",
+ Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void setTitleTo(int resourceId) {
+ Toast.makeText(ContactEditorActivity.this, "setTitleTo", Toast.LENGTH_LONG).show();
}
};
}
diff --git a/src/com/android/contacts/activities/TwoPaneActivity.java b/src/com/android/contacts/activities/TwoPaneActivity.java
index 3f8a139..9d8bd47 100644
--- a/src/com/android/contacts/activities/TwoPaneActivity.java
+++ b/src/com/android/contacts/activities/TwoPaneActivity.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
import android.text.TextUtils;
import android.widget.Toast;
@@ -169,7 +170,8 @@
public void onEditRequested(Uri contactLookupUri) {
setupContactEditorFragment();
- mEditorFragment.loadUri(contactLookupUri);
+ mEditorFragment.load(Intent.ACTION_EDIT, contactLookupUri, Contacts.CONTENT_ITEM_TYPE,
+ new Bundle());
}
public void onItemClicked(Intent intent) {
@@ -182,20 +184,41 @@
}
private class EditorFragmentListener implements ContactEditorFragment.Listener {
- public void onContactNotFound() {
- Toast.makeText(TwoPaneActivity.this, "onContactNotFound", Toast.LENGTH_LONG).show();
+ @Override
+ public void closeAfterDelete() {
+ Toast.makeText(TwoPaneActivity.this, "closeAfterDelete", Toast.LENGTH_LONG).show();
}
- public void onDialogRequested(int id, Bundle bundle) {
- Toast.makeText(TwoPaneActivity.this, "onDialogRequested", Toast.LENGTH_LONG).show();
+ @Override
+ public void closeAfterRevert() {
+ Toast.makeText(TwoPaneActivity.this, "closeAfterRevert", Toast.LENGTH_LONG).show();
}
- public void onEditorRequested(Intent intent) {
- Toast.makeText(TwoPaneActivity.this, "onEditorRequested", Toast.LENGTH_LONG).show();
+ @Override
+ public void closeAfterSaving(int resultCode, Intent resultIntent) {
+ Toast.makeText(TwoPaneActivity.this, "closeAfterSaving", Toast.LENGTH_LONG).show();
}
- public void onError() {
- Toast.makeText(TwoPaneActivity.this, "onError", Toast.LENGTH_LONG).show();
+ @Override
+ public void closeAfterSplit() {
+ Toast.makeText(TwoPaneActivity.this, "closeAfterSplit", Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void closeBecauseAccountSelectorAborted() {
+ Toast.makeText(TwoPaneActivity.this, "closeBecauseAccountSelectorAborted",
+ Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void closeBecauseContactNotFound() {
+ Toast.makeText(TwoPaneActivity.this, "closeBecauseContactNotFound",
+ Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void setTitleTo(int resourceId) {
+ Toast.makeText(TwoPaneActivity.this, "setTitleTo", Toast.LENGTH_LONG).show();
}
}
}
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
deleted file mode 100644
index 7b61e19..0000000
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ /dev/null
@@ -1,1414 +0,0 @@
-/*
- * Copyright (C) 2009 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.ui;
-
-import com.android.contacts.ContactsSearchManager;
-import com.android.contacts.ContactsUtils;
-import com.android.contacts.JoinContactActivity;
-import com.android.contacts.R;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.Editor;
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.EntitySet;
-import com.android.contacts.model.GoogleSource;
-import com.android.contacts.model.Sources;
-import com.android.contacts.model.ContactsSource.EditType;
-import com.android.contacts.model.Editor.EditorListener;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.ui.widget.BaseContactEditorView;
-import com.android.contacts.ui.widget.PhotoEditorView;
-import com.android.contacts.util.DialogManager;
-import com.android.contacts.util.EmptyService;
-import com.android.contacts.util.WeakAsyncTask;
-
-import android.accounts.Account;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.ActivityNotFoundException;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Entity;
-import android.content.Intent;
-import android.content.OperationApplicationException;
-import android.content.ContentProviderOperation.Builder;
-import android.content.DialogInterface.OnDismissListener;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.media.MediaScannerConnection;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.MediaStore;
-import android.provider.ContactsContract.AggregationExceptions;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts.Data;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.io.File;
-import java.lang.ref.WeakReference;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-
-/**
- * Activity for editing or inserting a contact.
- */
-public final class EditContactActivity extends Activity
- implements View.OnClickListener, Comparator<EntityDelta>,
- DialogManager.DialogShowingViewActivity {
-
- private static final String TAG = "EditContactActivity";
-
- /** The launch code when picking a photo and the raw data is returned */
- private static final int PHOTO_PICKED_WITH_DATA = 3021;
-
- /** The launch code when a contact to join with is returned */
- private static final int REQUEST_JOIN_CONTACT = 3022;
-
- /** The launch code when taking a picture */
- private static final int CAMERA_WITH_DATA = 3023;
-
- private static final String KEY_EDIT_STATE = "state";
- private static final String KEY_RAW_CONTACT_ID_REQUESTING_PHOTO = "photorequester";
- private static final String KEY_VIEW_ID_GENERATOR = "viewidgenerator";
- private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile";
- private static final String KEY_QUERY_SELECTION = "queryselection";
- private static final String KEY_CONTACT_ID_FOR_JOIN = "contactidforjoin";
-
- /** The result code when view activity should close after edit returns */
- public static final int RESULT_CLOSE_VIEW_ACTIVITY = 777;
-
- public static final int SAVE_MODE_DEFAULT = 0;
- public static final int SAVE_MODE_SPLIT = 1;
- public static final int SAVE_MODE_JOIN = 2;
-
- private long mRawContactIdRequestingPhoto = -1;
-
- private static final int DIALOG_CONFIRM_DELETE = 1;
- private static final int DIALOG_CONFIRM_READONLY_DELETE = 2;
- private static final int DIALOG_CONFIRM_MULTIPLE_DELETE = 3;
- private static final int DIALOG_CONFIRM_READONLY_HIDE = 4;
- private static final int DIALOG_PICK_PHOTO = 5;
- private static final int DIALOG_SPLIT = 6;
- private static final int DIALOG_SELECT_ACCOUNT = 7;
- private static final int DIALOG_VIEW_DIALOGS_ID1 = 8;
- private static final int DIALOG_VIEW_DIALOGS_ID2 = 9;
-
- private static final String BUNDLE_SELECT_ACCOUNT_LIST = "account_list";
-
- private static final int ICON_SIZE = 96;
-
- private static final File PHOTO_DIR = new File(
- Environment.getExternalStorageDirectory() + "/DCIM/Camera");
-
- private File mCurrentPhotoFile;
-
- String mQuerySelection;
-
- private long mContactIdForJoin;
-
- private static final int STATUS_LOADING = 0;
- private static final int STATUS_EDITING = 1;
- private static final int STATUS_SAVING = 2;
-
- private int mStatus;
- private DialogManager mDialogManager;
-
- EntitySet mState;
-
- /** The linear layout holding the ContactEditorViews */
- LinearLayout mContent;
-
- private ViewIdGenerator mViewIdGenerator;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- final Intent intent = getIntent();
- final String action = intent.getAction();
-
- setContentView(R.layout.act_edit);
-
- mDialogManager = new DialogManager(this, DIALOG_VIEW_DIALOGS_ID1, DIALOG_VIEW_DIALOGS_ID2);
-
- // Build editor and listen for photo requests
- mContent = (LinearLayout) findViewById(R.id.editors);
-
- findViewById(R.id.btn_done).setOnClickListener(this);
- findViewById(R.id.btn_discard).setOnClickListener(this);
-
- // Handle initial actions only when existing state missing
- final boolean hasIncomingState = icicle != null && icicle.containsKey(KEY_EDIT_STATE);
-
- if (Intent.ACTION_EDIT.equals(action) && !hasIncomingState) {
- setTitle(R.string.editContact_title_edit);
- mStatus = STATUS_LOADING;
-
- // Read initial state from database
- new QueryEntitiesTask(this).execute(intent);
- } else if (Intent.ACTION_INSERT.equals(action) && !hasIncomingState) {
- setTitle(R.string.editContact_title_insert);
- mStatus = STATUS_EDITING;
- // Trigger dialog to pick account type
- doAddAction();
- }
-
- if (icicle == null) {
- // If icicle is non-null, onRestoreInstanceState() will restore the generator.
- mViewIdGenerator = new ViewIdGenerator();
- }
- }
-
- private static class QueryEntitiesTask extends
- WeakAsyncTask<Intent, Void, EntitySet, EditContactActivity> {
-
- private String mSelection;
-
- public QueryEntitiesTask(EditContactActivity target) {
- super(target);
- }
-
- @Override
- protected EntitySet doInBackground(EditContactActivity target, Intent... params) {
- final Intent intent = params[0];
-
- final ContentResolver resolver = target.getContentResolver();
-
- // Handle both legacy and new authorities
- final Uri data = intent.getData();
- final String authority = data.getAuthority();
- final String mimeType = intent.resolveType(resolver);
-
- mSelection = "0";
- if (ContactsContract.AUTHORITY.equals(authority)) {
- if (Contacts.CONTENT_ITEM_TYPE.equals(mimeType)) {
- // Handle selected aggregate
- final long contactId = ContentUris.parseId(data);
- mSelection = RawContacts.CONTACT_ID + "=" + contactId;
- } else if (RawContacts.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final long rawContactId = ContentUris.parseId(data);
- final long contactId = ContactsUtils.queryForContactId(resolver, rawContactId);
- mSelection = RawContacts.CONTACT_ID + "=" + contactId;
- }
- } else if (android.provider.Contacts.AUTHORITY.equals(authority)) {
- final long rawContactId = ContentUris.parseId(data);
- mSelection = Data.RAW_CONTACT_ID + "=" + rawContactId;
- }
-
- return EntitySet.fromQuery(target.getContentResolver(), mSelection, null, null);
- }
-
- @Override
- protected void onPostExecute(EditContactActivity target, EntitySet entitySet) {
- target.mQuerySelection = mSelection;
-
- // Load edit details in background
- final Context context = target;
- final Sources sources = Sources.getInstance(context);
-
- // Handle any incoming values that should be inserted
- final Bundle extras = target.getIntent().getExtras();
- final boolean hasExtras = extras != null && extras.size() > 0;
- final boolean hasState = entitySet.size() > 0;
- if (hasExtras && hasState) {
- // Find source defining the first RawContact found
- final EntityDelta state = entitySet.get(0);
- final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final ContactsSource source = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_CONSTRAINTS);
- EntityModifier.parseExtras(context, source, state, extras);
- }
-
- target.mState = entitySet;
-
- // Bind UI to new background state
- target.bindEditors();
- }
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- if (hasValidState()) {
- // Store entities with modifications
- outState.putParcelable(KEY_EDIT_STATE, mState);
- }
-
- outState.putLong(KEY_RAW_CONTACT_ID_REQUESTING_PHOTO, mRawContactIdRequestingPhoto);
- outState.putParcelable(KEY_VIEW_ID_GENERATOR, mViewIdGenerator);
- if (mCurrentPhotoFile != null) {
- outState.putString(KEY_CURRENT_PHOTO_FILE, mCurrentPhotoFile.toString());
- }
- outState.putString(KEY_QUERY_SELECTION, mQuerySelection);
- outState.putLong(KEY_CONTACT_ID_FOR_JOIN, mContactIdForJoin);
- super.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- // Read modifications from instance
- mState = savedInstanceState.<EntitySet> getParcelable(KEY_EDIT_STATE);
- mRawContactIdRequestingPhoto = savedInstanceState.getLong(
- KEY_RAW_CONTACT_ID_REQUESTING_PHOTO);
- mViewIdGenerator = savedInstanceState.getParcelable(KEY_VIEW_ID_GENERATOR);
- String fileName = savedInstanceState.getString(KEY_CURRENT_PHOTO_FILE);
- if (fileName != null) {
- mCurrentPhotoFile = new File(fileName);
- }
- mQuerySelection = savedInstanceState.getString(KEY_QUERY_SELECTION);
- mContactIdForJoin = savedInstanceState.getLong(KEY_CONTACT_ID_FOR_JOIN);
-
- bindEditors();
-
- super.onRestoreInstanceState(savedInstanceState);
- }
-
- @Override
- protected Dialog onCreateDialog(int id, Bundle bundle) {
- switch (id) {
- case DIALOG_CONFIRM_DELETE:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.deleteConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, new DeleteClickListener())
- .setCancelable(false)
- .create();
- case DIALOG_CONFIRM_READONLY_DELETE:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.readOnlyContactDeleteConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, new DeleteClickListener())
- .setCancelable(false)
- .create();
- case DIALOG_CONFIRM_MULTIPLE_DELETE:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.multipleContactDeleteConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, new DeleteClickListener())
- .setCancelable(false)
- .create();
- case DIALOG_CONFIRM_READONLY_HIDE:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.readOnlyContactWarning)
- .setPositiveButton(android.R.string.ok, new DeleteClickListener())
- .setCancelable(false)
- .create();
- case DIALOG_PICK_PHOTO:
- return createPickPhotoDialog();
- case DIALOG_SPLIT:
- return createSplitDialog();
- case DIALOG_SELECT_ACCOUNT:
- return createSelectAccountDialog(bundle);
- default:
- return mDialogManager.onCreateDialog(id, bundle);
- }
- }
-
- /**
- * Dismiss the given {@link Dialog}.
- */
- static void dismissDialog(Dialog dialog) {
- try {
- // Only dismiss when valid reference and still showing
- if (dialog != null && dialog.isShowing()) {
- dialog.dismiss();
- }
- } catch (Exception e) {
- Log.w(TAG, "Ignoring exception while dismissing dialog: " + e.toString());
- }
- }
-
- /**
- * Check if our internal {@link #mState} is valid, usually checked before
- * performing user actions.
- */
- protected boolean hasValidState() {
- return mStatus == STATUS_EDITING && mState != null && mState.size() > 0;
- }
-
- /**
- * Rebuild the editors to match our underlying {@link #mState} object, usually
- * called once we've parsed {@link Entity} data or have inserted a new
- * {@link RawContacts}.
- */
- protected void bindEditors() {
- if (mState == null) {
- return;
- }
-
- final LayoutInflater inflater = (LayoutInflater) getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- final Sources sources = Sources.getInstance(this);
-
- // Sort the editors
- Collections.sort(mState, this);
-
- // Remove any existing editors and rebuild any visible
- mContent.removeAllViews();
- int size = mState.size();
- for (int i = 0; i < size; i++) {
- // TODO ensure proper ordering of entities in the list
- EntityDelta entity = mState.get(i);
- final ValuesDelta values = entity.getValues();
- if (!values.isVisible()) continue;
-
- final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
- final ContactsSource source = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_CONSTRAINTS);
- final long rawContactId = values.getAsLong(RawContacts._ID);
-
- BaseContactEditorView editor;
- if (!source.readOnly) {
- editor = (BaseContactEditorView) inflater.inflate(R.layout.item_contact_editor,
- mContent, false);
- } else {
- editor = (BaseContactEditorView) inflater.inflate(
- R.layout.item_read_only_contact_editor, mContent, false);
- }
- PhotoEditorView photoEditor = editor.getPhotoEditor();
- photoEditor.setEditorListener(new PhotoListener(rawContactId, source.readOnly,
- photoEditor));
-
- mContent.addView(editor);
- editor.setState(entity, source, mViewIdGenerator);
- }
-
- // Show editor now that we've loaded state
- mContent.setVisibility(View.VISIBLE);
- mStatus = STATUS_EDITING;
- }
-
- /**
- * Class that listens to requests coming from photo editors
- */
- private class PhotoListener implements EditorListener, DialogInterface.OnClickListener {
- private long mRawContactId;
- private boolean mReadOnly;
- private PhotoEditorView mEditor;
-
- public PhotoListener(long rawContactId, boolean readOnly, PhotoEditorView editor) {
- mRawContactId = rawContactId;
- mReadOnly = readOnly;
- mEditor = editor;
- }
-
- public void onDeleted(Editor editor) {
- // Do nothing
- }
-
- public void onRequest(int request) {
- if (!hasValidState()) return;
-
- if (request == EditorListener.REQUEST_PICK_PHOTO) {
- if (mEditor.hasSetPhoto()) {
- // There is an existing photo, offer to remove, replace, or promoto to primary
- createPhotoDialog().show();
- } else if (!mReadOnly) {
- // No photo set and not read-only, try to set the photo
- doPickPhotoAction(mRawContactId);
- }
- }
- }
-
- /**
- * Prepare dialog for picking a new {@link EditType} or entering a
- * custom label. This dialog is limited to the valid types as determined
- * by {@link EntityModifier}.
- */
- public Dialog createPhotoDialog() {
- Context context = EditContactActivity.this;
-
- // Wrap our context to inflate list items using correct theme
- final Context dialogContext = new ContextThemeWrapper(context,
- android.R.style.Theme_Light);
-
- String[] choices;
- if (mReadOnly) {
- choices = new String[1];
- choices[0] = getString(R.string.use_photo_as_primary);
- } else {
- choices = new String[3];
- choices[0] = getString(R.string.use_photo_as_primary);
- choices[1] = getString(R.string.removePicture);
- choices[2] = getString(R.string.changePicture);
- }
- final ListAdapter adapter = new ArrayAdapter<String>(dialogContext,
- android.R.layout.simple_list_item_1, choices);
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(dialogContext);
- builder.setTitle(R.string.attachToContact);
- builder.setSingleChoiceItems(adapter, -1, this);
- return builder.create();
- }
-
- /**
- * Called when something in the dialog is clicked
- */
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
-
- switch (which) {
- case 0:
- // Set the photo as super primary
- mEditor.setSuperPrimary(true);
-
- // And set all other photos as not super primary
- int count = mContent.getChildCount();
- for (int i = 0; i < count; i++) {
- View childView = mContent.getChildAt(i);
- if (childView instanceof BaseContactEditorView) {
- BaseContactEditorView editor = (BaseContactEditorView) childView;
- PhotoEditorView photoEditor = editor.getPhotoEditor();
- if (!photoEditor.equals(mEditor)) {
- photoEditor.setSuperPrimary(false);
- }
- }
- }
- break;
-
- case 1:
- // Remove the photo
- mEditor.setPhotoBitmap(null);
- break;
-
- case 2:
- // Pick a new photo for the contact
- doPickPhotoAction(mRawContactId);
- break;
- }
- }
- }
-
- /** {@inheritDoc} */
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.btn_done:
- doSaveAction(SAVE_MODE_DEFAULT);
- break;
- case R.id.btn_discard:
- doRevertAction();
- break;
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void onBackPressed() {
- doSaveAction(SAVE_MODE_DEFAULT);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- // Ignore failed requests
- if (resultCode != RESULT_OK) return;
-
- switch (requestCode) {
- case PHOTO_PICKED_WITH_DATA: {
- BaseContactEditorView requestingEditor = null;
- for (int i = 0; i < mContent.getChildCount(); i++) {
- View childView = mContent.getChildAt(i);
- if (childView instanceof BaseContactEditorView) {
- BaseContactEditorView editor = (BaseContactEditorView) childView;
- if (editor.getRawContactId() == mRawContactIdRequestingPhoto) {
- requestingEditor = editor;
- break;
- }
- }
- }
-
- if (requestingEditor != null) {
- final Bitmap photo = data.getParcelableExtra("data");
- requestingEditor.setPhotoBitmap(photo);
- mRawContactIdRequestingPhoto = -1;
- } else {
- // The contact that requested the photo is no longer present.
- // TODO: Show error message
- }
-
- break;
- }
-
- case CAMERA_WITH_DATA: {
- doCropPhoto(mCurrentPhotoFile);
- break;
- }
-
- case REQUEST_JOIN_CONTACT: {
- if (resultCode == RESULT_OK && data != null) {
- final long contactId = ContentUris.parseId(data.getData());
- joinAggregate(contactId);
- }
- }
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
-
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.edit, menu);
-
-
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- menu.findItem(R.id.menu_split).setVisible(mState != null && mState.size() > 1);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_done:
- return doSaveAction(SAVE_MODE_DEFAULT);
- case R.id.menu_discard:
- return doRevertAction();
- case R.id.menu_add:
- return doAddAction();
- case R.id.menu_delete:
- return doDeleteAction();
- case R.id.menu_split:
- return doSplitContactAction();
- case R.id.menu_join:
- return doJoinContactAction();
- }
- return false;
- }
-
- /**
- * Background task for persisting edited contact data, using the changes
- * defined by a set of {@link EntityDelta}. This task starts
- * {@link EmptyService} to make sure the background thread can finish
- * persisting in cases where the system wants to reclaim our process.
- */
- public static class PersistTask extends
- WeakAsyncTask<EntitySet, Void, Integer, EditContactActivity> {
- private static final int PERSIST_TRIES = 3;
-
- private static final int RESULT_UNCHANGED = 0;
- private static final int RESULT_SUCCESS = 1;
- private static final int RESULT_FAILURE = 2;
-
- private WeakReference<ProgressDialog> mProgress;
-
- private int mSaveMode;
- private Uri mContactLookupUri = null;
-
- public PersistTask(EditContactActivity target, int saveMode) {
- super(target);
- mSaveMode = saveMode;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onPreExecute(EditContactActivity target) {
- mProgress = new WeakReference<ProgressDialog>(ProgressDialog.show(target, null,
- target.getText(R.string.savingContact)));
-
- // Before starting this task, start an empty service to protect our
- // process from being reclaimed by the system.
- final Context context = target;
- context.startService(new Intent(context, EmptyService.class));
- }
-
- /** {@inheritDoc} */
- @Override
- protected Integer doInBackground(EditContactActivity target, EntitySet... params) {
- final Context context = target;
- final ContentResolver resolver = context.getContentResolver();
-
- EntitySet state = params[0];
-
- // Trim any empty fields, and RawContacts, before persisting
- final Sources sources = Sources.getInstance(context);
- EntityModifier.trimEmpty(state, sources);
-
- // Attempt to persist changes
- int tries = 0;
- Integer result = RESULT_FAILURE;
- while (tries++ < PERSIST_TRIES) {
- try {
- // Build operations and try applying
- final ArrayList<ContentProviderOperation> diff = state.buildDiff();
- ContentProviderResult[] results = null;
- if (!diff.isEmpty()) {
- results = resolver.applyBatch(ContactsContract.AUTHORITY, diff);
- }
-
- final long rawContactId = getRawContactId(state, diff, results);
- if (rawContactId != -1) {
- final Uri rawContactUri = ContentUris.withAppendedId(
- RawContacts.CONTENT_URI, rawContactId);
-
- // convert the raw contact URI to a contact URI
- mContactLookupUri = RawContacts.getContactLookupUri(resolver,
- rawContactUri);
- }
- result = (diff.size() > 0) ? RESULT_SUCCESS : RESULT_UNCHANGED;
- break;
-
- } catch (RemoteException e) {
- // Something went wrong, bail without success
- Log.e(TAG, "Problem persisting user edits", e);
- break;
-
- } catch (OperationApplicationException e) {
- // Version consistency failed, re-parent change and try again
- Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
- final EntitySet newState = EntitySet.fromQuery(resolver,
- target.mQuerySelection, null, null);
- state = EntitySet.mergeAfter(newState, state);
- }
- }
-
- return result;
- }
-
- private long getRawContactId(EntitySet state,
- final ArrayList<ContentProviderOperation> diff,
- final ContentProviderResult[] results) {
- long rawContactId = state.findRawContactId();
- if (rawContactId != -1) {
- return rawContactId;
- }
-
- // we gotta do some searching for the id
- final int diffSize = diff.size();
- for (int i = 0; i < diffSize; i++) {
- ContentProviderOperation operation = diff.get(i);
- if (operation.getType() == ContentProviderOperation.TYPE_INSERT
- && operation.getUri().getEncodedPath().contains(
- RawContacts.CONTENT_URI.getEncodedPath())) {
- return ContentUris.parseId(results[i].uri);
- }
- }
- return -1;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onPostExecute(EditContactActivity target, Integer result) {
- final Context context = target;
- final ProgressDialog progress = mProgress.get();
-
- if (result == RESULT_SUCCESS && mSaveMode != SAVE_MODE_JOIN) {
- Toast.makeText(context, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
- } else if (result == RESULT_FAILURE) {
- Toast.makeText(context, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
- }
-
- dismissDialog(progress);
-
- // Stop the service that was protecting us
- context.stopService(new Intent(context, EmptyService.class));
-
- target.onSaveCompleted(result != RESULT_FAILURE, mSaveMode, mContactLookupUri);
- }
- }
-
- /**
- * Saves or creates the contact based on the mode, and if successful
- * finishes the activity.
- */
- boolean doSaveAction(int saveMode) {
- if (!hasValidState()) {
- return false;
- }
-
- mStatus = STATUS_SAVING;
- final PersistTask task = new PersistTask(this, saveMode);
- task.execute(mState);
-
- return true;
- }
-
- private class DeleteClickListener implements DialogInterface.OnClickListener {
-
- public void onClick(DialogInterface dialog, int which) {
- Sources sources = Sources.getInstance(EditContactActivity.this);
- // Mark all raw contacts for deletion
- for (EntityDelta delta : mState) {
- delta.markDeleted();
- }
- // Save the deletes
- doSaveAction(SAVE_MODE_DEFAULT);
- finish();
- }
- }
-
- private void onSaveCompleted(boolean success, int saveMode, Uri contactLookupUri) {
- switch (saveMode) {
- case SAVE_MODE_DEFAULT:
- if (success && contactLookupUri != null) {
- final Intent resultIntent = new Intent();
-
- final Uri requestData = getIntent().getData();
- final String requestAuthority = requestData == null ? null : requestData
- .getAuthority();
-
- if (android.provider.Contacts.AUTHORITY.equals(requestAuthority)) {
- // Build legacy Uri when requested by caller
- final long contactId = ContentUris.parseId(Contacts.lookupContact(
- getContentResolver(), contactLookupUri));
- final Uri legacyUri = ContentUris.withAppendedId(
- android.provider.Contacts.People.CONTENT_URI, contactId);
- resultIntent.setData(legacyUri);
- } else {
- // Otherwise pass back a lookup-style Uri
- resultIntent.setData(contactLookupUri);
- }
-
- setResult(RESULT_OK, resultIntent);
- } else {
- setResult(RESULT_CANCELED, null);
- }
- finish();
- break;
-
- case SAVE_MODE_SPLIT:
- if (success) {
- Intent intent = new Intent();
- intent.setData(contactLookupUri);
- setResult(RESULT_CLOSE_VIEW_ACTIVITY, intent);
- }
- finish();
- break;
-
- case SAVE_MODE_JOIN:
- mStatus = STATUS_EDITING;
- if (success) {
- showJoinAggregateActivity(contactLookupUri);
- }
- break;
- }
- }
-
- /**
- * Shows a list of aggregates that can be joined into the currently viewed aggregate.
- *
- * @param contactLookupUri the fresh URI for the currently edited contact (after saving it)
- */
- public void showJoinAggregateActivity(Uri contactLookupUri) {
- if (contactLookupUri == null) {
- return;
- }
-
- mContactIdForJoin = ContentUris.parseId(contactLookupUri);
- Intent intent = new Intent(JoinContactActivity.JOIN_CONTACT);
- intent.putExtra(JoinContactActivity.EXTRA_TARGET_CONTACT_ID, mContactIdForJoin);
- startActivityForResult(intent, REQUEST_JOIN_CONTACT);
- }
-
- private interface JoinContactQuery {
- String[] PROJECTION = {
- RawContacts._ID,
- RawContacts.CONTACT_ID,
- RawContacts.NAME_VERIFIED,
- };
-
- String SELECTION = RawContacts.CONTACT_ID + "=? OR " + RawContacts.CONTACT_ID + "=?";
-
- int _ID = 0;
- int CONTACT_ID = 1;
- int NAME_VERIFIED = 2;
- }
-
- /**
- * Performs aggregation with the contact selected by the user from suggestions or A-Z list.
- */
- private void joinAggregate(final long contactId) {
- ContentResolver resolver = getContentResolver();
-
- // Load raw contact IDs for all raw contacts involved - currently edited and selected
- // in the join UIs
- Cursor c = resolver.query(RawContacts.CONTENT_URI,
- JoinContactQuery.PROJECTION,
- JoinContactQuery.SELECTION,
- new String[]{String.valueOf(contactId), String.valueOf(mContactIdForJoin)}, null);
-
- long rawContactIds[];
- long verifiedNameRawContactId = -1;
- try {
- rawContactIds = new long[c.getCount()];
- for (int i = 0; i < rawContactIds.length; i++) {
- c.moveToNext();
- long rawContactId = c.getLong(JoinContactQuery._ID);
- rawContactIds[i] = rawContactId;
- if (c.getLong(JoinContactQuery.CONTACT_ID) == mContactIdForJoin) {
- if (verifiedNameRawContactId == -1
- || c.getInt(JoinContactQuery.NAME_VERIFIED) != 0) {
- verifiedNameRawContactId = rawContactId;
- }
- }
- }
- } finally {
- c.close();
- }
-
- // For each pair of raw contacts, insert an aggregation exception
- ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
- for (int i = 0; i < rawContactIds.length; i++) {
- for (int j = 0; j < rawContactIds.length; j++) {
- if (i != j) {
- buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]);
- }
- }
- }
-
- // Mark the original contact as "name verified" to make sure that the contact
- // display name does not change as a result of the join
- Builder builder = ContentProviderOperation.newUpdate(
- ContentUris.withAppendedId(RawContacts.CONTENT_URI, verifiedNameRawContactId));
- builder.withValue(RawContacts.NAME_VERIFIED, 1);
- operations.add(builder.build());
-
- // Apply all aggregation exceptions as one batch
- try {
- getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
-
- // We can use any of the constituent raw contacts to refresh the UI - why not the first
- Intent intent = new Intent();
- intent.setData(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactIds[0]));
-
- // Reload the new state from database
- new QueryEntitiesTask(this).execute(intent);
-
- Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_LONG).show();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to apply aggregation exception batch", e);
- Toast.makeText(this, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
- } catch (OperationApplicationException e) {
- Log.e(TAG, "Failed to apply aggregation exception batch", e);
- Toast.makeText(this, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
- }
- }
-
- /**
- * Construct a {@link AggregationExceptions#TYPE_KEEP_TOGETHER} ContentProviderOperation.
- */
- private void buildJoinContactDiff(ArrayList<ContentProviderOperation> operations,
- long rawContactId1, long rawContactId2) {
- Builder builder =
- ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
- builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
- builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
- builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
- operations.add(builder.build());
- }
-
- /**
- * Revert any changes the user has made, and finish the activity.
- */
- private boolean doRevertAction() {
- finish();
- return true;
- }
-
- /**
- * Create a new {@link RawContacts} which will exist as another
- * {@link EntityDelta} under the currently edited {@link Contacts}.
- */
- private boolean doAddAction() {
- if (mStatus != STATUS_EDITING) {
- return false;
- }
-
- // Adding is okay when missing state
- new AddContactTask(this).execute();
- return true;
- }
-
- /**
- * Delete the entire contact currently being edited, which usually asks for
- * user confirmation before continuing.
- */
- private boolean doDeleteAction() {
- if (!hasValidState())
- return false;
- int readOnlySourcesCnt = 0;
- int writableSourcesCnt = 0;
- Sources sources = Sources.getInstance(EditContactActivity.this);
- for (EntityDelta delta : mState) {
- final String accountType = delta.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final ContactsSource contactsSource = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_CONSTRAINTS);
- if (contactsSource != null && contactsSource.readOnly) {
- readOnlySourcesCnt += 1;
- } else {
- writableSourcesCnt += 1;
- }
- }
-
- if (readOnlySourcesCnt > 0 && writableSourcesCnt > 0) {
- showDialog(DIALOG_CONFIRM_READONLY_DELETE);
- } else if (readOnlySourcesCnt > 0 && writableSourcesCnt == 0) {
- showDialog(DIALOG_CONFIRM_READONLY_HIDE);
- } else if (readOnlySourcesCnt == 0 && writableSourcesCnt > 1) {
- showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
- } else {
- showDialog(DIALOG_CONFIRM_DELETE);
- }
- return true;
- }
-
- /**
- * Pick a specific photo to be added under the currently selected tab.
- */
- boolean doPickPhotoAction(long rawContactId) {
- if (!hasValidState()) return false;
-
- mRawContactIdRequestingPhoto = rawContactId;
-
- showDialog(DIALOG_PICK_PHOTO);
-
- return true;
- }
-
- /**
- * Creates a dialog offering two options: take a photo or pick a photo from the gallery.
- */
- private Dialog createPickPhotoDialog() {
- Context context = EditContactActivity.this;
-
- // Wrap our context to inflate list items using correct theme
- final Context dialogContext = new ContextThemeWrapper(context,
- android.R.style.Theme_Light);
-
- String[] choices = new String[2];
- choices[0] = getString(R.string.take_photo);
- choices[1] = getString(R.string.pick_photo);
- final ListAdapter adapter = new ArrayAdapter<String>(dialogContext,
- android.R.layout.simple_list_item_1, choices);
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(dialogContext);
- builder.setTitle(R.string.attachToContact);
- builder.setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- switch(which) {
- case 0:
- doTakePhoto();
- break;
- case 1:
- doPickPhotoFromGallery();
- break;
- }
- }
- });
- return builder.create();
- }
-
- /**
- * Create a file name for the icon photo using current time.
- */
- private String getPhotoFileName() {
- Date date = new Date(System.currentTimeMillis());
- SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
- return dateFormat.format(date) + ".jpg";
- }
-
- /**
- * Launches Camera to take a picture and store it in a file.
- */
- protected void doTakePhoto() {
- try {
- // Launch camera to take photo for selected contact
- PHOTO_DIR.mkdirs();
- mCurrentPhotoFile = new File(PHOTO_DIR, getPhotoFileName());
- final Intent intent = getTakePickIntent(mCurrentPhotoFile);
- startActivityForResult(intent, CAMERA_WITH_DATA);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
- }
- }
-
- /**
- * Constructs an intent for capturing a photo and storing it in a temporary file.
- */
- public static Intent getTakePickIntent(File f) {
- Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);
- intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
- return intent;
- }
-
- /**
- * Sends a newly acquired photo to Gallery for cropping
- */
- protected void doCropPhoto(File f) {
- try {
-
- // Add the image to the media store
- MediaScannerConnection.scanFile(
- this,
- new String[] { f.getAbsolutePath() },
- new String[] { null },
- null);
-
- // Launch gallery to crop the photo
- final Intent intent = getCropImageIntent(Uri.fromFile(f));
- startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
- } catch (Exception e) {
- Log.e(TAG, "Cannot crop image", e);
- Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
- }
- }
-
- /**
- * Constructs an intent for image cropping.
- */
- public static Intent getCropImageIntent(Uri photoUri) {
- Intent intent = new Intent("com.android.camera.action.CROP");
- intent.setDataAndType(photoUri, "image/*");
- intent.putExtra("crop", "true");
- intent.putExtra("aspectX", 1);
- intent.putExtra("aspectY", 1);
- intent.putExtra("outputX", ICON_SIZE);
- intent.putExtra("outputY", ICON_SIZE);
- intent.putExtra("return-data", true);
- return intent;
- }
-
- /**
- * Launches Gallery to pick a photo.
- */
- protected void doPickPhotoFromGallery() {
- try {
- // Launch picker to choose photo for selected contact
- final Intent intent = getPhotoPickIntent();
- startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
- }
- }
-
- /**
- * Constructs an intent for picking a photo from Gallery, cropping it and returning the bitmap.
- */
- public static Intent getPhotoPickIntent() {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
- intent.setType("image/*");
- intent.putExtra("crop", "true");
- intent.putExtra("aspectX", 1);
- intent.putExtra("aspectY", 1);
- intent.putExtra("outputX", ICON_SIZE);
- intent.putExtra("outputY", ICON_SIZE);
- intent.putExtra("return-data", true);
- return intent;
- }
-
- /** {@inheritDoc} */
- public void onDeleted(Editor editor) {
- // Ignore any editor deletes
- }
-
- private boolean doSplitContactAction() {
- if (!hasValidState()) return false;
-
- showDialog(DIALOG_SPLIT);
- return true;
- }
-
- private Dialog createSplitDialog() {
- final AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.splitConfirmation_title);
- builder.setIcon(android.R.drawable.ic_dialog_alert);
- builder.setMessage(R.string.splitConfirmation);
- builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- // Split the contacts
- mState.splitRawContacts();
- doSaveAction(SAVE_MODE_SPLIT);
- }
- });
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.setCancelable(false);
- return builder.create();
- }
-
- private boolean doJoinContactAction() {
- return doSaveAction(SAVE_MODE_JOIN);
- }
-
- /**
- * Build dialog that handles adding a new {@link RawContacts} after the user
- * picks a specific {@link ContactsSource}.
- */
- private static class AddContactTask extends
- WeakAsyncTask<Void, Void, ArrayList<Account>, EditContactActivity> {
-
- public AddContactTask(EditContactActivity target) {
- super(target);
- }
-
- @Override
- protected ArrayList<Account> doInBackground(final EditContactActivity target,
- Void... params) {
- return Sources.getInstance(target).getAccounts(true);
- }
-
- @Override
- protected void onPostExecute(final EditContactActivity target, ArrayList<Account> accounts) {
- target.selectAccountAndCreateContact(accounts);
- }
- }
-
- public void selectAccountAndCreateContact(ArrayList<Account> accounts) {
- // No Accounts available. Create a phone-local contact.
- if (accounts.isEmpty()) {
- createContact(null);
- return; // Don't show a dialog.
- }
-
- // In the common case of a single account being writable, auto-select
- // it without showing a dialog.
- if (accounts.size() == 1) {
- createContact(accounts.get(0));
- return; // Don't show a dialog.
- }
-
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(BUNDLE_SELECT_ACCOUNT_LIST, accounts);
- showDialog(DIALOG_SELECT_ACCOUNT, bundle);
- }
-
- private Dialog createSelectAccountDialog(Bundle bundle) {
- final ArrayList<Account> accounts = bundle.getParcelableArrayList(
- BUNDLE_SELECT_ACCOUNT_LIST);
- // Wrap our context to inflate list items using correct theme
- final Context dialogContext = new ContextThemeWrapper(this, android.R.style.Theme_Light);
- final LayoutInflater dialogInflater =
- (LayoutInflater)dialogContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- final Sources sources = Sources.getInstance(this);
-
- final ArrayAdapter<Account> accountAdapter = new ArrayAdapter<Account>(this,
- android.R.layout.simple_list_item_2, accounts) {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = dialogInflater.inflate(android.R.layout.simple_list_item_2,
- parent, false);
- }
-
- // TODO: show icon along with title
- final TextView text1 = (TextView)convertView.findViewById(android.R.id.text1);
- final TextView text2 = (TextView)convertView.findViewById(android.R.id.text2);
-
- final Account account = this.getItem(position);
- final ContactsSource source = sources.getInflatedSource(account.type,
- ContactsSource.LEVEL_SUMMARY);
-
- text1.setText(account.name);
- text2.setText(source.getDisplayLabel(EditContactActivity.this));
-
- return convertView;
- }
- };
-
- final DialogInterface.OnClickListener clickListener =
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
-
- // Create new contact based on selected source
- final Account account = accountAdapter.getItem(which);
- createContact(account);
- }
- };
-
- final DialogInterface.OnCancelListener cancelListener =
- new DialogInterface.OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- // If nothing remains, close activity
- if (!hasValidState()) {
- finish();
- }
- }
- };
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.dialog_new_contact_account);
- builder.setSingleChoiceItems(accountAdapter, 0, clickListener);
- builder.setOnCancelListener(cancelListener);
- final Dialog result = builder.create();
- result.setOnDismissListener(new OnDismissListener() {
- public void onDismiss(DialogInterface dialog) {
- removeDialog(DIALOG_SELECT_ACCOUNT);
- }
- });
- return result;
- }
-
- /**
- * @param account may be null to signal a device-local contact should
- * be created.
- */
- private void createContact(Account account) {
- final Sources sources = Sources.getInstance(this);
- final ContentValues values = new ContentValues();
- if (account != null) {
- values.put(RawContacts.ACCOUNT_NAME, account.name);
- values.put(RawContacts.ACCOUNT_TYPE, account.type);
- } else {
- values.putNull(RawContacts.ACCOUNT_NAME);
- values.putNull(RawContacts.ACCOUNT_TYPE);
- }
-
- // Parse any values from incoming intent
- EntityDelta insert = new EntityDelta(ValuesDelta.fromAfter(values));
- final ContactsSource source = sources.getInflatedSource(
- account != null ? account.type : null,
- ContactsSource.LEVEL_CONSTRAINTS);
- final Bundle extras = getIntent().getExtras();
- EntityModifier.parseExtras(this, source, insert, extras);
-
- // Ensure we have some default fields
- EntityModifier.ensureKindExists(insert, source, Phone.CONTENT_ITEM_TYPE);
- EntityModifier.ensureKindExists(insert, source, Email.CONTENT_ITEM_TYPE);
-
- if (mState == null) {
- // Create state if none exists yet
- mState = EntitySet.fromSingle(insert);
- } else {
- // Add contact onto end of existing state
- mState.add(insert);
- }
-
- bindEditors();
- }
-
- /**
- * Compare EntityDeltas for sorting the stack of editors.
- */
- public int compare(EntityDelta one, EntityDelta two) {
- // Check direct equality
- if (one.equals(two)) {
- return 0;
- }
-
- final Sources sources = Sources.getInstance(this);
- String accountType = one.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final ContactsSource oneSource = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_SUMMARY);
- accountType = two.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final ContactsSource twoSource = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_SUMMARY);
-
- // Check read-only
- if (oneSource.readOnly && !twoSource.readOnly) {
- return 1;
- } else if (twoSource.readOnly && !oneSource.readOnly) {
- return -1;
- }
-
- // Check account type
- boolean skipAccountTypeCheck = false;
- boolean oneIsGoogle = oneSource instanceof GoogleSource;
- boolean twoIsGoogle = twoSource instanceof GoogleSource;
- if (oneIsGoogle && !twoIsGoogle) {
- return -1;
- } else if (twoIsGoogle && !oneIsGoogle) {
- return 1;
- } else if (oneIsGoogle && twoIsGoogle){
- skipAccountTypeCheck = true;
- }
-
- int value;
- if (!skipAccountTypeCheck) {
- value = oneSource.accountType.compareTo(twoSource.accountType);
- if (value != 0) {
- return value;
- }
- }
-
- // Check account name
- ValuesDelta oneValues = one.getValues();
- String oneAccount = oneValues.getAsString(RawContacts.ACCOUNT_NAME);
- if (oneAccount == null) oneAccount = "";
- ValuesDelta twoValues = two.getValues();
- String twoAccount = twoValues.getAsString(RawContacts.ACCOUNT_NAME);
- if (twoAccount == null) twoAccount = "";
- value = oneAccount.compareTo(twoAccount);
- if (value != 0) {
- return value;
- }
-
- // Both are in the same account, fall back to contact ID
- Long oneId = oneValues.getAsLong(RawContacts._ID);
- Long twoId = twoValues.getAsLong(RawContacts._ID);
- if (oneId == null) {
- return -1;
- } else if (twoId == null) {
- return 1;
- }
-
- return (int)(oneId - twoId);
- }
-
- @Override
- public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
- boolean globalSearch) {
- if (globalSearch) {
- super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
- } else {
- ContactsSearchManager.startSearch(this, initialQuery);
- }
- }
-
- public DialogManager getDialogManager() {
- return mDialogManager;
- }
-}
diff --git a/src/com/android/contacts/views/detail/ContactDetailFragment.java b/src/com/android/contacts/views/detail/ContactDetailFragment.java
index 49471cc..a35285f 100644
--- a/src/com/android/contacts/views/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/views/detail/ContactDetailFragment.java
@@ -195,6 +195,7 @@
}
public void loadUri(Uri lookupUri) {
+ // TODO: Ensure we are not loading twice here
mLookupUri = lookupUri;
if (mIsInitialized) startLoading(LOADER_DETAILS, null);
}
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index afb6f70..e50994a 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -16,96 +16,139 @@
package com.android.contacts.views.editor;
-import com.android.contacts.ContactOptionsActivity;
+import com.android.contacts.JoinContactActivity;
import com.android.contacts.R;
import com.android.contacts.model.ContactsSource;
+import com.android.contacts.model.Editor;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityModifier;
+import com.android.contacts.model.EntitySet;
+import com.android.contacts.model.GoogleSource;
import com.android.contacts.model.Sources;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.ui.EditContactActivity;
-import com.android.contacts.views.ContactLoader;
-import com.android.contacts.views.editor.viewModel.BaseViewModel;
-import com.android.contacts.views.editor.viewModel.EmailViewModel;
-import com.android.contacts.views.editor.viewModel.FooterViewModel;
-import com.android.contacts.views.editor.viewModel.GenericViewModel;
-import com.android.contacts.views.editor.viewModel.ImViewModel;
-import com.android.contacts.views.editor.viewModel.OrganizationViewModel;
-import com.android.contacts.views.editor.viewModel.PhotoViewModel;
-import com.android.contacts.views.editor.viewModel.StructuredNameViewModel;
-import com.android.contacts.views.editor.viewModel.StructuredPostalViewModel;
+import com.android.contacts.model.ContactsSource.EditType;
+import com.android.contacts.model.Editor.EditorListener;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.ui.ViewIdGenerator;
+import com.android.contacts.ui.widget.BaseContactEditorView;
+import com.android.contacts.ui.widget.PhotoEditorView;
+import com.android.contacts.util.EmptyService;
+import com.android.contacts.util.WeakAsyncTask;
+import android.accounts.Account;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.LoaderManagingFragment;
+import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.Entity;
import android.content.Intent;
import android.content.Loader;
-import android.content.Entity.NamedContentValues;
+import android.content.OperationApplicationException;
+import android.content.ContentProviderOperation.Builder;
+import android.content.DialogInterface.OnDismissListener;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.media.MediaScannerConnection;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+import android.provider.ContactsContract.AggregationExceptions;
import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Nickname;
-import android.provider.ContactsContract.CommonDataKinds.Note;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.View.OnCreateContextMenuListener;
+import android.view.View.OnClickListener;
+import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
-import android.widget.ListView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
import android.widget.Toast;
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
-public class ContactEditorFragment extends LoaderManagingFragment<ContactLoader.Result>
- implements OnCreateContextMenuListener {
+//Here are the open TODOs for the Fragment transition
+//TODO How to save data? Service?
+//TODO Do account-list lookup always in a thread
+//TODO Cleanup state handling (orientation changes etc).
+//TODO Cleanup the load function. It can currenlty also do insert, which is awkward
+//TODO Watch for background changes...How?
+
+public class ContactEditorFragment extends LoaderManagingFragment<ContactEditorLoader.Result> {
+
private static final String TAG = "ContactEditorFragment";
- private static final String BUNDLE_RAW_CONTACT_ID = "rawContactId";
+ private static final int LOADER_DATA = 1;
- private static final int LOADER_DETAILS = 1;
+ private static final String KEY_EDIT_STATE = "state";
+ private static final String KEY_RAW_CONTACT_ID_REQUESTING_PHOTO = "photorequester";
+ private static final String KEY_VIEW_ID_GENERATOR = "viewidgenerator";
+ private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile";
+ private static final String KEY_QUERY_SELECTION = "queryselection";
+ private static final String KEY_QUERY_SELECTION_ARGS = "queryselectionargs";
+ private static final String KEY_CONTACT_ID_FOR_JOIN = "contactidforjoin";
+
+ public static final int SAVE_MODE_DEFAULT = 0;
+ public static final int SAVE_MODE_SPLIT = 1;
+ public static final int SAVE_MODE_JOIN = 2;
+
+ private long mRawContactIdRequestingPhoto = -1;
+
+ private final EntityDeltaComparator mComparator = new EntityDeltaComparator();
+
+ private static final String BUNDLE_SELECT_ACCOUNT_LIST = "account_list";
+
+ private static final int ICON_SIZE = 96;
+
+ private static final File PHOTO_DIR = new File(
+ Environment.getExternalStorageDirectory() + "/DCIM/Camera");
+
+ private File mCurrentPhotoFile;
private Context mContext;
- private Uri mLookupUri;
+ private String mAction;
+ private Uri mUri;
+ private String mMimeType;
+ private Bundle mIntentExtras;
private Listener mListener;
+ private String mQuerySelection;
+ private String[] mQuerySelectionArgs;
+
+ private long mContactIdForJoin;
+
+ private LinearLayout mContent;
+ private EntitySet mState;
+
+ private ViewIdGenerator mViewIdGenerator;
+
private boolean mIsInitialized;
- private ContactLoader.Result mContactData;
- private ContactEditorHeaderView mHeaderView;
- private LinearLayout mFieldContainer;
-
- private int mReadOnlySourcesCnt;
- private int mWritableSourcesCnt;
- private boolean mAllRestricted;
-
- /**
- * A list of RawContacts included in this Contact.
- */
- private ArrayList<DisplayRawContact> mRawContacts = new ArrayList<DisplayRawContact>();
-
- private LayoutInflater mInflater;
-
public ContactEditorFragment() {
- // Explicit constructor for inflation
}
@Override
@@ -118,442 +161,1182 @@
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
final View view = inflater.inflate(R.layout.contact_editor_fragment, container, false);
- setHasOptionsMenu(true);
+ mContent = (LinearLayout) view.findViewById(R.id.editors);
- mInflater = inflater;
-
- mHeaderView = (ContactEditorHeaderView) view.findViewById(R.id.contact_header_widget);
-
- mFieldContainer = (LinearLayout) view.findViewById(R.id.field_container);
- mFieldContainer.setOnCreateContextMenuListener(this);
- mFieldContainer.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
+ view.findViewById(R.id.btn_done).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ doSaveAction(SAVE_MODE_DEFAULT);
+ }
+ });
+ view.findViewById(R.id.btn_discard).setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ doRevertAction();
+ }
+ });
return view;
}
+ // TODO: Think about splitting this. Doing INSERT via load is kinda weird
+ public void load(String action, Uri uri, String mimeType, Bundle intentExtras) {
+ mAction = action;
+ mUri = uri;
+ mMimeType = mimeType;
+ mIntentExtras = intentExtras;
+
+ if (mIsInitialized) {
+ if (Intent.ACTION_EDIT.equals(mAction)) {
+ // Read initial state from database
+ if (mListener != null) mListener.setTitleTo(R.string.editContact_title_edit);
+ startLoading(LOADER_DATA, null);
+ } else if (Intent.ACTION_INSERT.equals(mAction)) {
+ if (mListener != null) mListener.setTitleTo(R.string.editContact_title_insert);
+
+ doAddAction();
+ } else throw new IllegalArgumentException("Unknown Action String " + mAction +
+ ". Only support " + Intent.ACTION_EDIT + " or " + Intent.ACTION_INSERT);
+ }
+ }
+
+ @Override
+ protected void onInitializeLoaders() {
+ mIsInitialized = true;
+ if (mUri != null) {
+ if (Intent.ACTION_EDIT.equals(mAction)) {
+ // Read initial state from database
+ if (mListener != null) mListener.setTitleTo(R.string.editContact_title_edit);
+ startLoading(LOADER_DATA, null);
+ } else if (Intent.ACTION_INSERT.equals(mAction)) {
+ if (mListener != null) mListener.setTitleTo(R.string.editContact_title_insert);
+
+ doAddAction();
+ } else throw new IllegalArgumentException("Unknown Action String " + mAction +
+ ". Only support " + Intent.ACTION_EDIT + " or " + Intent.ACTION_INSERT);
+ }
+ }
+
public void setListener(Listener value) {
mListener = value;
}
- public void loadUri(Uri lookupUri) {
- mLookupUri = lookupUri;
- if (mIsInitialized) startLoading(LOADER_DETAILS, null);
- }
-
@Override
- protected void onInitializeLoaders() {
- mIsInitialized = true;
- if (mLookupUri != null) startLoading(LOADER_DETAILS, null);
- }
+ public void onCreate(Bundle savedState) {
+ // TODO: Currently savedState is always null (framework issue). Test once this is fixed
+ super.onCreate(savedState);
- @Override
- protected Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
- switch (id) {
- case LOADER_DETAILS: {
- return new ContactLoader(mContext, mLookupUri);
+ if (savedState == null) {
+ // If savedState is non-null, onRestoreInstanceState() will restore the generator.
+ mViewIdGenerator = new ViewIdGenerator();
+ } else {
+ // Read modifications from instance
+ mState = savedState.<EntitySet> getParcelable(KEY_EDIT_STATE);
+ mRawContactIdRequestingPhoto = savedState.getLong(
+ KEY_RAW_CONTACT_ID_REQUESTING_PHOTO);
+ mViewIdGenerator = savedState.getParcelable(KEY_VIEW_ID_GENERATOR);
+ String fileName = savedState.getString(KEY_CURRENT_PHOTO_FILE);
+ if (fileName != null) {
+ mCurrentPhotoFile = new File(fileName);
}
- default: {
- Log.wtf(TAG, "Unknown ID in onCreateLoader: " + id);
- }
- }
- return null;
- }
-
- @Override
- protected void onLoadFinished(Loader<ContactLoader.Result> loader,
- ContactLoader.Result data) {
- final int id = loader.getId();
- switch (id) {
- case LOADER_DETAILS:
- if (data == ContactLoader.Result.NOT_FOUND) {
- // Item has been deleted
- Log.i(TAG, "No contact found. Closing activity");
- mListener.onContactNotFound();
- return;
- }
- if (data == ContactLoader.Result.ERROR) {
- // Item has been deleted
- Log.i(TAG, "Error fetching contact. Closing activity");
- mListener.onError();
- return;
- }
- mContactData = data;
- bindData();
- break;
- default: {
- Log.wtf(TAG, "Unknown ID in onLoadFinished: " + id);
- }
+ mQuerySelection = savedState.getString(KEY_QUERY_SELECTION);
+ mQuerySelectionArgs = savedState.getStringArray(KEY_QUERY_SELECTION_ARGS);
+ mContactIdForJoin = savedState.getLong(KEY_CONTACT_ID_FOR_JOIN);
}
}
- private void bindData() {
- // Build up the contact entries
- buildEntries();
+ @Override
+ protected Loader<ContactEditorLoader.Result> onCreateLoader(int id, Bundle args) {
+ return new ContactEditorLoader(mContext, mUri, mMimeType, mIntentExtras);
+ }
- mHeaderView.setMergeInfo(mRawContacts.size());
+ @Override
+ protected void onLoadFinished(Loader<ContactEditorLoader.Result> loader,
+ ContactEditorLoader.Result data) {
+ if (data == ContactEditorLoader.Result.NOT_FOUND) {
+ // Item has been deleted
+ Log.i(TAG, "No contact found. Closing fragment");
+ if (mListener != null) mListener.closeBecauseContactNotFound();
+ return;
+ }
+ setData(data);
+ }
- createFieldViews();
+ public void setData(ContactEditorLoader.Result data) {
+ mState = data.getEntitySet();
+ bindEditors();
+ }
+
+ public void selectAccountAndCreateContact(ArrayList<Account> accounts, boolean isNewContact) {
+ // No Accounts available. Create a phone-local contact.
+ if (accounts.isEmpty()) {
+ createContact(null, isNewContact);
+ return; // Don't show a dialog.
+ }
+
+ // In the common case of a single account being writable, auto-select
+ // it without showing a dialog.
+ if (accounts.size() == 1) {
+ createContact(accounts.get(0), isNewContact);
+ return; // Don't show a dialog.
+ }
+
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(BUNDLE_SELECT_ACCOUNT_LIST, accounts);
+ getActivity().showDialog(R.id.edit_dialog_select_account, bundle);
}
/**
- * Build up the entries to display on the screen.
+ * @param account may be null to signal a device-local contact should
+ * be created.
+ * @param prefillFromIntent If this is set, the intent extras will be used to prefill the fields
*/
- private final void buildEntries() {
- // Clear out the old entries
- mRawContacts.clear();
-
- mReadOnlySourcesCnt = 0;
- mWritableSourcesCnt = 0;
- mAllRestricted = true;
-
- // TODO: This should be done in the background thread
+ private void createContact(Account account, boolean prefillFromIntent) {
final Sources sources = Sources.getInstance(mContext);
-
- // Build up method entries
- if (mContactData == null) {
- return;
+ final ContentValues values = new ContentValues();
+ if (account != null) {
+ values.put(RawContacts.ACCOUNT_NAME, account.name);
+ values.put(RawContacts.ACCOUNT_TYPE, account.type);
+ } else {
+ values.putNull(RawContacts.ACCOUNT_NAME);
+ values.putNull(RawContacts.ACCOUNT_TYPE);
}
- for (Entity entity: mContactData.getEntities()) {
- final ContentValues entValues = entity.getEntityValues();
- final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
- final String accountName = entValues.getAsString(RawContacts.ACCOUNT_NAME);
- final long rawContactId = entValues.getAsLong(RawContacts._ID);
- final String rawContactUriString = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
- rawContactId).toString();
+ // Parse any values from incoming intent
+ EntityDelta insert = new EntityDelta(ValuesDelta.fromAfter(values));
+ final ContactsSource source = sources.getInflatedSource(
+ account != null ? account.type : null,
+ ContactsSource.LEVEL_CONSTRAINTS);
+ EntityModifier.parseExtras(mContext, source, insert,
+ prefillFromIntent ? mIntentExtras : null);
- // Mark when this contact has any unrestricted components
- final boolean isRestricted = entValues.getAsInteger(RawContacts.IS_RESTRICTED) != 0;
- if (!isRestricted) mAllRestricted = false;
+ // Ensure we have some default fields
+ EntityModifier.ensureKindExists(insert, source, Phone.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, source, Email.CONTENT_ITEM_TYPE);
- final ContactsSource contactsSource = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_SUMMARY);
- final boolean writable = contactsSource == null || !contactsSource.readOnly;
- if (writable) {
- mWritableSourcesCnt += 1;
- } else {
- mReadOnlySourcesCnt += 1;
- }
-
- final DisplayRawContact rawContact = new DisplayRawContact(mContext, contactsSource,
- accountName, rawContactId, writable, mRawContactFooterListener);
- mRawContacts.add(rawContact);
-
- for (NamedContentValues subValue : entity.getSubValues()) {
- final ContentValues entryValues = subValue.values;
- entryValues.put(Data.RAW_CONTACT_ID, rawContactId);
-
- final long dataId = entryValues.getAsLong(Data._ID);
- final String mimeType = entryValues.getAsString(Data.MIMETYPE);
- if (mimeType == null) continue;
-
- final DataKind kind = sources.getKindOrFallback(accountType, mimeType, mContext,
- ContactsSource.LEVEL_CONSTRAINTS);
- if (kind == null) continue;
-
- if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final StructuredNameViewModel itemEditor =
- StructuredNameViewModel.createForExisting(mContext, rawContact, dataId,
- entryValues, kind.titleRes);
- rawContact.getFields().add(itemEditor);
- continue;
- }
-
- if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final StructuredPostalViewModel itemEditor =
- StructuredPostalViewModel.createForExisting(mContext, rawContact,
- dataId, entryValues, kind.titleRes);
- rawContact.getFields().add(itemEditor);
- continue;
- }
-
- if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final EmailViewModel itemEditor = EmailViewModel.createForExisting(mContext,
- rawContact, dataId, entryValues, kind.titleRes);
- rawContact.getFields().add(itemEditor);
- continue;
- }
-
- if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final ImViewModel itemEditor = ImViewModel.createForExisting(mContext,
- rawContact, dataId, entryValues, kind.titleRes);
- rawContact.getFields().add(itemEditor);
- continue;
- }
-
- if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) ||
- Note.CONTENT_ITEM_TYPE.equals(mimeType) ||
- Website.CONTENT_ITEM_TYPE.equals(mimeType) ||
- Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final GenericViewModel itemEditor = GenericViewModel.fromDataKind(
- mContext, rawContact, dataId, entryValues, kind);
- rawContact.getFields().add(itemEditor);
- continue;
- }
-
- if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final OrganizationViewModel itemEditor =
- OrganizationViewModel.createForExisting(mContext, rawContact, dataId,
- entryValues, kind.titleRes);
- rawContact.getFields().add(itemEditor);
- continue;
- }
-
- if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final PhotoViewModel itemEditor =
- PhotoViewModel.createForExisting(mContext, rawContact, dataId,
- entryValues);
- rawContact.getFields().add(itemEditor);
- continue;
- }
- }
+ if (mState == null) {
+ // Create state if none exists yet
+ mState = EntitySet.fromSingle(insert);
+ } else {
+ // Add contact onto end of existing state
+ mState.add(insert);
}
+
+ bindEditors();
}
- private void createFieldViews() {
- mFieldContainer.removeAllViews();
+ private void bindEditors() {
+ // Sort the editors
+ Collections.sort(mState, mComparator);
- for (int i = 0; i < mRawContacts.size(); i++) {
- final DisplayRawContact rawContact = mRawContacts.get(i);
- // Header
- rawContact.getHeader().createAndAddView(mInflater, mFieldContainer);
+ // Remove any existing editors and rebuild any visible
+ mContent.removeAllViews();
- // Data items
- final ArrayList<BaseViewModel> fields = rawContact.getFields();
- for (int j = 0; j < fields.size(); j++) {
- fields.get(j).createAndAddView(mInflater, mFieldContainer);
+ final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ final Sources sources = Sources.getInstance(mContext);
+ int size = mState.size();
+ for (int i = 0; i < size; i++) {
+ // TODO ensure proper ordering of entities in the list
+ final EntityDelta entity = mState.get(i);
+ final ValuesDelta values = entity.getValues();
+ if (!values.isVisible()) continue;
+
+ final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ final ContactsSource source = sources.getInflatedSource(accountType,
+ ContactsSource.LEVEL_CONSTRAINTS);
+ final long rawContactId = values.getAsLong(RawContacts._ID);
+
+ final BaseContactEditorView editor;
+ if (!source.readOnly) {
+ editor = (BaseContactEditorView) inflater.inflate(R.layout.item_contact_editor,
+ mContent, false);
+ } else {
+ editor = (BaseContactEditorView) inflater.inflate(
+ R.layout.item_read_only_contact_editor, mContent, false);
}
+ final PhotoEditorView photoEditor = editor.getPhotoEditor();
+ photoEditor.setEditorListener(new PhotoListener(rawContactId, source.readOnly,
+ photoEditor));
- // Footer
- rawContact.getFooter().createAndAddView(mInflater, mFieldContainer);
+ mContent.addView(editor);
+ editor.setState(entity, source, mViewIdGenerator);
}
+
+ // Show editor now that we've loaded state
+ mContent.setVisibility(View.VISIBLE);
}
@Override
public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
- inflater.inflate(R.menu.view, menu);
+ inflater.inflate(R.menu.edit, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
- // TODO: Prepare options
+ menu.findItem(R.id.menu_split).setVisible(mState != null && mState.size() > 1);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.menu_edit: {
- // TODO: This is temporary code to invoke the old editor. We can get rid of this
- // later
- final Intent intent = new Intent();
- intent.setClass(mContext, EditContactActivity.class);
- final long rawContactId = mRawContacts.get(0).getId();
- final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
- rawContactId);
- intent.setAction(Intent.ACTION_EDIT);
- intent.setData(rawContactUri);
- startActivity(intent);
- return true;
- }
- case R.id.menu_delete: {
- showDeleteConfirmationDialog();
- return true;
- }
- case R.id.menu_options: {
- final Intent intent = new Intent(mContext, ContactOptionsActivity.class);
- intent.setData(mContactData.getLookupUri());
- mContext.startActivity(intent);
- return true;
- }
- case R.id.menu_share: {
- if (mAllRestricted) return false;
-
- final String lookupKey = mContactData.getLookupKey();
- final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
-
- final Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setType(Contacts.CONTENT_VCARD_TYPE);
- intent.putExtra(Intent.EXTRA_STREAM, shareUri);
-
- // Launch chooser to share contact via
- final CharSequence chooseTitle = mContext.getText(R.string.share_via);
- final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
-
- try {
- mContext.startActivity(chooseIntent);
- } catch (ActivityNotFoundException ex) {
- Toast.makeText(mContext, R.string.share_error, Toast.LENGTH_SHORT).show();
- }
- return true;
- }
+ case R.id.menu_done:
+ return doSaveAction(SAVE_MODE_DEFAULT);
+ case R.id.menu_discard:
+ return doRevertAction();
+ case R.id.menu_add:
+ return doAddAction();
+ case R.id.menu_delete:
+ return doDeleteAction();
+ case R.id.menu_split:
+ return doSplitContactAction();
+ case R.id.menu_join:
+ return doJoinContactAction();
}
return false;
}
- private void showDeleteConfirmationDialog() {
- final int dialogId;
- if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
- dialogId = R.id.detail_dialog_confirm_readonly_delete;
- } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
- dialogId = R.id.detail_dialog_confirm_readonly_hide;
- } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
- dialogId = R.id.detail_dialog_confirm_multiple_delete;
- } else {
- dialogId = R.id.detail_dialog_confirm_delete;
- }
- if (mListener != null) mListener.onDialogRequested(dialogId, null);
+ private boolean doAddAction() {
+ // Load Accounts async so that we can present them
+ AsyncTask<Void, Void, ArrayList<Account>> loadAccountsTask =
+ new AsyncTask<Void, Void, ArrayList<Account>>() {
+ @Override
+ protected ArrayList<Account> doInBackground(Void... params) {
+ return Sources.getInstance(mContext).getAccounts(true);
+ }
+ @Override
+ protected void onPostExecute(ArrayList<Account> result) {
+ selectAccountAndCreateContact(result, true);
+ }
+ };
+ loadAccountsTask.execute();
+
+ return true;
}
- // This was the ListView based code to expand/collapse sections.
-// public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-// if (mListener == null) return;
-// final BaseViewModel baseEntry = mAdapter.getEntry(position);
-// if (baseEntry == null) return;
-//
-// if (baseEntry instanceof HeaderViewModel) {
-// // Toggle rawcontact visibility
-// final HeaderViewModel entry = (HeaderViewModel) baseEntry;
-// entry.setCollapsed(!entry.isCollapsed());
-// mAdapter.notifyDataSetChanged();
-// }
-// }
-
- private final DialogInterface.OnClickListener mDeleteListener =
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- mContext.getContentResolver().delete(mContactData.getLookupUri(), null, null);
+ /**
+ * Delete the entire contact currently being edited, which usually asks for
+ * user confirmation before continuing.
+ */
+ private boolean doDeleteAction() {
+ if (!hasValidState())
+ return false;
+ int readOnlySourcesCnt = 0;
+ int writableSourcesCnt = 0;
+ // TODO: This shouldn't be called from the UI thread
+ final Sources sources = Sources.getInstance(mContext);
+ for (EntityDelta delta : mState) {
+ final String accountType = delta.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ final ContactsSource contactsSource = sources.getInflatedSource(accountType,
+ ContactsSource.LEVEL_CONSTRAINTS);
+ if (contactsSource != null && contactsSource.readOnly) {
+ readOnlySourcesCnt += 1;
+ } else {
+ writableSourcesCnt += 1;
+ }
}
- };
+
+ if (readOnlySourcesCnt > 0 && writableSourcesCnt > 0) {
+ getActivity().showDialog(R.id.edit_dialog_confirm_readonly_delete);
+ } else if (readOnlySourcesCnt > 0 && writableSourcesCnt == 0) {
+ getActivity().showDialog(R.id.edit_dialog_confirm_readonly_hide);
+ } else if (readOnlySourcesCnt == 0 && writableSourcesCnt > 1) {
+ getActivity().showDialog(R.id.edit_dialog_confirm_multiple_delete);
+ } else {
+ getActivity().showDialog(R.id.edit_dialog_confirm_delete);
+ }
+ return true;
+ }
+
+ /**
+ * Pick a specific photo to be added under the currently selected tab.
+ */
+ /* package */ boolean doPickPhotoAction(long rawContactId) {
+ if (!hasValidState()) return false;
+
+ mRawContactIdRequestingPhoto = rawContactId;
+
+ getActivity().showDialog(R.id.edit_dialog_pick_photo);
+ return false;
+ }
public Dialog onCreateDialog(int id, Bundle bundle) {
- // TODO The delete dialogs mirror the functionality from the Contact-Detail-Fragment.
- // Consider whether we can extract common logic here
- // TODO The actual removal is not in a worker thread currently
switch (id) {
- case R.id.detail_dialog_confirm_delete:
+ case R.id.edit_dialog_confirm_delete:
return new AlertDialog.Builder(mContext)
.setTitle(R.string.deleteConfirmation_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.deleteConfirmation)
.setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, mDeleteListener)
+ .setPositiveButton(android.R.string.ok, new DeleteClickListener())
.setCancelable(false)
.create();
- case R.id.detail_dialog_confirm_readonly_delete:
+ case R.id.edit_dialog_confirm_readonly_delete:
return new AlertDialog.Builder(mContext)
.setTitle(R.string.deleteConfirmation_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.readOnlyContactDeleteConfirmation)
.setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, mDeleteListener)
+ .setPositiveButton(android.R.string.ok, new DeleteClickListener())
.setCancelable(false)
.create();
- case R.id.detail_dialog_confirm_multiple_delete:
+ case R.id.edit_dialog_confirm_multiple_delete:
return new AlertDialog.Builder(mContext)
.setTitle(R.string.deleteConfirmation_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.multipleContactDeleteConfirmation)
.setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, mDeleteListener)
+ .setPositiveButton(android.R.string.ok, new DeleteClickListener())
.setCancelable(false)
.create();
- case R.id.detail_dialog_confirm_readonly_hide: {
+ case R.id.edit_dialog_confirm_readonly_hide:
return new AlertDialog.Builder(mContext)
.setTitle(R.string.deleteConfirmation_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.readOnlyContactWarning)
- .setPositiveButton(android.R.string.ok, mDeleteListener)
+ .setPositiveButton(android.R.string.ok, new DeleteClickListener())
+ .setCancelable(false)
.create();
- }
- case R.id.edit_dialog_add_information: {
- final long rawContactId = bundle.getLong(BUNDLE_RAW_CONTACT_ID);
- final DisplayRawContact rawContact = findRawContactById(rawContactId);
- if (rawContact == null) return null;
- final ContactsSource source = rawContact.getSource();
-
- final ArrayList<DataKind> originalDataKinds = source.getSortedDataKinds();
- // We should not modify the result returned from getSortedDataKinds but
- // we have to filter some items out. Therefore we copy items into a new ArrayList
- final ArrayList<DataKind> filteredDataKinds =
- new ArrayList<DataKind>(originalDataKinds.size());
- final ArrayList<String> items = new ArrayList<String>(filteredDataKinds.size());
- for (DataKind dataKind : originalDataKinds) {
- // TODO: Filter out fields that do not make sense in the current Context
- // (Name, Photo, Notes etc)
- if (dataKind.titleRes == -1) continue;
- if (!dataKind.editable) continue;
- final String title = mContext.getString(dataKind.titleRes);
- items.add(title);
- filteredDataKinds.add(dataKind);
- }
- final DialogInterface.OnClickListener itemClickListener =
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- // Create an Intent for the INSERT-Editor. Its data is null
- // and the RawContact is identified in the Extras
- final String rawContactUriString = ContentUris.withAppendedId(
- RawContacts.CONTENT_URI, rawContactId).toString();
- final DataKind dataKind = filteredDataKinds.get(which);
- // TODO: Add new row
-// final Intent intent = new Intent();
-// intent.setType(dataKind.mimeType);
-// intent.setAction(Intent.ACTION_INSERT);
-// intent.putExtra(ContactFieldEditorActivity.BUNDLE_RAW_CONTACT_URI,
-// rawContactUriString);
-// if (mListener != null) mListener.onEditorRequested(intent);
- }
- };
- return new AlertDialog.Builder(mContext)
- .setItems(items.toArray(new String[0]), itemClickListener)
- .create();
- }
+ case R.id.edit_dialog_pick_photo:
+ return createPickPhotoDialog();
+ case R.id.edit_dialog_split:
+ return createSplitDialog();
+ case R.id.edit_dialog_select_account:
+ return createSelectAccountDialog(bundle);
default:
return null;
}
}
- private DisplayRawContact findRawContactById(long rawContactId) {
- for (DisplayRawContact rawContact : mRawContacts) {
- if (rawContact.getId() == rawContactId) return rawContact;
- }
- return null;
+ private Dialog createSelectAccountDialog(Bundle bundle) {
+ final ArrayList<Account> accounts = bundle.getParcelableArrayList(
+ BUNDLE_SELECT_ACCOUNT_LIST);
+ // Wrap our context to inflate list items using correct theme
+ final Context dialogContext = new ContextThemeWrapper(mContext,
+ android.R.style.Theme_Light);
+ final LayoutInflater dialogInflater =
+ (LayoutInflater)dialogContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ final Sources sources = Sources.getInstance(mContext);
+
+ final ArrayAdapter<Account> accountAdapter = new ArrayAdapter<Account>(mContext,
+ android.R.layout.simple_list_item_2, accounts) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = dialogInflater.inflate(android.R.layout.simple_list_item_2,
+ parent, false);
+ }
+
+ // TODO: show icon along with title
+ final TextView text1 = (TextView)convertView.findViewById(android.R.id.text1);
+ final TextView text2 = (TextView)convertView.findViewById(android.R.id.text2);
+
+ final Account account = this.getItem(position);
+ final ContactsSource source = sources.getInflatedSource(account.type,
+ ContactsSource.LEVEL_SUMMARY);
+
+ text1.setText(account.name);
+ text2.setText(source.getDisplayLabel(mContext));
+
+ return convertView;
+ }
+ };
+
+ final DialogInterface.OnClickListener clickListener =
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+
+ // Create new contact based on selected source
+ final Account account = accountAdapter.getItem(which);
+ createContact(account, false);
+ }
+ };
+
+ final DialogInterface.OnCancelListener cancelListener =
+ new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ // If nothing remains, close activity
+ if (!hasValidState()) {
+ mListener.closeBecauseAccountSelectorAborted();
+ }
+ }
+ };
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ builder.setTitle(R.string.dialog_new_contact_account);
+ builder.setSingleChoiceItems(accountAdapter, 0, clickListener);
+ builder.setOnCancelListener(cancelListener);
+ final Dialog result = builder.create();
+ result.setOnDismissListener(new OnDismissListener() {
+ public void onDismiss(DialogInterface dialog) {
+ // TODO: Check if we even need this...seems useless
+ //removeDialog(DIALOG_SELECT_ACCOUNT);
+ }
+ });
+ return result;
}
- private FooterViewModel.Listener mRawContactFooterListener =
- new FooterViewModel.Listener() {
- public void onAddClicked(DisplayRawContact rawContact) {
- // Create a bundle to show the Dialog
- final Bundle bundle = new Bundle();
- bundle.putLong(BUNDLE_RAW_CONTACT_ID, rawContact.getId());
- if (mListener != null) {
- mListener.onDialogRequested(R.id.edit_dialog_add_information, bundle);
+ private boolean doSplitContactAction() {
+ if (!hasValidState()) return false;
+
+ getActivity().showDialog(R.id.edit_dialog_split);
+ return true;
+ }
+
+ private Dialog createSplitDialog() {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ builder.setTitle(R.string.splitConfirmation_title);
+ builder.setIcon(android.R.drawable.ic_dialog_alert);
+ builder.setMessage(R.string.splitConfirmation);
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // Split the contacts
+ mState.splitRawContacts();
+ doSaveAction(SAVE_MODE_SPLIT);
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setCancelable(false);
+ return builder.create();
+ }
+
+ private boolean doJoinContactAction() {
+ return doSaveAction(SAVE_MODE_JOIN);
+ }
+
+ /**
+ * Creates a dialog offering two options: take a photo or pick a photo from the gallery.
+ */
+ private Dialog createPickPhotoDialog() {
+ // Wrap our context to inflate list items using correct theme
+ final Context dialogContext = new ContextThemeWrapper(mContext,
+ android.R.style.Theme_Light);
+
+ String[] choices = new String[2];
+ choices[0] = mContext.getString(R.string.take_photo);
+ choices[1] = mContext.getString(R.string.pick_photo);
+ final ListAdapter adapter = new ArrayAdapter<String>(dialogContext,
+ android.R.layout.simple_list_item_1, choices);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(dialogContext);
+ builder.setTitle(R.string.attachToContact);
+ builder.setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ switch(which) {
+ case 0:
+ doTakePhoto();
+ break;
+ case 1:
+ doPickPhotoFromGallery();
+ break;
+ }
+ }
+ });
+ return builder.create();
+ }
+
+
+ /**
+ * Launches Gallery to pick a photo.
+ */
+ protected void doPickPhotoFromGallery() {
+ try {
+ // Launch picker to choose photo for selected contact
+ final Intent intent = getPhotoPickIntent();
+ getActivity().startActivityForResult(intent,
+ R.id.edit_request_code_photo_picked_with_data);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ /**
+ * Constructs an intent for picking a photo from Gallery, cropping it and returning the bitmap.
+ */
+ public static Intent getPhotoPickIntent() {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
+ intent.setType("image/*");
+ intent.putExtra("crop", "true");
+ intent.putExtra("aspectX", 1);
+ intent.putExtra("aspectY", 1);
+ intent.putExtra("outputX", ICON_SIZE);
+ intent.putExtra("outputY", ICON_SIZE);
+ intent.putExtra("return-data", true);
+ return intent;
+ }
+
+ /**
+ * Check if our internal {@link #mState} is valid, usually checked before
+ * performing user actions.
+ */
+ private boolean hasValidState() {
+ return mState != null && mState.size() > 0;
+ }
+
+ /**
+ * Create a file name for the icon photo using current time.
+ */
+ private String getPhotoFileName() {
+ Date date = new Date(System.currentTimeMillis());
+ SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
+ return dateFormat.format(date) + ".jpg";
+ }
+
+ /**
+ * Launches Camera to take a picture and store it in a file.
+ */
+ protected void doTakePhoto() {
+ try {
+ // Launch camera to take photo for selected contact
+ PHOTO_DIR.mkdirs();
+ mCurrentPhotoFile = new File(PHOTO_DIR, getPhotoFileName());
+ final Intent intent = getTakePickIntent(mCurrentPhotoFile);
+
+ getActivity().startActivityForResult(intent, R.id.edit_request_code_camera_with_data);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ /**
+ * Constructs an intent for capturing a photo and storing it in a temporary file.
+ */
+ public static Intent getTakePickIntent(File f) {
+ Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
+ return intent;
+ }
+
+ /**
+ * Sends a newly acquired photo to Gallery for cropping
+ */
+ protected void doCropPhoto(File f) {
+ try {
+ // Add the image to the media store
+ MediaScannerConnection.scanFile(
+ mContext,
+ new String[] { f.getAbsolutePath() },
+ new String[] { null },
+ null);
+
+ // Launch gallery to crop the photo
+ final Intent intent = getCropImageIntent(Uri.fromFile(f));
+ getActivity().startActivityForResult(intent,
+ R.id.edit_request_code_photo_picked_with_data);
+ } catch (Exception e) {
+ Log.e(TAG, "Cannot crop image", e);
+ Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ /**
+ * Constructs an intent for image cropping.
+ */
+ public static Intent getCropImageIntent(Uri photoUri) {
+ Intent intent = new Intent("com.android.camera.action.CROP");
+ intent.setDataAndType(photoUri, "image/*");
+ intent.putExtra("crop", "true");
+ intent.putExtra("aspectX", 1);
+ intent.putExtra("aspectY", 1);
+ intent.putExtra("outputX", ICON_SIZE);
+ intent.putExtra("outputY", ICON_SIZE);
+ intent.putExtra("return-data", true);
+ return intent;
+ }
+
+ /**
+ * Saves or creates the contact based on the mode, and if successful
+ * finishes the activity.
+ */
+ private boolean doSaveAction(int saveMode) {
+ if (!hasValidState()) {
+ return false;
+ }
+
+ // TODO: Status still needed?
+ //mStatus = STATUS_SAVING;
+ final PersistTask task = new PersistTask(this, saveMode);
+ task.execute(mState);
+
+ return true;
+ }
+
+ private boolean doRevertAction() {
+ if (mListener != null) mListener.closeAfterRevert();
+
+ return true;
+ }
+
+ private void onSaveCompleted(boolean success, int saveMode, Uri contactLookupUri) {
+ switch (saveMode) {
+ case SAVE_MODE_DEFAULT:
+ final Intent resultIntent;
+ final int resultCode;
+ if (success && contactLookupUri != null) {
+ final String requestAuthority = mUri == null ? null : mUri.getAuthority();
+
+ final String legacyAuthority = "contacts";
+
+ resultIntent = new Intent();
+ if (legacyAuthority.equals(requestAuthority)) {
+ // Build legacy Uri when requested by caller
+ final long contactId = ContentUris.parseId(Contacts.lookupContact(
+ mContext.getContentResolver(), contactLookupUri));
+ final Uri legacyContentUri = Uri.parse("content://contacts/people");
+ final Uri legacyUri = ContentUris.withAppendedId(
+ legacyContentUri, contactId);
+ resultIntent.setData(legacyUri);
+ } else {
+ // Otherwise pass back a lookup-style Uri
+ resultIntent.setData(contactLookupUri);
+ }
+
+ resultCode = Activity.RESULT_OK;
+ } else {
+ resultCode = Activity.RESULT_CANCELED;
+ resultIntent = null;
+ }
+ if (mListener != null) mListener.closeAfterSaving(resultCode, resultIntent);
+ break;
+ case SAVE_MODE_SPLIT:
+ if (mListener != null) mListener.closeAfterSplit();
+ break;
+
+ case SAVE_MODE_JOIN:
+ //mStatus = STATUS_EDITING;
+ if (success) {
+ showJoinAggregateActivity(contactLookupUri);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Shows a list of aggregates that can be joined into the currently viewed aggregate.
+ *
+ * @param contactLookupUri the fresh URI for the currently edited contact (after saving it)
+ */
+ private void showJoinAggregateActivity(Uri contactLookupUri) {
+ if (contactLookupUri == null) {
+ return;
+ }
+
+ mContactIdForJoin = ContentUris.parseId(contactLookupUri);
+ final Intent intent = new Intent(JoinContactActivity.JOIN_CONTACT);
+ intent.putExtra(JoinContactActivity.EXTRA_TARGET_CONTACT_ID, mContactIdForJoin);
+ getActivity().startActivityForResult(intent, R.id.edit_request_code_join);
+ }
+
+ private interface JoinContactQuery {
+ String[] PROJECTION = {
+ RawContacts._ID,
+ RawContacts.CONTACT_ID,
+ RawContacts.NAME_VERIFIED,
+ };
+
+ String SELECTION = RawContacts.CONTACT_ID + "=? OR " + RawContacts.CONTACT_ID + "=?";
+
+ int _ID = 0;
+ int CONTACT_ID = 1;
+ int NAME_VERIFIED = 2;
+ }
+
+ /**
+ * Performs aggregation with the contact selected by the user from suggestions or A-Z list.
+ */
+ private void joinAggregate(final long contactId) {
+ final ContentResolver resolver = mContext.getContentResolver();
+
+ // Load raw contact IDs for all raw contacts involved - currently edited and selected
+ // in the join UIs
+ Cursor c = resolver.query(RawContacts.CONTENT_URI,
+ JoinContactQuery.PROJECTION,
+ JoinContactQuery.SELECTION,
+ new String[]{String.valueOf(contactId), String.valueOf(mContactIdForJoin)}, null);
+
+ long rawContactIds[];
+ long verifiedNameRawContactId = -1;
+ try {
+ rawContactIds = new long[c.getCount()];
+ for (int i = 0; i < rawContactIds.length; i++) {
+ c.moveToNext();
+ long rawContactId = c.getLong(JoinContactQuery._ID);
+ rawContactIds[i] = rawContactId;
+ if (c.getLong(JoinContactQuery.CONTACT_ID) == mContactIdForJoin) {
+ if (verifiedNameRawContactId == -1
+ || c.getInt(JoinContactQuery.NAME_VERIFIED) != 0) {
+ verifiedNameRawContactId = rawContactId;
+ }
+ }
+ }
+ } finally {
+ c.close();
+ }
+
+ // For each pair of raw contacts, insert an aggregation exception
+ ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
+ for (int i = 0; i < rawContactIds.length; i++) {
+ for (int j = 0; j < rawContactIds.length; j++) {
+ if (i != j) {
+ buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]);
+ }
}
}
- public void onSeparateClicked(DisplayRawContact rawContact) {
+
+ // Mark the original contact as "name verified" to make sure that the contact
+ // display name does not change as a result of the join
+ Builder builder = ContentProviderOperation.newUpdate(
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, verifiedNameRawContactId));
+ builder.withValue(RawContacts.NAME_VERIFIED, 1);
+ operations.add(builder.build());
+
+ // Apply all aggregation exceptions as one batch
+ try {
+ resolver.applyBatch(ContactsContract.AUTHORITY, operations);
+
+ // We can use any of the constituent raw contacts to refresh the UI - why not the first
+ final Intent intent = new Intent();
+ intent.setData(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactIds[0]));
+
+ // Reload the new state from database
+ // TODO: Reload necessary or do we have a listener?
+ //new QueryEntitiesTask(this).execute(intent);
+
+ Toast.makeText(mContext, R.string.contactsJoinedMessage, Toast.LENGTH_LONG).show();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to apply aggregation exception batch", e);
+ Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
+ } catch (OperationApplicationException e) {
+ Log.e(TAG, "Failed to apply aggregation exception batch", e);
+ Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
}
- public void onDeleteClicked(DisplayRawContact rawContact) {
- }
- };
+ }
+
+ /**
+ * Construct a {@link AggregationExceptions#TYPE_KEEP_TOGETHER} ContentProviderOperation.
+ */
+ private void buildJoinContactDiff(ArrayList<ContentProviderOperation> operations,
+ long rawContactId1, long rawContactId2) {
+ Builder builder =
+ ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
+ builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
+ builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
+ builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
+ operations.add(builder.build());
+ }
public static interface Listener {
/**
* Contact was not found, so somehow close this fragment.
*/
- public void onContactNotFound();
+ void closeBecauseContactNotFound();
/**
- * There was an error loading the contact
+ * Contact was split, so we can close now
*/
- public void onError();
+ void closeAfterSplit();
/**
- * User clicked a single item (e.g. mail) to edit it or is adding a new field
+ * User was presented with an account selection and couldn't decide.
*/
- public void onEditorRequested(Intent intent);
+ void closeBecauseAccountSelectorAborted();
/**
- * Show a dialog using the globally unique id
+ * User has tapped Revert, close the fragment now.
*/
- public void onDialogRequested(int id, Bundle bundle);
+ void closeAfterRevert();
+
+ /**
+ * User has removed the contact, close the fragment now.
+ */
+ void closeAfterDelete();
+
+ /**
+ * Set the Title (e.g. of the Activity)
+ */
+ void setTitleTo(int resourceId);
+
+ /**
+ * Contact was
+ * @param resultCode
+ * @param resultIntent
+ */
+ void closeAfterSaving(int resultCode, Intent resultIntent);
}
-}
+
+ private class EntityDeltaComparator implements Comparator<EntityDelta> {
+ /**
+ * Compare EntityDeltas for sorting the stack of editors.
+ */
+ public int compare(EntityDelta one, EntityDelta two) {
+ // Check direct equality
+ if (one.equals(two)) {
+ return 0;
+ }
+
+ final Sources sources = Sources.getInstance(mContext);
+ String accountType = one.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ final ContactsSource oneSource = sources.getInflatedSource(accountType,
+ ContactsSource.LEVEL_SUMMARY);
+ accountType = two.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ final ContactsSource twoSource = sources.getInflatedSource(accountType,
+ ContactsSource.LEVEL_SUMMARY);
+
+ // Check read-only
+ if (oneSource.readOnly && !twoSource.readOnly) {
+ return 1;
+ } else if (twoSource.readOnly && !oneSource.readOnly) {
+ return -1;
+ }
+
+ // Check account type
+ boolean skipAccountTypeCheck = false;
+ boolean oneIsGoogle = oneSource instanceof GoogleSource;
+ boolean twoIsGoogle = twoSource instanceof GoogleSource;
+ if (oneIsGoogle && !twoIsGoogle) {
+ return -1;
+ } else if (twoIsGoogle && !oneIsGoogle) {
+ return 1;
+ } else if (oneIsGoogle && twoIsGoogle){
+ skipAccountTypeCheck = true;
+ }
+
+ int value;
+ if (!skipAccountTypeCheck) {
+ value = oneSource.accountType.compareTo(twoSource.accountType);
+ if (value != 0) {
+ return value;
+ }
+ }
+
+ // Check account name
+ ValuesDelta oneValues = one.getValues();
+ String oneAccount = oneValues.getAsString(RawContacts.ACCOUNT_NAME);
+ if (oneAccount == null) oneAccount = "";
+ ValuesDelta twoValues = two.getValues();
+ String twoAccount = twoValues.getAsString(RawContacts.ACCOUNT_NAME);
+ if (twoAccount == null) twoAccount = "";
+ value = oneAccount.compareTo(twoAccount);
+ if (value != 0) {
+ return value;
+ }
+
+ // Both are in the same account, fall back to contact ID
+ Long oneId = oneValues.getAsLong(RawContacts._ID);
+ Long twoId = twoValues.getAsLong(RawContacts._ID);
+ if (oneId == null) {
+ return -1;
+ } else if (twoId == null) {
+ return 1;
+ }
+
+ return (int)(oneId - twoId);
+ }
+ }
+
+ /**
+ * Class that listens to requests coming from photo editors
+ */
+ private class PhotoListener implements EditorListener, DialogInterface.OnClickListener {
+ private long mRawContactId;
+ private boolean mReadOnly;
+ private PhotoEditorView mEditor;
+
+ public PhotoListener(long rawContactId, boolean readOnly, PhotoEditorView editor) {
+ mRawContactId = rawContactId;
+ mReadOnly = readOnly;
+ mEditor = editor;
+ }
+
+ public void onDeleted(Editor editor) {
+ // Do nothing
+ }
+
+ public void onRequest(int request) {
+ if (!hasValidState()) return;
+
+ if (request == EditorListener.REQUEST_PICK_PHOTO) {
+ if (mEditor.hasSetPhoto()) {
+ // There is an existing photo, offer to remove, replace, or promoto to primary
+ createPhotoDialog().show();
+ } else if (!mReadOnly) {
+ // No photo set and not read-only, try to set the photo
+ doPickPhotoAction(mRawContactId);
+ }
+ }
+ }
+
+ /**
+ * Prepare dialog for picking a new {@link EditType} or entering a
+ * custom label. This dialog is limited to the valid types as determined
+ * by {@link EntityModifier}.
+ */
+ public Dialog createPhotoDialog() {
+ // Wrap our context to inflate list items using correct theme
+ final Context dialogContext = new ContextThemeWrapper(mContext,
+ android.R.style.Theme_Light);
+
+ String[] choices;
+ if (mReadOnly) {
+ choices = new String[1];
+ choices[0] = mContext.getString(R.string.use_photo_as_primary);
+ } else {
+ choices = new String[3];
+ choices[0] = mContext.getString(R.string.use_photo_as_primary);
+ choices[1] = mContext.getString(R.string.removePicture);
+ choices[2] = mContext.getString(R.string.changePicture);
+ }
+ final ListAdapter adapter = new ArrayAdapter<String>(dialogContext,
+ android.R.layout.simple_list_item_1, choices);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(dialogContext);
+ builder.setTitle(R.string.attachToContact);
+ builder.setSingleChoiceItems(adapter, -1, this);
+ return builder.create();
+ }
+
+ /**
+ * Called when something in the dialog is clicked
+ */
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+
+ switch (which) {
+ case 0:
+ // Set the photo as super primary
+ mEditor.setSuperPrimary(true);
+
+ // And set all other photos as not super primary
+ int count = mContent.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View childView = mContent.getChildAt(i);
+ if (childView instanceof BaseContactEditorView) {
+ BaseContactEditorView editor = (BaseContactEditorView) childView;
+ PhotoEditorView photoEditor = editor.getPhotoEditor();
+ if (!photoEditor.equals(mEditor)) {
+ photoEditor.setSuperPrimary(false);
+ }
+ }
+ }
+ break;
+
+ case 1:
+ // Remove the photo
+ mEditor.setPhotoBitmap(null);
+ break;
+
+ case 2:
+ // Pick a new photo for the contact
+ doPickPhotoAction(mRawContactId);
+ break;
+ }
+ }
+ }
+
+
+ private class DeleteClickListener implements DialogInterface.OnClickListener {
+ public void onClick(DialogInterface dialog, int which) {
+ // TODO: Don't do this from the UI thread
+ final Sources sources = Sources.getInstance(mContext);
+ // Mark all raw contacts for deletion
+ for (EntityDelta delta : mState) {
+ delta.markDeleted();
+ }
+ // Save the deletes
+ doSaveAction(SAVE_MODE_DEFAULT);
+ mListener.closeAfterDelete();
+ }
+ }
+
+ // TODO: There has to be a nicer way than this WeakAsyncTask...? Maybe call a service?
+ /**
+ * Background task for persisting edited contact data, using the changes
+ * defined by a set of {@link EntityDelta}. This task starts
+ * {@link EmptyService} to make sure the background thread can finish
+ * persisting in cases where the system wants to reclaim our process.
+ */
+ public static class PersistTask extends
+ WeakAsyncTask<EntitySet, Void, Integer, ContactEditorFragment> {
+ private static final int PERSIST_TRIES = 3;
+
+ private static final int RESULT_UNCHANGED = 0;
+ private static final int RESULT_SUCCESS = 1;
+ private static final int RESULT_FAILURE = 2;
+
+ private WeakReference<ProgressDialog> mProgress;
+ private final Context mContext;
+
+ private int mSaveMode;
+ private Uri mContactLookupUri = null;
+
+ public PersistTask(ContactEditorFragment target, int saveMode) {
+ super(target);
+ mSaveMode = saveMode;
+ mContext = target.mContext;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onPreExecute(ContactEditorFragment target) {
+ mProgress = new WeakReference<ProgressDialog>(ProgressDialog.show(mContext, null,
+ mContext.getText(R.string.savingContact)));
+
+ // Before starting this task, start an empty service to protect our
+ // process from being reclaimed by the system.
+ mContext.startService(new Intent(mContext, EmptyService.class));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected Integer doInBackground(ContactEditorFragment target, EntitySet... params) {
+ final ContentResolver resolver = mContext.getContentResolver();
+
+ EntitySet state = params[0];
+
+ // Trim any empty fields, and RawContacts, before persisting
+ final Sources sources = Sources.getInstance(mContext);
+ EntityModifier.trimEmpty(state, sources);
+
+ // Attempt to persist changes
+ int tries = 0;
+ Integer result = RESULT_FAILURE;
+ while (tries++ < PERSIST_TRIES) {
+ try {
+ // Build operations and try applying
+ final ArrayList<ContentProviderOperation> diff = state.buildDiff();
+ ContentProviderResult[] results = null;
+ if (!diff.isEmpty()) {
+ results = resolver.applyBatch(ContactsContract.AUTHORITY, diff);
+ }
+
+ final long rawContactId = getRawContactId(state, diff, results);
+ if (rawContactId != -1) {
+ final Uri rawContactUri = ContentUris.withAppendedId(
+ RawContacts.CONTENT_URI, rawContactId);
+
+ // convert the raw contact URI to a contact URI
+ mContactLookupUri = RawContacts.getContactLookupUri(resolver,
+ rawContactUri);
+ }
+ result = (diff.size() > 0) ? RESULT_SUCCESS : RESULT_UNCHANGED;
+ break;
+
+ } catch (RemoteException e) {
+ // Something went wrong, bail without success
+ Log.e(TAG, "Problem persisting user edits", e);
+ break;
+
+ } catch (OperationApplicationException e) {
+ // Version consistency failed, re-parent change and try again
+ Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
+ final EntitySet newState = EntitySet.fromQuery(resolver,
+ target.mQuerySelection, target.mQuerySelectionArgs, null);
+ state = EntitySet.mergeAfter(newState, state);
+ }
+ }
+
+ return result;
+ }
+
+ private long getRawContactId(EntitySet state,
+ final ArrayList<ContentProviderOperation> diff,
+ final ContentProviderResult[] results) {
+ long rawContactId = state.findRawContactId();
+ if (rawContactId != -1) {
+ return rawContactId;
+ }
+
+ // we gotta do some searching for the id
+ final int diffSize = diff.size();
+ for (int i = 0; i < diffSize; i++) {
+ ContentProviderOperation operation = diff.get(i);
+ if (operation.getType() == ContentProviderOperation.TYPE_INSERT
+ && operation.getUri().getEncodedPath().contains(
+ RawContacts.CONTENT_URI.getEncodedPath())) {
+ return ContentUris.parseId(results[i].uri);
+ }
+ }
+ return -1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onPostExecute(ContactEditorFragment target, Integer result) {
+ final ProgressDialog progress = mProgress.get();
+
+ if (result == RESULT_SUCCESS && mSaveMode != SAVE_MODE_JOIN) {
+ Toast.makeText(mContext, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
+ } else if (result == RESULT_FAILURE) {
+ Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
+ }
+
+ if (progress != null) progress.dismiss();
+
+ // Stop the service that was protecting us
+ mContext.stopService(new Intent(mContext, EmptyService.class));
+
+ target.onSaveCompleted(result != RESULT_FAILURE, mSaveMode, mContactLookupUri);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ if (hasValidState()) {
+ // Store entities with modifications
+ outState.putParcelable(KEY_EDIT_STATE, mState);
+ }
+
+ outState.putLong(KEY_RAW_CONTACT_ID_REQUESTING_PHOTO, mRawContactIdRequestingPhoto);
+ outState.putParcelable(KEY_VIEW_ID_GENERATOR, mViewIdGenerator);
+ if (mCurrentPhotoFile != null) {
+ outState.putString(KEY_CURRENT_PHOTO_FILE, mCurrentPhotoFile.toString());
+ }
+ outState.putString(KEY_QUERY_SELECTION, mQuerySelection);
+ outState.putStringArray(KEY_QUERY_SELECTION_ARGS, mQuerySelectionArgs);
+ outState.putLong(KEY_CONTACT_ID_FOR_JOIN, mContactIdForJoin);
+ super.onSaveInstanceState(outState);
+ }
+
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // Ignore failed requests
+ if (resultCode != Activity.RESULT_OK) return;
+ switch (requestCode) {
+ case R.id.edit_request_code_photo_picked_with_data: {
+ BaseContactEditorView requestingEditor = null;
+ for (int i = 0; i < mContent.getChildCount(); i++) {
+ View childView = mContent.getChildAt(i);
+ if (childView instanceof BaseContactEditorView) {
+ BaseContactEditorView editor = (BaseContactEditorView) childView;
+ if (editor.getRawContactId() == mRawContactIdRequestingPhoto) {
+ requestingEditor = editor;
+ break;
+ }
+ }
+ }
+
+ if (requestingEditor != null) {
+ final Bitmap photo = data.getParcelableExtra("data");
+ requestingEditor.setPhotoBitmap(photo);
+ mRawContactIdRequestingPhoto = -1;
+ } else {
+ // The contact that requested the photo is no longer present.
+ // TODO: Show error message
+ }
+
+ break;
+ }
+
+ case R.id.edit_request_code_camera_with_data: {
+ doCropPhoto(mCurrentPhotoFile);
+ break;
+ }
+ case R.id.edit_request_code_join: {
+ if (data != null) {
+ final long contactId = ContentUris.parseId(data.getData());
+ joinAggregate(contactId);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/views/editor/ContactEditorHeaderView.java b/src/com/android/contacts/views/editor/ContactEditorHeaderView.java
deleted file mode 100644
index 18cbf72..0000000
--- a/src/com/android/contacts/views/editor/ContactEditorHeaderView.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.contacts.views.editor;
-
-import com.android.contacts.R;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-/**
- * Header for displaying the title bar in the contact editor
- */
-public class ContactEditorHeaderView extends FrameLayout {
- private static final String TAG = "ContactEditorHeaderView";
-
- private TextView mMergeInfo;
-
- public ContactEditorHeaderView(Context context) {
- this(context, null);
- }
-
- public ContactEditorHeaderView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public ContactEditorHeaderView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- final LayoutInflater inflater =
- (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(R.layout.contact_editor_header_view, this);
-
- mMergeInfo = (TextView) findViewById(R.id.merge_info);
-
- // Start with unmerged
- setMergeInfo(1);
- }
-
- public void setMergeInfo(int count) {
- if (count <= 1) {
- mMergeInfo.setVisibility(GONE);
- } else {
- mMergeInfo.setVisibility(VISIBLE);
- mMergeInfo.setText(
- getResources().getQuantityString(R.plurals.merge_info, count, count));
- }
- }
-}
diff --git a/src/com/android/contacts/views/editor/ContactEditorLoader.java b/src/com/android/contacts/views/editor/ContactEditorLoader.java
new file mode 100644
index 0000000..77c03c0
--- /dev/null
+++ b/src/com/android/contacts/views/editor/ContactEditorLoader.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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.views.editor;
+
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.model.ContactsSource;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityModifier;
+import com.android.contacts.model.EntitySet;
+import com.android.contacts.model.Sources;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Loader;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+
+public class ContactEditorLoader extends Loader<ContactEditorLoader.Result> {
+ private static final String TAG = "ContactEditorLoader";
+
+ private final Uri mLookupUri;
+ private final String mMimeType;
+ private Result mContact;
+ private boolean mDestroyed;
+ private ForceLoadContentObserver mObserver;
+ private final Bundle mIntentExtras;
+
+ public ContactEditorLoader(Context context, Uri lookupUri, String mimeType,
+ Bundle intentExtras) {
+ super(context);
+ mLookupUri = lookupUri;
+ mMimeType = mimeType;
+ mIntentExtras = intentExtras;
+ }
+
+ /**
+ * The result of a load operation. Contains all data necessary to display the contact for
+ * editing.
+ */
+ public static class Result {
+ /**
+ * Singleton instance that represents "No Contact Found"
+ */
+ public static final Result NOT_FOUND = new Result(null);
+
+ private final EntitySet mEntitySet;
+
+ private Result(EntitySet entitySet) {
+ mEntitySet = entitySet;
+ }
+
+ public EntitySet getEntitySet() {
+ return mEntitySet;
+ }
+ }
+
+ private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
+ @Override
+ protected Result doInBackground(Void... params) {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Uri uriCurrentFormat = ensureIsContactUri(resolver, mLookupUri);
+
+ // Handle both legacy and new authorities
+
+ final long contactId;
+ final String selection = "0";
+ if (Contacts.CONTENT_ITEM_TYPE.equals(mMimeType)) {
+ // Handle selected aggregate
+ contactId = ContentUris.parseId(uriCurrentFormat);
+ } else if (RawContacts.CONTENT_ITEM_TYPE.equals(mMimeType)) {
+ // Get id of corresponding aggregate
+ final long rawContactId = ContentUris.parseId(uriCurrentFormat);
+ contactId = ContactsUtils.queryForContactId(resolver, rawContactId);
+ } else throw new IllegalStateException();
+
+ return new Result(EntitySet.fromQuery(resolver, RawContacts.CONTACT_ID + "=?",
+ new String[] { String.valueOf(contactId) }, null));
+ }
+
+ /**
+ * Transforms the given Uri and returns a Lookup-Uri that represents the contact.
+ * For legacy contacts, a raw-contact lookup is performed.
+ */
+ private Uri ensureIsContactUri(final ContentResolver resolver, final Uri uri) {
+ if (uri == null) throw new IllegalArgumentException("uri must not be null");
+
+ final String authority = uri.getAuthority();
+
+ // Current Style Uri?
+ if (ContactsContract.AUTHORITY.equals(authority)) {
+ final String type = resolver.getType(uri);
+ // Contact-Uri? Good, return it
+ if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
+ return uri;
+ }
+
+ // RawContact-Uri? Transform it to ContactUri
+ if (RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
+ final long rawContactId = ContentUris.parseId(uri);
+ return RawContacts.getContactLookupUri(getContext().getContentResolver(),
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
+ }
+
+ // Anything else? We don't know what this is
+ throw new IllegalArgumentException("uri format is unknown");
+ }
+
+ // Legacy Style? Convert to RawContact
+ final String OBSOLETE_AUTHORITY = "contacts";
+ if (OBSOLETE_AUTHORITY.equals(authority)) {
+ // Legacy Format. Convert to RawContact-Uri and then lookup the contact
+ final long rawContactId = ContentUris.parseId(uri);
+ return RawContacts.getContactLookupUri(resolver,
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
+ }
+
+ throw new IllegalArgumentException("uri authority is unknown");
+ }
+
+ @Override
+ protected void onPostExecute(Result result) {
+ super.onPostExecute(result);
+
+ // TODO: This merging of extras is probably wrong on subsequent loads
+
+ // Load edit details in background
+ final Sources sources = Sources.getInstance(getContext());
+
+ // Handle any incoming values that should be inserted
+ final boolean hasExtras = mIntentExtras != null && mIntentExtras.size() > 0;
+ final boolean hasState = result.getEntitySet().size() > 0;
+ if (hasExtras && hasState) {
+ // Find source defining the first RawContact found
+ final EntityDelta state = result.getEntitySet().get(0);
+ final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ final ContactsSource source = sources.getInflatedSource(accountType,
+ ContactsSource.LEVEL_CONSTRAINTS);
+ EntityModifier.parseExtras(getContext(), source, state, mIntentExtras);
+ }
+
+ // The creator isn't interested in any further updates
+ if (mDestroyed) {
+ return;
+ }
+
+ mContact = result;
+ if (result != null) {
+ if (mObserver == null) {
+ mObserver = new ForceLoadContentObserver();
+ }
+ // TODO: Do we want a content observer here?
+// Log.i(TAG, "Registering content observer for " + mLookupUri);
+// getContext().getContentResolver().registerContentObserver(mLookupUri, true,
+// mObserver);
+ deliverResult(result);
+ }
+ }
+ }
+
+ @Override
+ public void startLoading() {
+ if (mContact != null) {
+ deliverResult(mContact);
+ } else {
+ forceLoad();
+ }
+ }
+
+ @Override
+ public void forceLoad() {
+ LoadContactTask task = new LoadContactTask();
+ task.execute((Void[])null);
+ }
+
+ @Override
+ public void stopLoading() {
+ mContact = null;
+ if (mObserver != null) {
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
+ }
+ }
+
+ @Override
+ public void destroy() {
+ mContact = null;
+ mDestroyed = true;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/views/editor/DisplayRawContact.java b/src/com/android/contacts/views/editor/DisplayRawContact.java
deleted file mode 100644
index 0e6564b..0000000
--- a/src/com/android/contacts/views/editor/DisplayRawContact.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor;
-
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.views.editor.viewModel.BaseViewModel;
-import com.android.contacts.views.editor.viewModel.FooterViewModel;
-import com.android.contacts.views.editor.viewModel.HeaderViewModel;
-
-import android.content.Context;
-
-import java.util.ArrayList;
-
-public class DisplayRawContact {
- private final ContactsSource mSource;
- private String mAccountName;
- private final long mId;
- private boolean mWritable;
- private final HeaderViewModel mHeader;
- private final FooterViewModel mFooter;
- private final ArrayList<BaseViewModel> mFields = new ArrayList<BaseViewModel>();
-
- public DisplayRawContact(Context context, ContactsSource source, String accountName, long id,
- boolean writable, FooterViewModel.Listener footerListener) {
- mSource = source;
- mAccountName = accountName;
- mId = id;
- mWritable = writable;
- mHeader = new HeaderViewModel(context, this);
- mFooter = new FooterViewModel(context, this, footerListener);
- }
-
- public ContactsSource getSource() {
- return mSource;
- }
-
- public String getAccountName() {
- return mAccountName;
- }
-
- public long getId() {
- return mId;
- }
-
- public boolean isWritable() {
- return mWritable;
- }
-
- public ArrayList<BaseViewModel> getFields() {
- return mFields;
- }
-
- public HeaderViewModel getHeader() {
- return mHeader;
- }
-
- public FooterViewModel getFooter() {
- return mFooter;
- }
-}
diff --git a/src/com/android/contacts/views/editor/view/EditorItemView.java b/src/com/android/contacts/views/editor/view/EditorItemView.java
deleted file mode 100644
index 5d22d62..0000000
--- a/src/com/android/contacts/views/editor/view/EditorItemView.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.view;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class EditorItemView extends LinearLayout implements OnClickListener, OnFocusChangeListener {
- private final int CAPTION_WIDTH_DIP = 70;
- private final int TYPE_BUTTON_WIDTH_DIP = 50;
-
- private TextView mCaptionTextView;
- private LinearLayout mFieldContainer;
- private Button mTypeButton;
-
- private Listener mListener;
-
- private int[] mTypeResIds;
- private int mCustomTypeIndex;
-
- private boolean mHasFocus;
-
- public EditorItemView(Context context) {
- super(context);
- createEmptyLayout();
- }
-
- public EditorItemView(Context context, AttributeSet attrs) {
- super(context, attrs);
- createEmptyLayout();
- }
-
- public EditorItemView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- createEmptyLayout();
- }
-
- private void createEmptyLayout() {
- final int captionWidthPixels =
- (int) (getResources().getDisplayMetrics().density * CAPTION_WIDTH_DIP);
-
- // Caption
- mCaptionTextView = new TextView(getContext());
- mCaptionTextView.setLayoutParams(new LinearLayout.LayoutParams(captionWidthPixels,
- LayoutParams.WRAP_CONTENT));
- addView(mCaptionTextView);
-
- // Text Fields
- mFieldContainer = new LinearLayout(getContext());
- mFieldContainer.setOrientation(LinearLayout.VERTICAL);
- mFieldContainer.setLayoutParams(new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT,
- 1.0f));
- addView(mFieldContainer);
- }
-
- /**
- * Configures the View. This function must only be called once after construction
- * @param captionResId
- * The caption of this item
- * @param fieldResIds
- * Ressource Ids of the editable fields
- * @param typeResIds
- * A list of user selectable type-ressource-ids. If this parameter is null,
- * no type can be selected
- * @param customTypeIndex
- * The index of the type representing "Custom" (allowing the user to type
- * a custom type. If this is -1, no type is custom. If types is null, this
- * parameter is ignored.
- */
- public void configure(int captionResId, int[] fieldResIds, int[] typeResIds,
- int customTypeIndex) {
- mCaptionTextView.setText(captionResId);
-
- // Create Fields
- for (int fieldResId : fieldResIds) {
- final EditText fieldEditText = new EditText(getContext());
- fieldEditText.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.WRAP_CONTENT));
- fieldEditText.setContentDescription(getResources().getString(fieldResId));
- fieldEditText.setOnFocusChangeListener(this);
- mFieldContainer.addView(fieldEditText);
- }
-
- // Configure Type Button
- mCustomTypeIndex = customTypeIndex;
- mTypeResIds = typeResIds;
- if (typeResIds != null) {
- final int typeButtonWidthPixels =
- (int) (getResources().getDisplayMetrics().density * TYPE_BUTTON_WIDTH_DIP);
- mTypeButton = new Button(getContext());
- mTypeButton.setLayoutParams(new LinearLayout.LayoutParams(typeButtonWidthPixels,
- LayoutParams.WRAP_CONTENT));
- }
- }
-
- public void setFieldValue(int index, CharSequence value) {
- final EditText editText = (EditText) mFieldContainer.getChildAt(index);
- editText.setText(value);
- }
-
- public String getFieldValue(int index) {
- final EditText editText = (EditText) mFieldContainer.getChildAt(index);
- final CharSequence resultCharSequence = editText.getText();
- if (resultCharSequence == null) return "";
- return resultCharSequence.toString();
- }
-
- public void setType(int typeValue, String labelValue) {
- if (mTypeButton == null) {
- return;
- }
- if (typeValue == mCustomTypeIndex) {
- mTypeButton.setText(labelValue);
- } else {
- final int typeResId = mTypeResIds[typeValue];
- mTypeButton.setText(getResources().getString(typeResId));
- }
- }
-
- @Override
- public void onClick(View v) {
- if (v == mTypeButton) {
-
- return;
- }
- }
-
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- // Focus was gained, lost etc. We should only fire an event if we lost focus to another
- // control
- if (mHasFocus && !hasFocus && mListener != null) {
- mListener.onFocusLost();
- }
- mHasFocus = hasFocus;
- }
-
- public void setListener(Listener value) {
- mListener = value;
- }
-
- public interface Listener {
- /**
- * Called when the user has changed the Type.
- * @param newIndex
- * The index of the newly selected type (corresponds to the array passed as
- * typeResIds in configure.
- * @param customText
- * If this is the type "Custom", this field contains the user-entered text.
- * Otherwise this parameter is null
- */
- void onTypeChanged(int newIndex, String customText);
-
- /**
- * Called when the user has navigated away from this editor (this is not raised if
- * the focus switches between fields of the same editor).
- */
- void onFocusLost();
- }
-}
diff --git a/src/com/android/contacts/views/editor/view/FieldAndTypeView.java b/src/com/android/contacts/views/editor/view/FieldAndTypeView.java
deleted file mode 100644
index 69e35b3..0000000
--- a/src/com/android/contacts/views/editor/view/FieldAndTypeView.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.view;
-
-import com.android.contacts.R;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class FieldAndTypeView extends LinearLayout {
- private TextView mCaptionTextView;
- private EditText mFieldEditText;
- private Button mTypeButton;
- private Listener mListener;
- private boolean mHasFocus;
-
- public FieldAndTypeView(Context context) {
- super(context);
- }
-
- public FieldAndTypeView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public FieldAndTypeView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public static FieldAndTypeView inflate(LayoutInflater inflater, ViewGroup parent,
- boolean attachToRoot) {
- return (FieldAndTypeView) inflater.inflate(R.layout.list_edit_item_field_and_type,
- parent, attachToRoot);
- }
-
- public void setListener(Listener value) {
- mListener = value;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mCaptionTextView = (TextView) findViewById(R.id.caption);
- mFieldEditText = (EditText) findViewById(R.id.field);
- mFieldEditText.setOnFocusChangeListener(mFieldEditTextFocusChangeListener);
- mTypeButton = (Button) findViewById(R.id.type);
- }
-
- public void setLabelText(int resId) {
- mCaptionTextView.setText(resId);
- }
-
- public void setFieldValue(CharSequence value) {
- mFieldEditText.setText(value);
- }
-
- public CharSequence getFieldValue() {
- return mFieldEditText.getText();
- }
-
- public void setTypeDisplayLabel(CharSequence type) {
- mTypeButton.setText(type);
- }
-
- private OnFocusChangeListener mFieldEditTextFocusChangeListener = new OnFocusChangeListener() {
- public void onFocusChange(View v, boolean hasFocus) {
- if (mHasFocus && !hasFocus && mListener != null) {
- mListener.onFocusLost(FieldAndTypeView.this);
- }
- mHasFocus = hasFocus;
- }
- };
-
- public interface Listener {
- void onFocusLost(FieldAndTypeView view);
- }
-}
diff --git a/src/com/android/contacts/views/editor/view/FooterView.java b/src/com/android/contacts/views/editor/view/FooterView.java
deleted file mode 100644
index adc47cd..0000000
--- a/src/com/android/contacts/views/editor/view/FooterView.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.view;
-
-import com.android.contacts.R;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.LinearLayout;
-
-public class FooterView extends LinearLayout {
- private Button mAddInformationButton;
- private Button mSeparateButton;
- private Button mDeleteButton;
- private Listener mListener;
-
- public FooterView(Context context) {
- super(context);
- }
-
- public FooterView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public FooterView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public static FooterView inflate(LayoutInflater inflater, ViewGroup parent,
- boolean attachToRoot) {
- return (FooterView) inflater.inflate(R.layout.list_edit_item_footer, parent, attachToRoot);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mAddInformationButton = (Button) findViewById(R.id.add_information);
- mAddInformationButton.setOnClickListener(mClickListener);
-
- mSeparateButton = (Button) findViewById(R.id.separate);
- mSeparateButton.setOnClickListener(mClickListener);
-
- mDeleteButton = (Button) findViewById(R.id.delete);
- mDeleteButton.setOnClickListener(mClickListener);
- }
-
- public void setListener(Listener value) {
- mListener = value;
- }
-
- private OnClickListener mClickListener = new OnClickListener() {
- public void onClick(View v) {
- if (mListener == null) return;
- switch (v.getId()) {
- case R.id.add_information:
- mListener.onAddClicked();
- break;
- case R.id.separate:
- mListener.onSeparateClicked();
- break;
- case R.id.delete:
- mListener.onDeleteClicked();
- break;
- }
- }
- };
-
- public static interface Listener {
- void onAddClicked();
- void onSeparateClicked();
- void onDeleteClicked();
- }
-}
diff --git a/src/com/android/contacts/views/editor/view/HeaderView.java b/src/com/android/contacts/views/editor/view/HeaderView.java
deleted file mode 100644
index 6822520..0000000
--- a/src/com/android/contacts/views/editor/view/HeaderView.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.view;
-
-import com.android.contacts.R;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class HeaderView extends LinearLayout {
- private ImageView mLogoImageView;
- private TextView mCaptionTextView;
-
- public HeaderView(Context context) {
- super(context);
- }
-
- public HeaderView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public HeaderView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public static HeaderView inflate(LayoutInflater inflater, ViewGroup parent,
- boolean attachToRoot) {
- return (HeaderView) inflater.inflate(R.layout.list_edit_item_header, parent, attachToRoot);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mLogoImageView = (ImageView) findViewById(R.id.logo);
- mCaptionTextView = (TextView) findViewById(R.id.caption);
- }
-
- public void setCaptionText(String value) {
- mCaptionTextView.setText(value);
- }
-
- public void setLogo(Drawable value) {
- mLogoImageView.setImageDrawable(value);
- }
-}
diff --git a/src/com/android/contacts/views/editor/view/OrganizationView.java b/src/com/android/contacts/views/editor/view/OrganizationView.java
deleted file mode 100644
index 53b400f..0000000
--- a/src/com/android/contacts/views/editor/view/OrganizationView.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.view;
-
-import com.android.contacts.R;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class OrganizationView extends LinearLayout {
- private TextView mCaptionTextView;
- private EditText mCompanyFieldEditText;
- private EditText mTitleFieldEditText;
- private Button mTypeButton;
- private Listener mListener;
- private boolean mHasFocus;
-
- public OrganizationView(Context context) {
- super(context);
- }
-
- public OrganizationView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public OrganizationView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public static OrganizationView inflate(LayoutInflater inflater, ViewGroup parent,
- boolean attachToRoot) {
- return (OrganizationView) inflater.inflate(R.layout.list_edit_item_organization, parent,
- attachToRoot);
- }
-
- public void setListener(Listener value) {
- mListener = value;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mCaptionTextView = (TextView) findViewById(R.id.caption);
- mCompanyFieldEditText = (EditText) findViewById(R.id.company_field);
- mCompanyFieldEditText.setOnFocusChangeListener(mFieldEditTextFocusChangeListener);
- mTitleFieldEditText = (EditText) findViewById(R.id.title_field);
- mTitleFieldEditText.setOnFocusChangeListener(mFieldEditTextFocusChangeListener);
- mTypeButton = (Button) findViewById(R.id.type);
- }
-
- public void setLabelText(int resId) {
- mCaptionTextView.setText(resId);
- }
-
- public void setFieldValues(CharSequence company, CharSequence title) {
- mCompanyFieldEditText.setText(company);
- mTitleFieldEditText.setText(title);
- }
-
- public CharSequence getCompanyFieldValue() {
- return mCompanyFieldEditText.getText();
- }
-
- public CharSequence getTitleFieldValue() {
- return mTitleFieldEditText.getText();
- }
-
- public void setTypeDisplayLabel(CharSequence type) {
- mTypeButton.setText(type);
- }
-
- private OnFocusChangeListener mFieldEditTextFocusChangeListener = new OnFocusChangeListener() {
- public void onFocusChange(View v, boolean hasFocus) {
- boolean anyHasFocus = mCompanyFieldEditText.hasFocus()
- || mTitleFieldEditText.hasFocus();
- if (mHasFocus && !anyHasFocus && mListener != null) {
- mListener.onFocusLost(OrganizationView.this);
- }
- mHasFocus = anyHasFocus;
- }
- };
-
- public interface Listener {
- void onFocusLost(OrganizationView view);
- }
-}
diff --git a/src/com/android/contacts/views/editor/view/PhotoView.java b/src/com/android/contacts/views/editor/view/PhotoView.java
deleted file mode 100644
index d43e90e..0000000
--- a/src/com/android/contacts/views/editor/view/PhotoView.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.view;
-
-import com.android.contacts.R;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-public class PhotoView extends LinearLayout {
- private ImageView mPhotoImageView;
- private ImageView mTakePhotoActionButton;
- private ImageView mGalleryActionButton;
- private Listener mListener;
-
- public PhotoView(Context context) {
- super(context);
- }
-
- public PhotoView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public PhotoView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public static PhotoView inflate(LayoutInflater inflater, ViewGroup parent,
- boolean attachToRoot) {
- return (PhotoView) inflater.inflate(R.layout.list_edit_item_photo, parent, attachToRoot);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mPhotoImageView = (ImageView) findViewById(R.id.photo);
-
- mTakePhotoActionButton = (ImageView) findViewById(R.id.action_icon);
- mTakePhotoActionButton.setOnClickListener(mClickListener);
-
- mGalleryActionButton = (ImageView) findViewById(R.id.secondary_action_button);
- mGalleryActionButton.setOnClickListener(mClickListener);
- }
-
- public void setListener(Listener value) {
- mListener = value;
- }
-
- public void setPhoto(Bitmap value) {
- mPhotoImageView.setImageBitmap(value);
- }
-
- private OnClickListener mClickListener = new OnClickListener() {
- public void onClick(View v) {
- if (mListener == null) return;
- switch (v.getId()) {
- case R.id.action_icon:
- mListener.onTakePhotoClicked();
- break;
- case R.id.secondary_action_button:
- mListener.onChooseFromGalleryClicked();
- break;
- }
- }
- };
-
- public static interface Listener {
- void onTakePhotoClicked();
- void onChooseFromGalleryClicked();
- }
-}
diff --git a/src/com/android/contacts/views/editor/view/SimpleOrStructuredView.java b/src/com/android/contacts/views/editor/view/SimpleOrStructuredView.java
deleted file mode 100644
index 2bd0bd0..0000000
--- a/src/com/android/contacts/views/editor/view/SimpleOrStructuredView.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.view;
-
-import com.android.contacts.R;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class SimpleOrStructuredView extends LinearLayout {
- private TextView mCaptionTextView;
- private EditText mFieldEditText;
- private Button mStructuredEditorButton;
- private Listener mListener;
- private boolean mHasFocus;
-
- public SimpleOrStructuredView(Context context) {
- super(context);
- }
-
- public SimpleOrStructuredView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public SimpleOrStructuredView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public static SimpleOrStructuredView inflate(LayoutInflater inflater, ViewGroup parent,
- boolean attachToRoot) {
- return (SimpleOrStructuredView) inflater.inflate(
- R.layout.list_edit_item_simple_or_structured, parent, attachToRoot);
- }
-
- public void setListener(Listener value) {
- mListener = value;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mCaptionTextView = (TextView) findViewById(R.id.caption);
- mFieldEditText = (EditText) findViewById(R.id.field);
- mFieldEditText.setOnFocusChangeListener(mFieldEditTextFocusChangeListener);
- mStructuredEditorButton = (Button) findViewById(R.id.structuredEditorButton);
- mStructuredEditorButton.setOnClickListener(mFullEditorClickListener);
- }
-
- public void setLabelText(int resId) {
- mCaptionTextView.setText(resId);
- }
-
- public void setDisplayName(CharSequence value) {
- mFieldEditText.setText(value);
- }
-
- public CharSequence getDisplayName() {
- return mFieldEditText.getText();
- }
-
- private OnFocusChangeListener mFieldEditTextFocusChangeListener = new OnFocusChangeListener() {
- public void onFocusChange(View v, boolean hasFocus) {
- if (mHasFocus && !hasFocus && mListener != null) {
- mListener.onFocusLost(SimpleOrStructuredView.this);
- }
- mHasFocus = hasFocus;
- }
- };
-
- private OnClickListener mFullEditorClickListener = new OnClickListener() {
- public void onClick(View v) {
- if (mListener != null) {
- mListener.onStructuredEditorRequested(SimpleOrStructuredView.this);
- }
- }
- };
-
- public interface Listener {
- void onFocusLost(SimpleOrStructuredView view);
- void onStructuredEditorRequested(SimpleOrStructuredView view);
- }
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/BaseViewModel.java b/src/com/android/contacts/views/editor/viewModel/BaseViewModel.java
deleted file mode 100644
index 3e5a4f4..0000000
--- a/src/com/android/contacts/views/editor/viewModel/BaseViewModel.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.views.editor.DisplayRawContact;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-public abstract class BaseViewModel {
- private final DisplayRawContact mRawContact;
- private final Context mContext;
-
- public BaseViewModel(Context context, DisplayRawContact rawContact) {
- if (context == null) throw new IllegalArgumentException("context must not be null");
- if (rawContact == null) throw new IllegalArgumentException("rawContact must not be null");
- mContext = context;
- mRawContact = rawContact;
- }
-
- public DisplayRawContact getRawContact() {
- return mRawContact;
- }
-
- public Context getContext() {
- return mContext;
- }
-
- public abstract View createAndAddView(LayoutInflater inflater, ViewGroup parent);
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/DataViewModel.java b/src/com/android/contacts/views/editor/viewModel/DataViewModel.java
deleted file mode 100644
index 23e98e4..0000000
--- a/src/com/android/contacts/views/editor/viewModel/DataViewModel.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.views.ContactSaveService;
-import com.android.contacts.views.editor.DisplayRawContact;
-import com.android.internal.util.ArrayUtils;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ContentProviderOperation.Builder;
-import android.net.Uri;
-import android.provider.ContactsContract.Data;
-
-import java.util.ArrayList;
-
-public abstract class DataViewModel extends BaseViewModel {
- private final long mDataId;
- private final ContentValues mContentValues;
- private final String mMimeType;
- private final Uri mDataUri;
-
- protected DataViewModel(Context context, DisplayRawContact rawContact, long dataId,
- ContentValues contentValues, String mimeType) {
- super(context, rawContact);
- mDataId = dataId;
- mContentValues = contentValues;
- mMimeType = mimeType;
- mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, mDataId);
- }
-
- public long getDataId() {
- return mDataId;
- }
-
- public Uri getDataUri() {
- return mDataUri;
- }
-
- protected ContentValues getContentValues() {
- return mContentValues;
- }
-
- public String getMimeType() {
- return mMimeType;
- }
-
- /**
- * Uncoditionally saves the current state to the database. No difference analysis is performed
- */
- public void saveData() {
- final ContentResolver resolver = getContext().getContentResolver();
-
- final ArrayList<ContentProviderOperation> operations =
- new ArrayList<ContentProviderOperation>();
-
- final Builder builder;
- if (getDataUri() == null) {
- // INSERT
- builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
- builder.withValue(Data.MIMETYPE, getMimeType());
- builder.withValue(Data.RAW_CONTACT_ID, getRawContact().getId());
- writeToBuilder(builder, true);
- } else {
- // UPDATE
- builder = ContentProviderOperation.newUpdate(getDataUri());
- writeToBuilder(builder, false);
- }
- operations.add(builder.build());
-
- // Tell the Service to save
- // TODO: Handle the case where the data element has been removed in the background
- final Intent serviceIntent = new Intent();
- final ContentProviderOperation[] operationsArray =
- operations.toArray(ArrayUtils.emptyArray(ContentProviderOperation.class));
- serviceIntent.putExtra(ContactSaveService.EXTRA_OPERATIONS, operationsArray);
- serviceIntent.setClass(getContext().getApplicationContext(), ContactSaveService.class);
-
- getContext().startService(serviceIntent);
- }
-
- protected abstract void writeToBuilder(final Builder builder, boolean isInsert);
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/EmailViewModel.java b/src/com/android/contacts/views/editor/viewModel/EmailViewModel.java
deleted file mode 100644
index bcc4397..0000000
--- a/src/com/android/contacts/views/editor/viewModel/EmailViewModel.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.views.editor.DisplayRawContact;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-
-public class EmailViewModel extends FieldAndTypeViewModel {
- private EmailViewModel(Context context, DisplayRawContact rawContact, long dataId,
- ContentValues contentValues, int titleResId) {
- super(context, rawContact, dataId, contentValues, Email.CONTENT_ITEM_TYPE, titleResId,
- Email.ADDRESS, Email.TYPE, Email.LABEL);
- }
-
- public static EmailViewModel createForExisting(Context context, DisplayRawContact rawContact,
- long dataId, ContentValues contentValues, int titleResId) {
- return new EmailViewModel(context, rawContact, dataId, contentValues, titleResId);
- }
-
- @Override
- protected CharSequence getTypeDisplayLabel() {
- return Email.getTypeLabel(getContext().getResources(), getType(), getLabel());
- }
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/FieldAndTypeViewModel.java b/src/com/android/contacts/views/editor/viewModel/FieldAndTypeViewModel.java
deleted file mode 100644
index 3ca11ae..0000000
--- a/src/com/android/contacts/views/editor/viewModel/FieldAndTypeViewModel.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.views.editor.DisplayRawContact;
-import com.android.contacts.views.editor.view.FieldAndTypeView;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.ContentProviderOperation.Builder;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-public abstract class FieldAndTypeViewModel extends DataViewModel {
- private static final String TAG = "FieldAndTypeViewModel";
-
- private final int mLabelResId;
- private final String mFieldColumn;
- private final String mLabelColumn;
- private final String mTypeColumn;
-
- protected FieldAndTypeViewModel(Context context, DisplayRawContact rawContact,
- long dataId, ContentValues contentValues, String mimeType, int labelResId,
- String fieldColumn, String typeColumn, String labelColumn) {
- super(context, rawContact, dataId, contentValues, mimeType);
- mLabelResId = labelResId;
-
- mFieldColumn = fieldColumn;
- mTypeColumn = typeColumn;
- mLabelColumn = labelColumn;
- }
-
- @Override
- public FieldAndTypeView createAndAddView(LayoutInflater inflater, ViewGroup parent) {
- final FieldAndTypeView result = FieldAndTypeView.inflate(inflater, parent, false);
-
- result.setListener(mViewListener);
- result.setLabelText(mLabelResId);
- result.setFieldValue(getFieldValue());
- result.setTypeDisplayLabel(getTypeDisplayLabel());
-
- parent.addView(result);
- return result;
- }
-
- @Override
- protected void writeToBuilder(Builder builder, boolean isInsert) {
- builder.withValue(mFieldColumn, getFieldValue());
- builder.withValue(mTypeColumn, getType());
- builder.withValue(mLabelColumn, getLabel());
- }
-
- protected String getFieldValue() {
- return getContentValues().getAsString(mFieldColumn);
- }
-
- protected void putFieldValue(String value) {
- getContentValues().put(mFieldColumn, value);
- }
-
- protected int getType() {
- return getContentValues().getAsInteger(mTypeColumn).intValue();
- }
-
- protected void putType(int value) {
- getContentValues().put(mTypeColumn, value);
- }
-
- protected String getLabel() {
- return getContentValues().getAsString(mLabelColumn);
- }
-
- protected void putLabel(String value) {
- getContentValues().put(mLabelColumn, value);
- }
-
- protected abstract CharSequence getTypeDisplayLabel();
-
- private FieldAndTypeView.Listener mViewListener = new FieldAndTypeView.Listener() {
- public void onFocusLost(FieldAndTypeView view) {
- Log.v(TAG, "Received FocusLost. Checking for changes");
- boolean hasChanged = false;
-
- final String oldValue = getFieldValue();
- final String newValue = view.getFieldValue().toString();
- if (!TextUtils.equals(oldValue, newValue)) {
- putFieldValue(newValue);
- hasChanged = true;
- }
- if (hasChanged) {
- Log.v(TAG, "Found changes. Updating DB");
- saveData();
- }
- }
- };
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/FooterViewModel.java b/src/com/android/contacts/views/editor/viewModel/FooterViewModel.java
deleted file mode 100644
index df254fc..0000000
--- a/src/com/android/contacts/views/editor/viewModel/FooterViewModel.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.views.editor.DisplayRawContact;
-import com.android.contacts.views.editor.view.FooterView;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-public class FooterViewModel extends BaseViewModel {
- private final Listener mListener;
-
- public FooterViewModel(Context context, DisplayRawContact rawContact, Listener listener) {
- super(context, rawContact);
- if (listener == null) throw new IllegalArgumentException("listener must not be null");
- mListener = listener;
- }
-
- @Override
- public View createAndAddView(LayoutInflater inflater, ViewGroup parent) {
- final FooterView result = FooterView.inflate(inflater, parent, false);
-
- result.setListener(mViewListener);
- parent.addView(result);
- return result;
- }
-
- private FooterView.Listener mViewListener = new FooterView.Listener() {
- public void onAddClicked() {
- if (mListener != null) mListener.onAddClicked(getRawContact());
- }
-
- public void onSeparateClicked() {
- if (mListener != null) mListener.onAddClicked(getRawContact());
- }
-
- public void onDeleteClicked() {
- if (mListener != null) mListener.onAddClicked(getRawContact());
- }
- };
-
- public interface Listener {
- public void onAddClicked(DisplayRawContact rawContact);
- public void onSeparateClicked(DisplayRawContact rawContact);
- public void onDeleteClicked(DisplayRawContact rawContact);
- }
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/GenericViewModel.java b/src/com/android/contacts/views/editor/viewModel/GenericViewModel.java
deleted file mode 100644
index 5817876..0000000
--- a/src/com/android/contacts/views/editor/viewModel/GenericViewModel.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.ContactsSource.EditField;
-import com.android.contacts.model.ContactsSource.EditType;
-import com.android.contacts.views.editor.DisplayRawContact;
-import com.android.contacts.views.editor.view.EditorItemView;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.ContentProviderOperation.Builder;
-import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-public class GenericViewModel extends DataViewModel {
- private static final String TAG = "GenericViewModel";
-
- private final int mLabelResId;
- private final Field[] mFields;
- private final int[] mTypeResIds;
- private final int mCustomTypeIndex;
- private final String mTypeColumn;
- private final String mLabelColumn;
-
- private EditorItemView mEditorItemView;
-
- /**
- * Overload without custom types
- */
- protected GenericViewModel(Context context, DisplayRawContact rawContact,
- long dataId, ContentValues contentValues, String mimeType, int labelResId,
- Field[] fields) {
- this(context, rawContact, dataId, contentValues, mimeType, labelResId, fields,
- null, -1, null, null);
- }
-
- protected GenericViewModel(Context context, DisplayRawContact rawContact,
- long dataId, ContentValues contentValues, String mimeType, int labelResId,
- Field[] fields, int[] typeResIds, int customTypeIndex,
- String typeColumn, String labelColumn) {
- super(context, rawContact, dataId, contentValues, mimeType);
- mLabelResId = labelResId;
- mFields = fields;
- mTypeResIds = typeResIds;
- mCustomTypeIndex = customTypeIndex;
- mTypeColumn = typeColumn;
- mLabelColumn = labelColumn;
- }
-
- public static GenericViewModel fromDataKind(Context context, DisplayRawContact rawContact,
- long dataId, ContentValues contentValues, DataKind dataKind) {
- final Field[] fields = new Field[dataKind.fieldList.size()];
- for (int i = 0; i < fields.length; i++) {
- final EditField editField = dataKind.fieldList.get(i);
- fields[i] = new Field(editField.titleRes, editField.column);
- }
- final int[] typeResIds;
- if (dataKind.typeList == null) {
- typeResIds = null;
- } else {
- typeResIds = new int[dataKind.typeList.size()];
- for (int i = 0; i < typeResIds.length; i++) {
- final EditType editType = dataKind.typeList.get(i);
- typeResIds[i] = editType.labelRes;
- }
- }
- return new GenericViewModel(context, rawContact, dataId, contentValues, dataKind.mimeType,
- dataKind.titleRes, fields, typeResIds, BaseTypes.TYPE_CUSTOM,
- dataKind.typeColumn, "");
- }
-
- @Override
- public EditorItemView createAndAddView(LayoutInflater inflater, ViewGroup parent) {
- if (mEditorItemView == null) {
- final EditorItemView result = new EditorItemView(getContext());
- result.setListener(mViewListener);
-
- final int[] fieldResIds = new int[mFields.length];
- for (int i = 0; i < mFields.length; i++) {
- fieldResIds[i] = mFields[i].getResId();
- }
-
- result.configure(mLabelResId, fieldResIds, mTypeResIds, mCustomTypeIndex);
-
- parent.addView(result);
-
- mEditorItemView = result;
- }
-
- // Set fields
- final ContentValues contentValues = getContentValues();
- for (int i = 0; i < mFields.length; i++) {
- mEditorItemView.setFieldValue(i, contentValues.getAsString(mFields[i].getName()));
- }
-
- // Set type if required
- if (mTypeColumn != null) {
- mEditorItemView.setType(getTypeValue(), getLabelValue());
- }
-
- return mEditorItemView;
- }
-
- @Override
- protected void writeToBuilder(Builder builder, boolean isInsert) {
- for (int i = 0; i < mFields.length; i++) {
- final String fieldName = mFields[i].getName();
- builder.withValue(fieldName, getContentValues().getAsString(fieldName));
- }
- if (mTypeColumn != null) {
- builder.withValue(mTypeColumn, getTypeValue());
- builder.withValue(mLabelColumn, getLabelValue());
- }
- }
-
- private int getTypeValue() {
- return getContentValues().getAsInteger(mTypeColumn);
- }
-
- private String getLabelValue() {
- return getContentValues().getAsString(mLabelColumn);
- }
-
- private EditorItemView.Listener mViewListener = new EditorItemView.Listener() {
- public void onFocusLost() {
- Log.v(TAG, "Received FocusLost. Checking for changes");
- boolean hasChanged = false;
-
- final ContentValues contentValues = getContentValues();
-
- for (int i = 0; i < mFields.length; i++) {
- final String oldValue = contentValues.getAsString(mFields[i].getName());
- final String newValue = mEditorItemView.getFieldValue(i);
- if (!TextUtils.equals(oldValue, newValue)) {
- contentValues.put(mFields[i].getName(), newValue);
- hasChanged = true;
- }
- }
- if (hasChanged) {
- Log.v(TAG, "Found changes. Updating DB");
- saveData();
- }
- }
-
- @Override
- public void onTypeChanged(int newIndex, String customText) {
- // TODO Auto-generated method stub
-
- }
- };
-
- /* package */ static class Field {
- private int mResId;
- private String mName;
-
- public int getResId() {
- return mResId;
- }
-
- public String getName() {
- return mName;
- }
-
- public Field(int resourceId, String databaseField) {
- mResId = resourceId;
- mName = databaseField;
- }
- }
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/HeaderViewModel.java b/src/com/android/contacts/views/editor/viewModel/HeaderViewModel.java
deleted file mode 100644
index 901452c..0000000
--- a/src/com/android/contacts/views/editor/viewModel/HeaderViewModel.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.R;
-import com.android.contacts.views.editor.DisplayRawContact;
-import com.android.contacts.views.editor.view.HeaderView;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-public class HeaderViewModel extends BaseViewModel {
- private boolean mCollapsed;
-
- public HeaderViewModel(Context context, DisplayRawContact rawContact) {
- super(context, rawContact);
- }
-
- public boolean isCollapsed() {
- return mCollapsed;
- }
-
- public void setCollapsed(boolean collapsed) {
- mCollapsed = collapsed;
- }
-
- @Override
- public View createAndAddView(LayoutInflater inflater, ViewGroup parent) {
- final HeaderView result = HeaderView.inflate(inflater, parent, false);
-
- CharSequence accountType = getRawContact().getSource().getDisplayLabel(getContext());
- if (TextUtils.isEmpty(accountType)) {
- accountType = getContext().getString(R.string.account_phone);
- }
- final String accountName = getRawContact().getAccountName();
-
- final String accountTypeDisplay;
- if (TextUtils.isEmpty(accountName)) {
- accountTypeDisplay = getContext().getString(R.string.account_type_format,
- accountType);
- } else {
- accountTypeDisplay = getContext().getString(R.string.account_type_and_name,
- accountType, accountName);
- }
-
- result.setCaptionText(accountTypeDisplay);
- result.setLogo(getRawContact().getSource().getDisplayIcon(getContext()));
-
- parent.addView(result);
- return result;
- }
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/ImViewModel.java b/src/com/android/contacts/views/editor/viewModel/ImViewModel.java
deleted file mode 100644
index 7ee3b21..0000000
--- a/src/com/android/contacts/views/editor/viewModel/ImViewModel.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.views.editor.DisplayRawContact;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.ContentProviderOperation.Builder;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-
-/**
- * Editor for Instant Messaging fields. The Type (HOME, WORK, OTHER, CUSTOM) is not shown but
- * instead the same field is used for showing the Protocol (AIM, YAHOO etc). When
- * creating new Im rows, the Type is set to OTHER
- */
-public class ImViewModel extends FieldAndTypeViewModel {
- private ImViewModel(Context context, DisplayRawContact rawContact, long dataId,
- ContentValues contentValues, int titleResId) {
- super(context, rawContact, dataId, contentValues, Im.CONTENT_ITEM_TYPE, titleResId, Im.DATA,
- Im.PROTOCOL, Im.CUSTOM_PROTOCOL);
- }
-
- public static ImViewModel createForExisting(Context context, DisplayRawContact rawContact,
- long dataId, ContentValues contentValues, int titleResId) {
- return new ImViewModel(context, rawContact, dataId, contentValues, titleResId);
- }
-
- @Override
- protected CharSequence getTypeDisplayLabel() {
- return Im.getProtocolLabel(getContext().getResources(), getType(), getLabel());
- }
-
- @Override
- protected void writeToBuilder(Builder builder, boolean isInsert) {
- // The Type field is not exposed in the UI. Write OTHER for Insert but don't change it
- // for updates
- if (isInsert) {
- builder.withValue(Im.TYPE, Im.TYPE_OTHER);
- builder.withValue(Im.LABEL, "");
- }
- super.writeToBuilder(builder, isInsert);
- }
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/OrganizationViewModel.java b/src/com/android/contacts/views/editor/viewModel/OrganizationViewModel.java
deleted file mode 100644
index bfa296d..0000000
--- a/src/com/android/contacts/views/editor/viewModel/OrganizationViewModel.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.views.editor.DisplayRawContact;
-import com.android.contacts.views.editor.view.OrganizationView;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.ContentProviderOperation.Builder;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-public class OrganizationViewModel extends DataViewModel {
- private static final String TAG = "OrganizationViewModel";
-
- private final int mLabelResId;
- private final String mCompanyFieldColumn;
- private final String mTitleFieldColumn;
- private final String mLabelColumn;
- private final String mTypeColumn;
-
- private OrganizationViewModel(Context context, DisplayRawContact rawContact,
- long dataId, ContentValues contentValues, int labelResId) {
- super(context, rawContact, dataId, contentValues, Organization.CONTENT_ITEM_TYPE);
- mLabelResId = labelResId;
-
- mCompanyFieldColumn = Organization.COMPANY;
- mTitleFieldColumn = Organization.TITLE;
- mTypeColumn = Organization.TYPE;
- mLabelColumn = Organization.LABEL;
- }
-
- public static OrganizationViewModel createForExisting(Context context,
- DisplayRawContact rawContact, long dataId, ContentValues contentValues,
- int titleResId) {
- return new OrganizationViewModel(context, rawContact, dataId, contentValues, titleResId);
- }
-
- @Override
- public OrganizationView createAndAddView(LayoutInflater inflater, ViewGroup parent) {
- final OrganizationView result = OrganizationView.inflate(inflater, parent, false);
-
- result.setListener(mViewListener);
- result.setLabelText(mLabelResId);
- result.setFieldValues(getCompanyFieldValue(), getTitleFieldValue());
- result.setTypeDisplayLabel(getTypeDisplayLabel());
-
- parent.addView(result);
- return result;
- }
-
- @Override
- protected void writeToBuilder(Builder builder, boolean isInsert) {
- builder.withValue(mCompanyFieldColumn, getCompanyFieldValue());
- builder.withValue(mTitleFieldColumn, getTitleFieldValue());
- builder.withValue(mTypeColumn, getType());
- builder.withValue(mLabelColumn, getLabel());
- }
-
- protected String getCompanyFieldValue() {
- return getContentValues().getAsString(mCompanyFieldColumn);
- }
-
- protected String getTitleFieldValue() {
- return getContentValues().getAsString(mTitleFieldColumn);
- }
-
- private void putCompanyFieldValue(String value) {
- getContentValues().put(mCompanyFieldColumn, value);
- }
-
- private void putTitleFieldValue(String value) {
- getContentValues().put(mTitleFieldColumn, value);
- }
-
- private int getType() {
- return getContentValues().getAsInteger(mTypeColumn).intValue();
- }
-
- private void putType(int value) {
- getContentValues().put(mTypeColumn, value);
- }
-
- private String getLabel() {
- return getContentValues().getAsString(mLabelColumn);
- }
-
- private void putLabel(String value) {
- getContentValues().put(mLabelColumn, value);
- }
-
- private CharSequence getTypeDisplayLabel() {
- return Organization.getTypeLabel(getContext().getResources(), getType(), getLabel());
- }
-
- private OrganizationView.Listener mViewListener = new OrganizationView.Listener() {
- public void onFocusLost(OrganizationView view) {
- Log.v(TAG, "Received FocusLost. Checking for changes");
- boolean hasChanged = false;
-
- final String oldCompanyValue = getCompanyFieldValue();
- final String newCompanyValue = view.getCompanyFieldValue().toString();
- if (!TextUtils.equals(oldCompanyValue, newCompanyValue)) {
- putCompanyFieldValue(newCompanyValue);
- hasChanged = true;
- }
-
- final String oldTitleValue = getTitleFieldValue();
- final String newTitleValue = view.getTitleFieldValue().toString();
- if (!TextUtils.equals(oldTitleValue, newTitleValue)) {
- putTitleFieldValue(newTitleValue);
- hasChanged = true;
- }
- if (hasChanged) {
- Log.v(TAG, "Found changes. Updating DB");
- saveData();
- }
- }
- };
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/PhotoViewModel.java b/src/com/android/contacts/views/editor/viewModel/PhotoViewModel.java
deleted file mode 100644
index 682545c..0000000
--- a/src/com/android/contacts/views/editor/viewModel/PhotoViewModel.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.views.editor.DisplayRawContact;
-import com.android.contacts.views.editor.view.PhotoView;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.ContentProviderOperation.Builder;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Editor for the contact photo.
- */
-public class PhotoViewModel extends DataViewModel {
- private PhotoViewModel(Context context, DisplayRawContact rawContact, long dataId,
- ContentValues contentValues) {
- super(context, rawContact, dataId, contentValues, Im.CONTENT_ITEM_TYPE);
- }
-
- public static PhotoViewModel createForExisting(Context context, DisplayRawContact rawContact,
- long dataId, ContentValues contentValues) {
- return new PhotoViewModel(context, rawContact, dataId, contentValues);
- }
-
- @Override
- protected void writeToBuilder(Builder builder, boolean isInsert) {
- // TODO
- }
-
- @Override
- public View createAndAddView(LayoutInflater inflater, ViewGroup parent) {
- final PhotoView result = PhotoView.inflate(inflater, parent, false);
-
- final byte[] binaryData = getContentValues().getAsByteArray(Photo.PHOTO);
-
- final Bitmap bitmap = binaryData != null
- ? BitmapFactory.decodeByteArray(binaryData, 0, binaryData.length)
- : null;
- result.setPhoto(bitmap);
- parent.addView(result);
- return result;
- }
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/StructuredNameViewModel.java b/src/com/android/contacts/views/editor/viewModel/StructuredNameViewModel.java
deleted file mode 100644
index 3351f4e..0000000
--- a/src/com/android/contacts/views/editor/viewModel/StructuredNameViewModel.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.views.editor.DisplayRawContact;
-import com.android.contacts.views.editor.view.SimpleOrStructuredView;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.ContentProviderOperation.Builder;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Editor for the StructuredName. Handles both the structured representation as well the
- * single field display
- */
-public class StructuredNameViewModel extends DataViewModel {
- protected static final String TAG = "StructuredNameViewModel";
- private final int mLabelResId;
-
- private StructuredNameViewModel(Context context, DisplayRawContact rawContact, long dataId,
- ContentValues contentValues, int labelResId) {
- super(context, rawContact, dataId, contentValues, StructuredName.CONTENT_ITEM_TYPE);
- mLabelResId = labelResId;
- }
-
- public static StructuredNameViewModel createForExisting(Context context, DisplayRawContact rawContact,
- long dataId, ContentValues contentValues, int labelResId) {
- return new StructuredNameViewModel(context, rawContact, dataId, contentValues, labelResId);
- }
-
- @Override
- protected void writeToBuilder(Builder builder, boolean isInsert) {
- // TODO: Handle both structured and unstructured inputs.
- // if (structuredEntered()) {
- // } else {
- builder.withValue(StructuredName.DISPLAY_NAME, getDisplayName());
- builder.withValue(StructuredName.GIVEN_NAME, null);
- builder.withValue(StructuredName.FAMILY_NAME, null);
- builder.withValue(StructuredName.PREFIX, null);
- builder.withValue(StructuredName.MIDDLE_NAME, null);
- builder.withValue(StructuredName.SUFFIX, null);
- // }
- }
-
- @Override
- public View createAndAddView(LayoutInflater inflater, ViewGroup parent) {
- final SimpleOrStructuredView result =
- SimpleOrStructuredView.inflate(inflater, parent, false);
-
- result.setListener(mViewListener);
- result.setLabelText(mLabelResId);
- result.setDisplayName(getDisplayName());
-
- parent.addView(result);
- return result;
- }
-
- private String getDisplayName() {
- return getContentValues().getAsString(StructuredName.DISPLAY_NAME);
- }
-
- private void putDisplayName(String value) {
- getContentValues().put(StructuredName.DISPLAY_NAME, value);
- }
-
- private SimpleOrStructuredView.Listener mViewListener = new SimpleOrStructuredView.Listener() {
- public void onFocusLost(SimpleOrStructuredView view) {
- Log.v(TAG, "Received FocusLost. Checking for changes");
- boolean hasChanged = false;
-
- final String oldValue = getDisplayName();
- final String newValue = view.getDisplayName().toString();
- if (!TextUtils.equals(oldValue, newValue)) {
- putDisplayName(newValue);
- hasChanged = true;
- }
- if (hasChanged) {
- Log.v(TAG, "Found changes. Updating DB");
- saveData();
- }
- }
-
- public void onStructuredEditorRequested(SimpleOrStructuredView view) {
- // TODO
- }
- };
-}
diff --git a/src/com/android/contacts/views/editor/viewModel/StructuredPostalViewModel.java b/src/com/android/contacts/views/editor/viewModel/StructuredPostalViewModel.java
deleted file mode 100644
index f1a1559..0000000
--- a/src/com/android/contacts/views/editor/viewModel/StructuredPostalViewModel.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.views.editor.viewModel;
-
-import com.android.contacts.views.editor.DisplayRawContact;
-import com.android.contacts.views.editor.view.SimpleOrStructuredView;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.ContentProviderOperation.Builder;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Editor for the StructuredPostal. Handles both the structured representation as well the
- * single field display
- */
-public class StructuredPostalViewModel extends DataViewModel {
- protected static final String TAG = "StructuredPostalViewModel";
- private final int mLabelResId;
-
- private StructuredPostalViewModel(Context context, DisplayRawContact rawContact, long dataId,
- ContentValues contentValues, int labelResId) {
- super(context, rawContact, dataId, contentValues, StructuredPostal.CONTENT_ITEM_TYPE);
- mLabelResId = labelResId;
- }
-
- public static StructuredPostalViewModel createForExisting(Context context,
- DisplayRawContact rawContact, long dataId, ContentValues contentValues,
- int labelResId) {
- return new StructuredPostalViewModel(context, rawContact, dataId, contentValues,
- labelResId);
- }
-
- @Override
- protected void writeToBuilder(Builder builder, boolean isInsert) {
- // TODO: Writing the non-structured field works pretty badly as we
- // currently can't parse it properly. This is also a problem when an address it taken
- // from Maps
-
- // TODO: Handle both structured and unstructured inputs.
-
- // if (structuredEntered()) {
- // } else {
- builder.withValue(StructuredPostal.FORMATTED_ADDRESS, getFormattedAddress());
- builder.withValue(StructuredPostal.STREET, null);
- builder.withValue(StructuredPostal.POBOX, null);
- builder.withValue(StructuredPostal.NEIGHBORHOOD, null);
- builder.withValue(StructuredPostal.CITY, null);
- builder.withValue(StructuredPostal.REGION, null);
- builder.withValue(StructuredPostal.POSTCODE, null);
- builder.withValue(StructuredPostal.COUNTRY, null);
- // }
- }
-
- @Override
- public View createAndAddView(LayoutInflater inflater, ViewGroup parent) {
- final SimpleOrStructuredView result =
- SimpleOrStructuredView.inflate(inflater, parent, false);
-
- result.setListener(mViewListener);
- result.setLabelText(mLabelResId);
- result.setDisplayName(getFormattedAddress());
-
- parent.addView(result);
- return result;
- }
-
- private String getFormattedAddress() {
- return getContentValues().getAsString(StructuredPostal.FORMATTED_ADDRESS);
- }
-
- private void putDisplayName(String value) {
- getContentValues().put(StructuredPostal.FORMATTED_ADDRESS, value);
- }
-
- private SimpleOrStructuredView.Listener mViewListener = new SimpleOrStructuredView.Listener() {
- public void onFocusLost(SimpleOrStructuredView view) {
- Log.v(TAG, "Received FocusLost. Checking for changes");
- boolean hasChanged = false;
-
- final String oldValue = getFormattedAddress();
- final String newValue = view.getDisplayName().toString();
- if (!TextUtils.equals(oldValue, newValue)) {
- putDisplayName(newValue);
- hasChanged = true;
- }
- if (hasChanged) {
- Log.v(TAG, "Found changes. Updating DB");
- saveData();
- }
- }
-
- public void onStructuredEditorRequested(SimpleOrStructuredView view) {
- // TODO
- }
- };
-}