Merge "Change contact detail on 7" to fragment carousel" into jb-dev
diff --git a/res/layout/contact_detail_add_connection_entry_view.xml b/res/layout/contact_detail_add_connection_entry_view.xml
new file mode 100644
index 0000000..208a8d4
--- /dev/null
+++ b/res/layout/contact_detail_add_connection_entry_view.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2012, 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.
+ */
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/detail_item_side_margin"
+ android:paddingRight="@dimen/detail_item_side_margin">
+ <LinearLayout
+ android:id="@+id/primary_action_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/detail_item_vertical_margin"
+ android:paddingBottom="@dimen/detail_item_vertical_margin"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:minHeight="@dimen/detail_min_line_item_height"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+
+ <ImageView
+ android:id="@+id/add_connection_icon"
+ android:layout_width="@dimen/detail_network_icon_size"
+ android:layout_height="@dimen/detail_network_icon_size"
+ android:layout_marginLeft="@dimen/detail_item_icon_margin"
+ android:layout_marginRight="@dimen/detail_item_icon_margin"
+ android:layout_gravity="center_vertical"
+ android:scaleType="centerInside" />
+
+ <TextView
+ android:id="@+id/add_connection_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+ </LinearLayout>
+</FrameLayout>
diff --git a/res/layout/contact_detail_network_title_entry_view.xml b/res/layout/contact_detail_network_title_entry_view.xml
index 1907a7a..09b8b34 100644
--- a/res/layout/contact_detail_network_title_entry_view.xml
+++ b/res/layout/contact_detail_network_title_entry_view.xml
@@ -49,6 +49,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorSecondary" />
</LinearLayout>
</FrameLayout>
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index fcc6510..5d188fb 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -1254,11 +1254,11 @@
final AccountType accountType = AccountTypeManager.getInstance(context).getAccountType(
type, dataSet);
final String serviceName = accountType.getViewContactNotifyServiceClassName();
- final String resPackageName = accountType.resPackageName;
- if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(resPackageName)) {
+ final String servicePackageName = accountType.getViewContactNotifyServicePackageName();
+ if (!TextUtils.isEmpty(serviceName) && !TextUtils.isEmpty(servicePackageName)) {
final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
final Intent intent = new Intent();
- intent.setClassName(resPackageName, serviceName);
+ intent.setClassName(servicePackageName, serviceName);
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(uri, RawContacts.CONTENT_ITEM_TYPE);
try {
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index fdfd0f7..b8bf45f 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -506,6 +506,7 @@
}
} finally {
outputStream.close();
+ photoFile.delete();
}
} catch (IOException e) {
Log.e(TAG, "Failed to write photo: " + photoFile.toString() + " because: " + e);
diff --git a/src/com/android/contacts/ContactsUtils.java b/src/com/android/contacts/ContactsUtils.java
index 9a014f4..ca32ff8 100644
--- a/src/com/android/contacts/ContactsUtils.java
+++ b/src/com/android/contacts/ContactsUtils.java
@@ -204,16 +204,16 @@
* Returns the intent to launch for the given invitable account type and contact lookup URI.
* This will return null if the account type is not invitable (i.e. there is no
* {@link AccountType#getInviteContactActivityClassName()} or
- * {@link AccountType#resPackageName}).
+ * {@link AccountType#syncAdapterPackageName}).
*/
public static Intent getInvitableIntent(AccountType accountType, Uri lookupUri) {
- String resPackageName = accountType.resPackageName;
+ String syncAdapterPackageName = accountType.syncAdapterPackageName;
String className = accountType.getInviteContactActivityClassName();
- if (TextUtils.isEmpty(resPackageName) || TextUtils.isEmpty(className)) {
+ if (TextUtils.isEmpty(syncAdapterPackageName) || TextUtils.isEmpty(className)) {
return null;
}
Intent intent = new Intent();
- intent.setClassName(resPackageName, className);
+ intent.setClassName(syncAdapterPackageName, className);
intent.setAction(ContactsContract.Intents.INVITE_CONTACT);
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index b9401e1..fd06d60 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -290,12 +290,15 @@
}
private void update() {
+ boolean isIconifiedChanging = mSearchView.isIconified() == mSearchMode;
if (mSearchMode) {
setFocusOnSearchView();
// Since we have the {@link SearchView} in a custom action bar, we must manually handle
// expanding the {@link SearchView} when a search is initiated. Note that a side effect
// of this method is that the {@link SearchView} query text is set to empty string.
- mSearchView.onActionViewExpanded();
+ if (isIconifiedChanging) {
+ mSearchView.onActionViewExpanded();
+ }
if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_STANDARD) {
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
}
@@ -320,7 +323,9 @@
mActionBar.setTitle(null);
// Since we have the {@link SearchView} in a custom action bar, we must manually handle
// collapsing the {@link SearchView} when search mode is exited.
- mSearchView.onActionViewCollapsed();
+ if (isIconifiedChanging) {
+ mSearchView.onActionViewCollapsed();
+ }
if (mListener != null) {
mListener.onAction(Action.STOP_SEARCH_MODE);
mListener.onSelectedTabChanged();
diff --git a/src/com/android/contacts/activities/AttachPhotoActivity.java b/src/com/android/contacts/activities/AttachPhotoActivity.java
index 0ec0590..942f0be 100644
--- a/src/com/android/contacts/activities/AttachPhotoActivity.java
+++ b/src/com/android/contacts/activities/AttachPhotoActivity.java
@@ -80,7 +80,7 @@
mTempPhotoUri = Uri.parse(icicle.getString(KEY_TEMP_PHOTO_URI));
mTempPhotoFile = new File(mTempPhotoUri.getPath());
} else {
- mTempPhotoFile = ContactPhotoUtils.generateTempPhotoFile();
+ mTempPhotoFile = ContactPhotoUtils.generateTempPhotoFile(this);
mTempPhotoUri = Uri.fromFile(mTempPhotoFile);
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index d591b42..9dde2dd 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -207,7 +207,7 @@
account.type, account.dataSet);
Intent intent = new Intent();
- intent.setClassName(accountType.resPackageName,
+ intent.setClassName(accountType.syncAdapterPackageName,
accountType.getCreateContactActivityClassName());
intent.setAction(Intent.ACTION_INSERT);
intent.setType(Contacts.CONTENT_ITEM_TYPE);
@@ -232,7 +232,7 @@
account.type, account.dataSet);
Intent intent = new Intent();
- intent.setClassName(accountType.resPackageName,
+ intent.setClassName(accountType.syncAdapterPackageName,
accountType.getEditContactActivityClassName());
intent.setAction(Intent.ACTION_EDIT);
intent.setData(rawContactUri);
diff --git a/src/com/android/contacts/activities/GroupDetailActivity.java b/src/com/android/contacts/activities/GroupDetailActivity.java
index 6562278..e49789e 100644
--- a/src/com/android/contacts/activities/GroupDetailActivity.java
+++ b/src/com/android/contacts/activities/GroupDetailActivity.java
@@ -147,7 +147,8 @@
final Uri uri = ContentUris.withAppendedId(Groups.CONTENT_URI,
mFragment.getGroupId());
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.setClassName(accountType.resPackageName, accountType.getViewGroupActivity());
+ intent.setClassName(accountType.syncAdapterPackageName,
+ accountType.getViewGroupActivity());
startActivity(intent);
}
});
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 1194a5e..bcceff4 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -150,7 +150,7 @@
private ContactsUnavailableFragment mContactsUnavailableFragment;
private ProviderStatusWatcher mProviderStatusWatcher;
- private int mProviderStatus = -1;
+ private int mProviderStatus;
private boolean mOptionsMenuContactsAvailable;
@@ -490,8 +490,6 @@
@Override
protected void onPause() {
mOptionsMenuContactsAvailable = false;
-
- mProviderStatus = -1;
mProviderStatusWatcher.stop();
super.onPause();
}
@@ -499,8 +497,9 @@
@Override
protected void onResume() {
super.onResume();
+
mProviderStatusWatcher.start();
- showContactsUnavailableFragmentIfNecessary();
+ updateViewConfiguration(true);
// Re-register the listener, which may have been cleared when onSaveInstanceState was
// called. See also: onSaveInstanceState
@@ -983,15 +982,12 @@
@Override
public void onProviderStatusChange() {
- showContactsUnavailableFragmentIfNecessary();
+ updateViewConfiguration(false);
}
- private void showContactsUnavailableFragmentIfNecessary() {
+ private void updateViewConfiguration(boolean forceUpdate) {
int providerStatus = mProviderStatusWatcher.getProviderStatus();
- if (providerStatus == mProviderStatus) {
- return;
- }
-
+ if (!forceUpdate && (providerStatus == mProviderStatus)) return;
mProviderStatus = providerStatus;
View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view);
diff --git a/src/com/android/contacts/activities/PhotoSelectionActivity.java b/src/com/android/contacts/activities/PhotoSelectionActivity.java
index 509bd48..0610bb6 100644
--- a/src/com/android/contacts/activities/PhotoSelectionActivity.java
+++ b/src/com/android/contacts/activities/PhotoSelectionActivity.java
@@ -21,6 +21,7 @@
import com.android.contacts.detail.PhotoSelectionHandler;
import com.android.contacts.editor.PhotoActionPopup;
import com.android.contacts.model.EntityDeltaList;
+import com.android.contacts.util.ContactPhotoUtils;
import com.android.contacts.util.SchedulingUtils;
import android.animation.Animator;
@@ -42,7 +43,6 @@
import android.widget.FrameLayout.LayoutParams;
import android.widget.ImageView;
-import java.io.File;
/**
* Popup activity for choosing a contact photo within the Contacts app.
@@ -60,6 +60,12 @@
/** Number of ms for the animation to hide the backdrop on finish. */
private static final int BACKDROP_FADEOUT_DURATION = 100;
+ /** Key used to persist photo-filename (NOT full file-path). */
+ private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile";
+
+ /** Key used to persist whether a sub-activity is currently in progress. */
+ private static final String KEY_SUB_ACTIVITY_IN_PROGRESS = "subinprogress";
+
/** Intent extra to get the photo URI. */
public static final String PHOTO_URI = "photo_uri";
@@ -132,14 +138,23 @@
private boolean mCloseActivityWhenCameBackFromSubActivity;
/**
+ * A photo result received by the activity, persisted across activity lifecycle.
+ */
+ private PendingPhotoResult mPendingPhotoResult;
+
+ /**
* The photo file being interacted with, if any. Saved/restored between activity instances.
*/
- private File mCurrentPhotoFile;
+ private String mCurrentPhotoFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.photoselection_activity);
+ if (savedInstanceState != null) {
+ mCurrentPhotoFile = savedInstanceState.getString(KEY_CURRENT_PHOTO_FILE);
+ mSubActivityInProgress = savedInstanceState.getBoolean(KEY_SUB_ACTIVITY_IN_PROGRESS);
+ }
// Pull data out of the intent.
final Intent intent = getIntent();
@@ -395,11 +410,19 @@
}
@Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString(KEY_CURRENT_PHOTO_FILE, mCurrentPhotoFile);
+ outState.putBoolean(KEY_SUB_ACTIVITY_IN_PROGRESS, mSubActivityInProgress);
+ }
+
+ @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mPhotoHandler != null) {
mSubActivityInProgress = false;
if (mPhotoHandler.handlePhotoActivityResult(requestCode, resultCode, data)) {
- // Result was handled. We'll get a callback later.
+ // Clear out any pending photo result.
+ mPendingPhotoResult = null;
} else {
// User cancelled the sub-activity and returning to the photo selection activity.
if (mCloseActivityWhenCameBackFromSubActivity) {
@@ -410,8 +433,8 @@
}
}
} else {
- // The result comes back before we prepare the handler? This activity won't get
- // re-created for orientation changes, so this shouldn't happen.
+ // Create a pending photo result to be handled when the photo handler is created.
+ mPendingPhotoResult = new PendingPhotoResult(requestCode, resultCode, data);
}
}
@@ -428,14 +451,20 @@
mPhotoHandler = new PhotoHandler(this, mPhotoView, mode, mState);
- // Setting the photo in displayPhoto() resulted in a relayout
- // request... to avoid jank, wait until this layout has happened.
- SchedulingUtils.doAfterLayout(mBackdrop, new Runnable() {
- @Override
- public void run() {
- animatePhotoOpen();
- }
- });
+ if (mPendingPhotoResult != null) {
+ mPhotoHandler.handlePhotoActivityResult(mPendingPhotoResult.mRequestCode,
+ mPendingPhotoResult.mResultCode, mPendingPhotoResult.mData);
+ mPendingPhotoResult = null;
+ } else {
+ // Setting the photo in displayPhoto() resulted in a relayout
+ // request... to avoid jank, wait until this layout has happened.
+ SchedulingUtils.doAfterLayout(mBackdrop, new Runnable() {
+ @Override
+ public void run() {
+ animatePhotoOpen();
+ }
+ });
+ }
}
private final class PhotoHandler extends PhotoSelectionHandler {
@@ -454,27 +483,27 @@
}
@Override
- public void startPhotoActivity(Intent intent, int requestCode, File photoFile) {
+ public void startPhotoActivity(Intent intent, int requestCode, String photoFile) {
mSubActivityInProgress = true;
mCurrentPhotoFile = photoFile;
PhotoSelectionActivity.this.startActivityForResult(intent, requestCode);
}
private final class PhotoListener extends PhotoActionListener {
-
@Override
public void onPhotoSelected(Bitmap bitmap) {
EntityDeltaList delta = getDeltaForAttachingPhotoToContact();
long rawContactId = getWritableEntityId();
- String filePath = mCurrentPhotoFile.getAbsolutePath();
+ final String croppedPath = ContactPhotoUtils.pathForCroppedPhoto(
+ PhotoSelectionActivity.this, mCurrentPhotoFile);
Intent intent = ContactSaveService.createSaveContactIntent(
- mContext, delta, "", 0, mIsProfile, null, null, rawContactId, filePath);
+ mContext, delta, "", 0, mIsProfile, null, null, rawContactId, croppedPath);
startService(intent);
finish();
}
@Override
- public File getCurrentPhotoFile() {
+ public String getCurrentPhotoFile() {
return mCurrentPhotoFile;
}
@@ -486,4 +515,15 @@
}
}
}
+
+ private static class PendingPhotoResult {
+ final private int mRequestCode;
+ final private int mResultCode;
+ final private Intent mData;
+ private PendingPhotoResult(int requestCode, int resultCode, Intent data) {
+ mRequestCode = requestCode;
+ mResultCode = resultCode;
+ mData = data;
+ }
+ }
}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 70a8e79..29aa51a 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -845,7 +845,7 @@
for (AccountType accountType : mOtherEntriesMap.keySet()) {
// Add a title for each third party app
- mAllEntries.add(NetworkTitleViewEntry.fromAccountType(mContext, accountType));
+ mAllEntries.add(new NetworkTitleViewEntry(mContext, accountType));
for (DetailViewEntry detailEntry : mOtherEntriesMap.get(accountType)) {
// Add indented separator
@@ -901,7 +901,7 @@
};
// Finally create the entry.
- mAllEntries.add(NetworkTitleViewEntry.forMoreNetworks(mContext, onClickListener));
+ mAllEntries.add(new AddConnectionViewEntry(mContext, onClickListener));
}
/**
@@ -1158,35 +1158,43 @@
}
/**
- * A title for a section of contact details from a single 3rd party network. It's also
- * used for the "More networks" entry, which has the same layout.
+ * A title for a section of contact details from a single 3rd party network.
*/
private static class NetworkTitleViewEntry extends ViewEntry {
private final Drawable mIcon;
private final CharSequence mLabel;
- private final View.OnClickListener mOnClickListener;
- private NetworkTitleViewEntry(Drawable icon, CharSequence label, View.OnClickListener
- onClickListener) {
+ public NetworkTitleViewEntry(Context context, AccountType type) {
super(ViewAdapter.VIEW_TYPE_NETWORK_TITLE_ENTRY);
- this.mIcon = icon;
- this.mLabel = label;
- this.mOnClickListener = onClickListener;
+ this.mIcon = type.getDisplayIcon(context);
+ this.mLabel = type.getDisplayLabel(context);
this.isEnabled = false;
}
- public static NetworkTitleViewEntry fromAccountType(Context context, AccountType type) {
- return new NetworkTitleViewEntry(
- type.getDisplayIcon(context), type.getDisplayLabel(context), null);
+ public Drawable getIcon() {
+ return mIcon;
}
- public static NetworkTitleViewEntry forMoreNetworks(Context context, View.OnClickListener
- onClickListener) {
- // TODO Icon is temporary. Need proper one.
- return new NetworkTitleViewEntry(
- context.getResources().getDrawable(R.drawable.ic_menu_add_field_holo_light),
- context.getString(R.string.add_connection_button),
- onClickListener);
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+ }
+
+ /**
+ * This is used for the "Add Connections" entry.
+ */
+ private static class AddConnectionViewEntry extends ViewEntry {
+ private final Drawable mIcon;
+ private final CharSequence mLabel;
+ private final View.OnClickListener mOnClickListener;
+
+ private AddConnectionViewEntry(Context context, View.OnClickListener onClickListener) {
+ super(ViewAdapter.VIEW_TYPE_ADD_CONNECTION_ENTRY);
+ this.mIcon = context.getResources().getDrawable(
+ R.drawable.ic_menu_add_field_holo_light);
+ this.mLabel = context.getString(R.string.add_connection_button);
+ this.mOnClickListener = onClickListener;
+ this.isEnabled = true;
}
@Override
@@ -1219,7 +1227,6 @@
public String mimetype;
public Context context = null;
- public String resPackageName = null;
public boolean isPrimary = false;
public int secondaryActionIcon = -1;
public int secondaryActionDescription = -1;
@@ -1255,7 +1262,6 @@
entry.kind = (kind.titleRes == -1 || kind.titleRes == 0) ? ""
: context.getString(kind.titleRes);
entry.data = buildDataString(kind, values, context);
- entry.resPackageName = kind.resPackageName;
if (kind.typeColumn != null && values.containsKey(kind.typeColumn)) {
entry.type = values.getAsInteger(kind.typeColumn);
@@ -1383,6 +1389,14 @@
}
}
+ private static class KindTitleViewCache {
+ public final TextView titleView;
+
+ public KindTitleViewCache(View view) {
+ titleView = (TextView)view.findViewById(R.id.title);
+ }
+ }
+
/**
* Cache of the children views for a view that displays a {@link NetworkTitleViewEntry}
*/
@@ -1397,6 +1411,21 @@
}
/**
+ * Cache of the children views for a view that displays a {@link AddConnectionViewEntry}
+ */
+ private static class AddConnectionViewCache {
+ public final TextView name;
+ public final ImageView icon;
+ public final View primaryActionView;
+
+ public AddConnectionViewCache(View view) {
+ name = (TextView) view.findViewById(R.id.add_connection_label);
+ icon = (ImageView) view.findViewById(R.id.add_connection_icon);
+ primaryActionView = view.findViewById(R.id.primary_action_view);
+ }
+ }
+
+ /**
* Cache of the children views of a contact detail entry represented by a
* {@link DetailViewEntry}
*/
@@ -1440,8 +1469,9 @@
public static final int VIEW_TYPE_HEADER_ENTRY = 1;
public static final int VIEW_TYPE_KIND_TITLE_ENTRY = 2;
public static final int VIEW_TYPE_NETWORK_TITLE_ENTRY = 3;
- public static final int VIEW_TYPE_SEPARATOR_ENTRY = 4;
- private static final int VIEW_TYPE_COUNT = 5;
+ public static final int VIEW_TYPE_ADD_CONNECTION_ENTRY = 4;
+ public static final int VIEW_TYPE_SEPARATOR_ENTRY = 5;
+ private static final int VIEW_TYPE_COUNT = 6;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
@@ -1456,6 +1486,8 @@
return getDetailEntryView(position, convertView, parent);
case VIEW_TYPE_NETWORK_TITLE_ENTRY:
return getNetworkTitleEntryView(position, convertView, parent);
+ case VIEW_TYPE_ADD_CONNECTION_ENTRY:
+ return getAddConnectionEntryView(position, convertView, parent);
default:
throw new IllegalStateException("Invalid view type ID " +
getItemViewType(position));
@@ -1553,11 +1585,19 @@
private View getKindTitleEntryView(int position, View convertView, ViewGroup parent) {
final KindTitleViewEntry entry = (KindTitleViewEntry) getItem(position);
+ final View result;
+ final KindTitleViewCache viewCache;
- final View result = (convertView != null) ? convertView :
- mInflater.inflate(R.layout.list_separator, parent, false);
- final TextView titleTextView = (TextView) result.findViewById(R.id.title);
- titleTextView.setText(entry.getTitle());
+ if (convertView != null) {
+ result = convertView;
+ viewCache = (KindTitleViewCache)result.getTag();
+ } else {
+ result = mInflater.inflate(R.layout.list_separator, parent, false);
+ viewCache = new KindTitleViewCache(result);
+ result.setTag(viewCache);
+ }
+
+ viewCache.titleView.setText(entry.getTitle());
return result;
}
@@ -1575,8 +1615,6 @@
parent, false);
viewCache = new NetworkTitleViewCache(result);
result.setTag(viewCache);
- result.findViewById(R.id.primary_action_view).setOnClickListener(
- entry.mOnClickListener);
}
viewCache.name.setText(entry.getLabel());
@@ -1585,6 +1623,27 @@
return result;
}
+ private View getAddConnectionEntryView(int position, View convertView, ViewGroup parent) {
+ final AddConnectionViewEntry entry = (AddConnectionViewEntry) getItem(position);
+ final View result;
+ final AddConnectionViewCache viewCache;
+
+ if (convertView != null) {
+ result = convertView;
+ viewCache = (AddConnectionViewCache) result.getTag();
+ } else {
+ result = mInflater.inflate(R.layout.contact_detail_add_connection_entry_view,
+ parent, false);
+ viewCache = new AddConnectionViewCache(result);
+ result.setTag(viewCache);
+ }
+ viewCache.name.setText(entry.getLabel());
+ viewCache.icon.setImageDrawable(entry.getIcon());
+ viewCache.primaryActionView.setOnClickListener(entry.mOnClickListener);
+
+ return result;
+ }
+
private View getDetailEntryView(int position, View convertView, ViewGroup parent) {
final DetailViewEntry entry = (DetailViewEntry) getItem(position);
final View v;
diff --git a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
index 6c35682..e8ce622 100644
--- a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
@@ -67,7 +67,7 @@
final Uri uri = ContentUris.withAppendedId(StreamItems.CONTENT_URI,
streamItemEntry.getId());
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.setClassName(accountType.resPackageName,
+ intent.setClassName(accountType.syncAdapterPackageName,
accountType.getViewStreamItemActivity());
startActivity(intent);
}
@@ -84,7 +84,7 @@
final AccountType accountType = getAccountTypeForStreamItemEntry(tag.streamItem);
final Intent intent = new Intent(Intent.ACTION_VIEW, tag.getStreamItemPhotoUri());
- intent.setClassName(accountType.resPackageName,
+ intent.setClassName(accountType.syncAdapterPackageName,
accountType.getViewStreamItemPhotoActivity());
startActivity(intent);
}
diff --git a/src/com/android/contacts/detail/PhotoSelectionHandler.java b/src/com/android/contacts/detail/PhotoSelectionHandler.java
index e11a054..73296a6 100644
--- a/src/com/android/contacts/detail/PhotoSelectionHandler.java
+++ b/src/com/android/contacts/detail/PhotoSelectionHandler.java
@@ -115,12 +115,15 @@
final PhotoActionListener listener = getListener();
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
+ // Photo was chosen (either new or existing from gallery), and cropped.
case REQUEST_CODE_PHOTO_PICKED_WITH_DATA: {
- Bitmap bitmap = BitmapFactory.decodeFile(
- listener.getCurrentPhotoFile().getAbsolutePath());
+ final String path = ContactPhotoUtils.pathForCroppedPhoto(
+ mContext, listener.getCurrentPhotoFile());
+ Bitmap bitmap = BitmapFactory.decodeFile(path);
listener.onPhotoSelected(bitmap);
return true;
}
+ // Photo was successfully taken, now crop it.
case REQUEST_CODE_CAMERA_WITH_DATA: {
doCropPhoto(listener.getCurrentPhotoFile());
return true;
@@ -183,23 +186,28 @@
}
/** Used by subclasses to delegate to their enclosing Activity or Fragment. */
- protected abstract void startPhotoActivity(Intent intent, int requestCode, File photoFile);
+ protected abstract void startPhotoActivity(Intent intent, int requestCode, String photoFile);
/**
* Sends a newly acquired photo to Gallery for cropping
*/
- private void doCropPhoto(File f) {
+ private void doCropPhoto(String fileName) {
try {
+ // Obtain the absolute paths for the newly-taken photo, and the destination
+ // for the soon-to-be-cropped photo.
+ final String newPath = ContactPhotoUtils.pathForNewCameraPhoto(fileName);
+ final String croppedPath = ContactPhotoUtils.pathForCroppedPhoto(mContext, fileName);
+
// Add the image to the media store
MediaScannerConnection.scanFile(
mContext,
- new String[] { f.getAbsolutePath() },
+ new String[] { newPath },
new String[] { null },
null);
// Launch gallery to crop the photo
- final Intent intent = getCropImageIntent(f);
- startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, f);
+ final Intent intent = getCropImageIntent(newPath, croppedPath);
+ startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, fileName);
} catch (Exception e) {
Log.e(TAG, "Cannot crop image", e);
Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
@@ -212,7 +220,7 @@
* what should be returned by
* {@link PhotoSelectionHandler.PhotoActionListener#getCurrentPhotoFile()}.
*/
- private void startTakePhotoActivity(File photoFile) {
+ private void startTakePhotoActivity(String photoFile) {
final Intent intent = getTakePhotoIntent(photoFile);
startPhotoActivity(intent, REQUEST_CODE_CAMERA_WITH_DATA, photoFile);
}
@@ -223,7 +231,7 @@
* stored by the content-provider.
* {@link PhotoSelectionHandler#handlePhotoActivityResult(int, int, Intent)}.
*/
- private void startPickFromGalleryActivity(File photoFile) {
+ private void startPickFromGalleryActivity(String photoFile) {
final Intent intent = getPhotoPickIntent(photoFile);
startPhotoActivity(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA, photoFile);
}
@@ -243,41 +251,44 @@
/**
* Constructs an intent for picking a photo from Gallery, cropping it and returning the bitmap.
*/
- private Intent getPhotoPickIntent(File photoFile) {
- Uri photoUri = Uri.fromFile(photoFile);
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
+ private Intent getPhotoPickIntent(String photoFile) {
+ final String croppedPhotoPath = ContactPhotoUtils.pathForCroppedPhoto(mContext, photoFile);
+ final Uri croppedPhotoUri = Uri.fromFile(new File(croppedPhotoPath));
+ final 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", mPhotoPickSize);
intent.putExtra("outputY", mPhotoPickSize);
- intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, croppedPhotoUri);
return intent;
}
/**
* Constructs an intent for image cropping.
*/
- private Intent getCropImageIntent(File photoFile) {
- Uri photoUri = Uri.fromFile(photoFile);
+ private Intent getCropImageIntent(String inputPhotoPath, String croppedPhotoPath) {
+ final Uri inputPhotoUri = Uri.fromFile(new File(inputPhotoPath));
+ final Uri croppedPhotoUri = Uri.fromFile(new File(croppedPhotoPath));
Intent intent = new Intent("com.android.camera.action.CROP");
- intent.setDataAndType(photoUri, "image/*");
+ intent.setDataAndType(inputPhotoUri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", mPhotoPickSize);
intent.putExtra("outputY", mPhotoPickSize);
- intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, croppedPhotoUri);
return intent;
}
/**
* Constructs an intent for capturing a photo and storing it in a temporary file.
*/
- public static Intent getTakePhotoIntent(File f) {
+ private static Intent getTakePhotoIntent(String fileName) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);
- intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
+ final String newPhotoPath = ContactPhotoUtils.pathForNewCameraPhoto(fileName);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(newPhotoPath)));
return intent;
}
@@ -296,10 +307,10 @@
public void onTakePhotoChosen() {
try {
// Launch camera to take photo for selected contact
- startTakePhotoActivity(ContactPhotoUtils.generateTempPhotoFile());
+ startTakePhotoActivity(ContactPhotoUtils.generateTempPhotoFileName());
} catch (ActivityNotFoundException e) {
- Toast.makeText(mContext, R.string.photoPickerNotFoundText,
- Toast.LENGTH_LONG).show();
+ Toast.makeText(
+ mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
}
}
@@ -307,10 +318,10 @@
public void onPickFromGalleryChosen() {
try {
// Launch picker to choose photo for selected contact
- startPickFromGalleryActivity(ContactPhotoUtils.generateTempPhotoFile());
+ startPickFromGalleryActivity(ContactPhotoUtils.generateTempPhotoFileName());
} catch (ActivityNotFoundException e) {
- Toast.makeText(mContext, R.string.photoPickerNotFoundText,
- Toast.LENGTH_LONG).show();
+ Toast.makeText(
+ mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
}
}
@@ -325,7 +336,7 @@
* fragment's responsibility to maintain this in saved state, since this handler instance
* will not survive rotation.
*/
- public abstract File getCurrentPhotoFile();
+ public abstract String getCurrentPhotoFile();
/**
* Called when the photo selection dialog is dismissed.
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 7d9f510..cd9d98b 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -23,6 +23,7 @@
import com.android.contacts.activities.ContactEditorAccountsChangedActivity;
import com.android.contacts.activities.ContactEditorActivity;
import com.android.contacts.activities.JoinContactActivity;
+import com.android.contacts.activities.PhotoSelectionActivity;
import com.android.contacts.detail.PhotoSelectionHandler;
import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
import com.android.contacts.editor.Editor.EditorListener;
@@ -35,6 +36,7 @@
import com.android.contacts.model.EntityModifier;
import com.android.contacts.model.GoogleAccountType;
import com.android.contacts.util.AccountsListAdapter;
+import com.android.contacts.util.ContactPhotoUtils;
import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
import com.android.contacts.util.HelpUtils;
@@ -195,7 +197,7 @@
private Cursor mGroupMetaData;
- private File mCurrentPhotoFile;
+ private String mCurrentPhotoFile;
private Bundle mUpdatedPhotos = new Bundle();
private Context mContext;
@@ -410,10 +412,7 @@
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);
- }
+ mCurrentPhotoFile = savedState.getString(KEY_CURRENT_PHOTO_FILE);
mContactIdForJoin = savedState.getLong(KEY_CONTACT_ID_FOR_JOIN);
mContactWritableForJoin = savedState.getBoolean(KEY_CONTACT_WRITABLE_FOR_JOIN);
mAggregationSuggestionsRawContactId = savedState.getLong(KEY_SHOW_JOIN_SUGGESTIONS);
@@ -918,10 +917,10 @@
// help menu depending on whether this is inserting or editing
if (Intent.ACTION_INSERT.equals(mAction)) {
// inserting
- HelpUtils.prepareHelpMenuItem(getActivity(), helpMenu, R.string.help_url_people_add);
+ HelpUtils.prepareHelpMenuItem(mContext, helpMenu, R.string.help_url_people_add);
} else if (Intent.ACTION_EDIT.equals(mAction)) {
// editing
- HelpUtils.prepareHelpMenuItem(getActivity(), helpMenu, R.string.help_url_people_edit);
+ HelpUtils.prepareHelpMenuItem(mContext, helpMenu, R.string.help_url_people_edit);
} else {
// something else, so don't show the help menu
helpMenu.setVisible(false);
@@ -966,7 +965,7 @@
// If we just started creating a new contact and haven't added any data, it's too
// early to do a join
if (mState.size() == 1 && mState.get(0).isContactInsert() && !hasPendingChanges()) {
- Toast.makeText(getActivity(), R.string.toast_join_with_empty_contact,
+ Toast.makeText(mContext, R.string.toast_join_with_empty_contact,
Toast.LENGTH_LONG).show();
return true;
}
@@ -1024,10 +1023,11 @@
saveDefaultAccountIfNecessary();
// Save contact
- Intent intent = ContactSaveService.createSaveContactIntent(getActivity(), mState,
- SAVE_MODE_EXTRA_KEY, saveMode, isEditingUserProfile(), getActivity().getClass(),
- ContactEditorActivity.ACTION_SAVE_COMPLETED, mUpdatedPhotos);
- getActivity().startService(intent);
+ Intent intent = ContactSaveService.createSaveContactIntent(mContext, mState,
+ SAVE_MODE_EXTRA_KEY, saveMode, isEditingUserProfile(),
+ ((Activity)mContext).getClass(), ContactEditorActivity.ACTION_SAVE_COMPLETED,
+ mUpdatedPhotos);
+ mContext.startService(intent);
// Don't try to save the same photos twice.
mUpdatedPhotos = new Bundle();
@@ -1543,9 +1543,7 @@
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_CURRENT_PHOTO_FILE, mCurrentPhotoFile);
outState.putLong(KEY_CONTACT_ID_FOR_JOIN, mContactIdForJoin);
outState.putBoolean(KEY_CONTACT_WRITABLE_FOR_JOIN, mContactWritableForJoin);
outState.putLong(KEY_SHOW_JOIN_SUGGESTIONS, mAggregationSuggestionsRawContactId);
@@ -1606,7 +1604,7 @@
/**
* Sets the photo stored in mPhoto and writes it to the RawContact with the given id
*/
- private void setPhoto(long rawContact, Bitmap photo, File photoFile) {
+ private void setPhoto(long rawContact, Bitmap photo, String photoFile) {
BaseRawContactEditorView requestingEditor = getRawContactEditorView(rawContact);
if (photo == null || photo.getHeight() < 0 || photo.getWidth() < 0) {
@@ -1620,7 +1618,9 @@
Log.w(TAG, "The contact that requested the photo is no longer present.");
}
- mUpdatedPhotos.putString(String.valueOf(rawContact), photoFile.getAbsolutePath());
+ final String croppedPhotoPath =
+ ContactPhotoUtils.pathForCroppedPhoto(mContext, mCurrentPhotoFile);
+ mUpdatedPhotos.putString(String.valueOf(rawContact), croppedPhotoPath);
}
/**
@@ -1757,7 +1757,7 @@
}
@Override
- public void startPhotoActivity(Intent intent, int requestCode, File photoFile) {
+ public void startPhotoActivity(Intent intent, int requestCode, String photoFile) {
mRawContactIdRequestingPhoto = mEditor.getRawContactId();
mStatus = Status.SUB_ACTIVITY;
mCurrentPhotoFile = photoFile;
@@ -1819,7 +1819,7 @@
}
@Override
- public File getCurrentPhotoFile() {
+ public String getCurrentPhotoFile() {
return mCurrentPhotoFile;
}
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index 068a641..9e19735 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -375,7 +375,7 @@
public void onClick(View v) {
final Uri uri = ContentUris.withAppendedId(Groups.CONTENT_URI, mGroupId);
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.setClassName(accountType.resPackageName,
+ intent.setClassName(accountType.syncAdapterPackageName,
accountType.getViewGroupActivity());
startActivity(intent);
}
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index 3cc54f1..7e48d83 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -21,7 +21,6 @@
import com.google.android.collect.Maps;
import com.google.common.annotations.VisibleForTesting;
-import android.accounts.Account;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -29,7 +28,6 @@
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
@@ -62,11 +60,25 @@
public String dataSet = null;
/**
- * Package that resources should be loaded from, either defined through an
- * {@link Account} or for matching against {@link Data#RES_PACKAGE}.
+ * Package that resources should be loaded from. Will be null for embedded types, in which
+ * case resources are stored in this package itself.
+ *
+ * TODO Clean up {@link #resourcePackageName}, {@link #syncAdapterPackageName} and
+ * {@link #getViewContactNotifyServicePackageName()}.
+ *
+ * There's the following invariants:
+ * - {@link #syncAdapterPackageName} is always set to the actual sync adapter package name.
+ * - {@link #resourcePackageName} too is set to the same value, unless {@link #isEmbedded()},
+ * in which case it'll be null.
+ * There's an unfortunate exception of {@link FallbackAccountType}. Even though it
+ * {@link #isEmbedded()}, but we set non-null to {@link #resourcePackageName} for unit tests.
*/
- public String resPackageName;
- public String summaryResPackageName;
+ public String resourcePackageName;
+ /**
+ * The package name for the authenticator (for the embedded types, i.e. Google and Exchange)
+ * or the sync adapter (for external type, including extensions).
+ */
+ public String syncAdapterPackageName;
public int titleRes;
public int iconRes;
@@ -125,24 +137,33 @@
public abstract boolean areContactsWritable();
/**
- * Returns an optional custom edit activity. The activity class should reside
- * in the sync adapter package as determined by {@link #resPackageName}.
+ * Returns an optional custom edit activity.
+ *
+ * Only makes sense for non-embedded account types.
+ * The activity class should reside in the sync adapter package as determined by
+ * {@link #syncAdapterPackageName}.
*/
public String getEditContactActivityClassName() {
return null;
}
/**
- * Returns an optional custom new contact activity. The activity class should reside
- * in the sync adapter package as determined by {@link #resPackageName}.
+ * Returns an optional custom new contact activity.
+ *
+ * Only makes sense for non-embedded account types.
+ * The activity class should reside in the sync adapter package as determined by
+ * {@link #syncAdapterPackageName}.
*/
public String getCreateContactActivityClassName() {
return null;
}
/**
- * Returns an optional custom invite contact activity. The activity class should reside
- * in the sync adapter package as determined by {@link #resPackageName}.
+ * Returns an optional custom invite contact activity.
+ *
+ * Only makes sense for non-embedded account types.
+ * The activity class should reside in the sync adapter package as determined by
+ * {@link #syncAdapterPackageName}.
*/
public String getInviteContactActivityClassName() {
return null;
@@ -151,13 +172,25 @@
/**
* Returns an optional service that can be launched whenever a contact is being looked at.
* This allows the sync adapter to provide more up-to-date information.
+ *
* The service class should reside in the sync adapter package as determined by
- * {@link #resPackageName}.
+ * {@link #getViewContactNotifyServicePackageName()}.
*/
public String getViewContactNotifyServiceClassName() {
return null;
}
+ /**
+ * TODO This is way too hacky should be removed.
+ *
+ * This is introduced for {@link GoogleAccountType} where {@link #syncAdapterPackageName}
+ * is the authenticator package name but the notification service is in the sync adapter
+ * package. See {@link #resourcePackageName} -- we should clean up those.
+ */
+ public String getViewContactNotifyServicePackageName() {
+ return syncAdapterPackageName;
+ }
+
/** Returns an optional Activity string that can be used to view the group. */
public String getViewGroupActivity() {
return null;
@@ -174,7 +207,8 @@
}
public CharSequence getDisplayLabel(Context context) {
- return getResourceText(context, summaryResPackageName, titleRes, accountType);
+ // Note this resource is defined in the sync adapter package, not resourcePackageName.
+ return getResourceText(context, syncAdapterPackageName, titleRes, accountType);
}
/**
@@ -213,7 +247,8 @@
* the contact card. (If not defined, returns null.)
*/
public CharSequence getInviteContactActionLabel(Context context) {
- return getResourceText(context, summaryResPackageName, getInviteContactActionResId(), "");
+ // Note this resource is defined in the sync adapter package, not resourcePackageName.
+ return getResourceText(context, syncAdapterPackageName, getInviteContactActionResId(), "");
}
/**
@@ -221,8 +256,9 @@
* own "View Updates" string
*/
public CharSequence getViewGroupLabel(Context context) {
+ // Note this resource is defined in the sync adapter package, not resourcePackageName.
final CharSequence customTitle =
- getResourceText(context, summaryResPackageName, getViewGroupLabelResId(), null);
+ getResourceText(context, syncAdapterPackageName, getViewGroupLabelResId(), null);
return customTitle == null
? context.getText(R.string.view_updates_from_group)
@@ -250,9 +286,9 @@
}
public Drawable getDisplayIcon(Context context) {
- if (this.titleRes != -1 && this.summaryResPackageName != null) {
+ if (this.titleRes != -1 && this.syncAdapterPackageName != null) {
final PackageManager pm = context.getPackageManager();
- return pm.getDrawable(this.summaryResPackageName, this.iconRes, null);
+ return pm.getDrawable(this.syncAdapterPackageName, this.iconRes, null);
} else if (this.titleRes != -1) {
return context.getResources().getDrawable(this.iconRes);
} else {
@@ -306,7 +342,7 @@
"mime type '" + kind.mimeType + "' is already registered");
}
- kind.resPackageName = this.resPackageName;
+ kind.resourcePackageName = this.resourcePackageName;
this.mKinds.add(kind);
this.mMimeKinds.put(kind.mimeType, kind);
return kind;
diff --git a/src/com/android/contacts/model/DataKind.java b/src/com/android/contacts/model/DataKind.java
index c697e6f..0d60317 100644
--- a/src/com/android/contacts/model/DataKind.java
+++ b/src/com/android/contacts/model/DataKind.java
@@ -40,7 +40,7 @@
public static final String PSEUDO_MIME_TYPE_PHONETIC_NAME = "#phoneticName";
public static final String PSEUDO_COLUMN_PHONETIC_NAME = "#phoneticName";
- public String resPackageName;
+ public String resourcePackageName;
public String mimeType;
public int titleRes;
public int iconAltRes;
@@ -110,7 +110,7 @@
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("DataKind:");
- sb.append(" resPackageName=").append(resPackageName);
+ sb.append(" resPackageName=").append(resourcePackageName);
sb.append(" mimeType=").append(mimeType);
sb.append(" titleRes=").append(titleRes);
sb.append(" iconAltRes=").append(iconAltRes);
diff --git a/src/com/android/contacts/model/ExchangeAccountType.java b/src/com/android/contacts/model/ExchangeAccountType.java
index 21a30f8..1e3f6ee 100644
--- a/src/com/android/contacts/model/ExchangeAccountType.java
+++ b/src/com/android/contacts/model/ExchangeAccountType.java
@@ -43,10 +43,10 @@
public static final String ACCOUNT_TYPE = "com.android.exchange";
- public ExchangeAccountType(Context context, String resPackageName) {
+ public ExchangeAccountType(Context context, String authenticatorPackageName) {
this.accountType = ACCOUNT_TYPE;
- this.resPackageName = null;
- this.summaryResPackageName = resPackageName;
+ this.resourcePackageName = null;
+ this.syncAdapterPackageName = authenticatorPackageName;
try {
addDataKindStructuredName(context);
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index f36e9e0..d7420af 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -101,17 +101,17 @@
* @param injectedMetadata If non-null, it'll be used to initialize the type. Only set by
* tests. If null, the metadata is loaded from the specified package.
*/
- ExternalAccountType(Context context, String resPackageName, boolean isExtension,
+ ExternalAccountType(Context context, String packageName, boolean isExtension,
XmlResourceParser injectedMetadata) {
this.mIsExtension = isExtension;
- this.resPackageName = resPackageName;
- this.summaryResPackageName = resPackageName;
+ this.resourcePackageName = packageName;
+ this.syncAdapterPackageName = packageName;
final PackageManager pm = context.getPackageManager();
final XmlResourceParser parser;
if (injectedMetadata == null) {
try {
- parser = loadContactsXml(context, resPackageName);
+ parser = loadContactsXml(context, packageName);
} catch (NameNotFoundException e1) {
// If the package name is not found, we can't initialize this account type.
return;
@@ -147,7 +147,7 @@
error.append(parser.getLineNumber());
}
error.append(" for external package ");
- error.append(resPackageName);
+ error.append(packageName);
Log.e(TAG, error.toString(), e);
return;
@@ -159,13 +159,13 @@
mExtensionPackageNames = new ArrayList<String>();
mInviteActionLabelResId = resolveExternalResId(context, mInviteActionLabelAttribute,
- summaryResPackageName, ATTR_INVITE_CONTACT_ACTION_LABEL);
+ syncAdapterPackageName, ATTR_INVITE_CONTACT_ACTION_LABEL);
mViewGroupLabelResId = resolveExternalResId(context, mViewGroupLabelAttribute,
- summaryResPackageName, ATTR_VIEW_GROUP_ACTION_LABEL);
+ syncAdapterPackageName, ATTR_VIEW_GROUP_ACTION_LABEL);
titleRes = resolveExternalResId(context, mAccountTypeLabelAttribute,
- this.resPackageName, ATTR_ACCOUNT_LABEL);
+ syncAdapterPackageName, ATTR_ACCOUNT_LABEL);
iconRes = resolveExternalResId(context, mAccountTypeIconAttribute,
- this.resPackageName, ATTR_ACCOUNT_ICON);
+ syncAdapterPackageName, ATTR_ACCOUNT_ICON);
// If we reach this point, the account type has been successfully initialized.
mIsInitialized = true;
diff --git a/src/com/android/contacts/model/FallbackAccountType.java b/src/com/android/contacts/model/FallbackAccountType.java
index 6ce7a43..892c049 100644
--- a/src/com/android/contacts/model/FallbackAccountType.java
+++ b/src/com/android/contacts/model/FallbackAccountType.java
@@ -31,8 +31,9 @@
this.titleRes = R.string.account_phone;
this.iconRes = R.mipmap.ic_launcher_contacts;
- this.resPackageName = resPackageName;
- this.summaryResPackageName = resPackageName;
+ // Note those are only set for unit tests.
+ this.resourcePackageName = resPackageName;
+ this.syncAdapterPackageName = resPackageName;
try {
addDataKindStructuredName(context);
@@ -65,7 +66,7 @@
* {@code resPackageName} is injectable.
*/
@NeededForTesting
- static AccountType createForTest(Context context, String resPackageName) {
+ static AccountType createWithPackageNameForTest(Context context, String resPackageName) {
return new FallbackAccountType(context, resPackageName);
}
diff --git a/src/com/android/contacts/model/GoogleAccountType.java b/src/com/android/contacts/model/GoogleAccountType.java
index 822d829..a8a4069 100644
--- a/src/com/android/contacts/model/GoogleAccountType.java
+++ b/src/com/android/contacts/model/GoogleAccountType.java
@@ -39,10 +39,10 @@
private static final List<String> mExtensionPackages =
Lists.newArrayList("com.google.android.apps.plus");
- public GoogleAccountType(Context context, String resPackageName) {
+ public GoogleAccountType(Context context, String authenticatorPackageName) {
this.accountType = ACCOUNT_TYPE;
- this.resPackageName = null;
- this.summaryResPackageName = resPackageName;
+ this.resourcePackageName = null;
+ this.syncAdapterPackageName = authenticatorPackageName;
try {
addDataKindStructuredName(context);
@@ -183,4 +183,15 @@
public boolean areContactsWritable() {
return true;
}
+
+ @Override
+ public String getViewContactNotifyServiceClassName() {
+ return "com.google.android.syncadapters.contacts." +
+ "SyncHighResPhotoIntentService";
+ }
+
+ @Override
+ public String getViewContactNotifyServicePackageName() {
+ return "com.google.android.syncadapters.contacts";
+ }
}
diff --git a/src/com/android/contacts/quickcontact/DataAction.java b/src/com/android/contacts/quickcontact/DataAction.java
index 45565a1..ddec799 100644
--- a/src/com/android/contacts/quickcontact/DataAction.java
+++ b/src/com/android/contacts/quickcontact/DataAction.java
@@ -283,13 +283,13 @@
public Drawable getAlternateIcon() {
if (mAlternateIconRes == 0) return null;
- final String resPackageName = mKind.resPackageName;
- if (resPackageName == null) {
+ final String resourcePackageName = mKind.resourcePackageName;
+ if (resourcePackageName == null) {
return mContext.getResources().getDrawable(mAlternateIconRes);
}
final PackageManager pm = mContext.getPackageManager();
- return pm.getDrawable(resPackageName, mAlternateIconRes, null);
+ return pm.getDrawable(resourcePackageName, mAlternateIconRes, null);
}
@Override
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
index d858e73..6b56470 100644
--- a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
+++ b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
@@ -224,7 +224,7 @@
final Uri uri = ContentUris.withAppendedId(StreamItems.CONTENT_URI,
streamItem.getId());
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.setClassName(accountType.resPackageName,
+ intent.setClassName(accountType.syncAdapterPackageName,
accountType.getViewStreamItemActivity());
views.setOnClickPendingIntent(R.id.name_and_snippet_container,
PendingIntent.getActivity(context, 0, intent, 0));
diff --git a/src/com/android/contacts/util/ContactPhotoUtils.java b/src/com/android/contacts/util/ContactPhotoUtils.java
index f214e9f..2bd8e80 100644
--- a/src/com/android/contacts/util/ContactPhotoUtils.java
+++ b/src/com/android/contacts/util/ContactPhotoUtils.java
@@ -17,6 +17,7 @@
package com.android.contacts.util;
+import android.content.Context;
import android.graphics.Bitmap;
import android.os.Environment;
import android.util.Log;
@@ -35,11 +36,9 @@
private static final String TAG = "ContactPhotoUtils";
private static final String PHOTO_DATE_FORMAT = "'IMG'_yyyyMMdd_HHmmss";
+ private static final String NEW_PHOTO_DIR_PATH =
+ Environment.getExternalStorageDirectory() + "/DCIM/Camera";
- // TODO: /DCIM/Camera isn't the ideal place to stash cropped contact photos.
- // Where is the right place?
- private static final File PHOTO_DIR = new File(
- Environment.getExternalStorageDirectory() + "/DCIM/Camera");
/**
* Generate a new, unique file to be used as an out-of-band communication
@@ -47,15 +46,28 @@
* This file will be passed to other activities (such as the gallery/camera/cropper/etc.),
* and read by us once they are finished writing it.
*/
- public static File generateTempPhotoFile() {
- PHOTO_DIR.mkdirs();
- return new File(PHOTO_DIR, generateTempPhotoFileName());
+ public static File generateTempPhotoFile(Context context) {
+ return new File(pathForCroppedPhoto(context, generateTempPhotoFileName()));
}
- private static String generateTempPhotoFileName() {
+ public static String pathForCroppedPhoto(Context context, String fileName) {
+ final File dir = new File(context.getExternalCacheDir() + "/tmp");
+ dir.mkdirs();
+ final File f = new File(dir, fileName);
+ return f.getAbsolutePath();
+ }
+
+ public static String pathForNewCameraPhoto(String fileName) {
+ final File dir = new File(NEW_PHOTO_DIR_PATH);
+ dir.mkdirs();
+ final File f = new File(dir, fileName);
+ return f.getAbsolutePath();
+ }
+
+ public static String generateTempPhotoFileName() {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat dateFormat = new SimpleDateFormat(PHOTO_DATE_FORMAT);
- return dateFormat.format(date) + ".jpg";
+ return "ContactPhoto-" + dateFormat.format(date) + ".jpg";
}
/**
diff --git a/tests/src/com/android/contacts/model/AccountTypeTest.java b/tests/src/com/android/contacts/model/AccountTypeTest.java
index 8bc7429..6d4d6b0 100644
--- a/tests/src/com/android/contacts/model/AccountTypeTest.java
+++ b/tests/src/com/android/contacts/model/AccountTypeTest.java
@@ -66,8 +66,8 @@
AccountType accountType = new AccountType() {
{
- resPackageName = packageName;
- summaryResPackageName = packageName;
+ resourcePackageName = packageName;
+ syncAdapterPackageName = packageName;
}
@Override protected int getInviteContactActionResId() {
return externalResID;
diff --git a/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java b/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
index 27c645a..4c47d68 100644
--- a/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
+++ b/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
@@ -126,8 +126,8 @@
// Create a fallback type with the same resource package name, and compare all the data
// kinds to its.
- final AccountType reference = FallbackAccountType.createForTest(
- getContext(), type.resPackageName);
+ final AccountType reference = FallbackAccountType.createWithPackageNameForTest(
+ getContext(), type.resourcePackageName);
assertsDataKindEquals(reference.getSortedDataKinds(), type.getSortedDataKinds());
}