summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Garnes <matt@cyngn.com>2015-04-30 10:33:15 -0700
committerMatt Garnes <matt@cyngn.com>2015-04-30 10:33:15 -0700
commit6825b2dc0c51e7cb9ce3ee2e5e32fec821c4c299 (patch)
tree3122cf62c8cc8e7f5b186754e61d80e8774b516b
parente00a2a6518e83990dd19c09d55fa2d59d4bfac6e (diff)
parent75209bbb8d7be819cf9dce121da371d90e5685e1 (diff)
downloadpackages_apps_Contacts-caf/cm-12.1.tar.gz
packages_apps_Contacts-caf/cm-12.1.tar.bz2
packages_apps_Contacts-caf/cm-12.1.zip
Merge remote-tracking branch 'caf/LA.BR.1.2.3' into caf/cm-12.1caf/cm-12.1
-rw-r--r--AndroidManifest.xml2
-rw-r--r--res/drawable-hdpi/ic_trash_white_24.pngbin0 -> 290 bytes
-rw-r--r--res/drawable-mdpi/ic_trash_white_24.pngbin0 -> 201 bytes
-rw-r--r--res/drawable-xhdpi/ic_trash_white_24.pngbin0 -> 339 bytes
-rw-r--r--res/drawable-xxhdpi/ic_trash_white_24.pngbin0 -> 517 bytes
-rw-r--r--res/menu/quickcontact.xml4
-rw-r--r--res/values-land/vals.xml19
-rw-r--r--res/values-zh-rCN/rcs_string.xml1
-rw-r--r--res/values/dimens.xml2
-rw-r--r--src/com/android/contacts/ContactSaveService.java106
-rw-r--r--src/com/android/contacts/activities/ActionBarAdapter.java9
-rw-r--r--src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java3
-rw-r--r--src/com/android/contacts/activities/MultiPickContactActivity.java8
-rw-r--r--src/com/android/contacts/activities/PeopleActivity.java12
-rw-r--r--src/com/android/contacts/editor/ContactEditorFragment.java9
-rw-r--r--src/com/android/contacts/group/GroupDetailFragment.java21
-rwxr-xr-xsrc/com/android/contacts/list/ContactBrowseListFragment.java3
-rw-r--r--src/com/android/contacts/list/JoinContactListFragment.java7
-rw-r--r--src/com/android/contacts/list/JoinContactLoader.java29
-rw-r--r--src/com/android/contacts/quickcontact/MyQrcodeActivity.java3
-rwxr-xr-x[-rw-r--r--]src/com/android/contacts/quickcontact/QuickContactActivity.java82
-rw-r--r--src/com/android/contacts/util/ImageViewDrawableSetter.java13
-rw-r--r--src/com/android/contacts/widget/MultiShrinkScroller.java25
23 files changed, 240 insertions, 118 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8e456cabf..f05a8d076 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -274,7 +274,7 @@
<data android:mimeType="vnd.android.cursor.item/person" />
</intent-filter>
- <intent-filter android:label="@string/viewContactDesription">
+ <intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/person" />
diff --git a/res/drawable-hdpi/ic_trash_white_24.png b/res/drawable-hdpi/ic_trash_white_24.png
new file mode 100644
index 000000000..86e909905
--- /dev/null
+++ b/res/drawable-hdpi/ic_trash_white_24.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_trash_white_24.png b/res/drawable-mdpi/ic_trash_white_24.png
new file mode 100644
index 000000000..fc67992ff
--- /dev/null
+++ b/res/drawable-mdpi/ic_trash_white_24.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_trash_white_24.png b/res/drawable-xhdpi/ic_trash_white_24.png
new file mode 100644
index 000000000..df96462c2
--- /dev/null
+++ b/res/drawable-xhdpi/ic_trash_white_24.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_trash_white_24.png b/res/drawable-xxhdpi/ic_trash_white_24.png
new file mode 100644
index 000000000..96acd7e9f
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_trash_white_24.png
Binary files differ
diff --git a/res/menu/quickcontact.xml b/res/menu/quickcontact.xml
index 789fecd2b..f3960ee34 100644
--- a/res/menu/quickcontact.xml
+++ b/res/menu/quickcontact.xml
@@ -45,6 +45,10 @@
android:showAsAction="always" />
<item
+ android:id="@+id/menu_delete"
+ android:title="@string/menu_deleteContact" />
+
+ <item
android:id="@+id/menu_share"
android:title="@string/menu_share"
android:alphabeticShortcut="s" />
diff --git a/res/values-land/vals.xml b/res/values-land/vals.xml
deleted file mode 100644
index ebcae31e9..000000000
--- a/res/values-land/vals.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-<resources>
- <!-- The ratio of width:height for the contact's photo -->
- <item name="quickcontact_photo_ratio" type="vals" format="float">0.7</item>
-</resources>
diff --git a/res/values-zh-rCN/rcs_string.xml b/res/values-zh-rCN/rcs_string.xml
index 966f26ddc..002b8cffa 100644
--- a/res/values-zh-rCN/rcs_string.xml
+++ b/res/values-zh-rCN/rcs_string.xml
@@ -98,4 +98,5 @@
<string name="first_name_max_length">姓氏不能超过二十个字节。</string>
<string name="last_name_max_length">名字不能超过二十个字节。</string>
<string name="full_name_max_length">姓名不能超过四十个字节。</string>
+ <string name="Unformatted_profile_phone_number">当前联系人不支持增强屏显!</string>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b93b303c6..9610a3fa8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -22,6 +22,8 @@
<dimen name="quickcontact_maximum_title_size">36dp</dimen>
<!-- When QC is uncollapsed, the title has this much margin on its left, right and bottom -->
<dimen name="quickcontact_title_initial_margin">16dp</dimen>
+ <!-- The ratio of width:height for the contact's photo in landscape -->
+ <item name="quickcontact_landscape_photo_ratio" type="dimen" format="float">0.7</item>
<!-- Top padding of the entire contact editor -->
<dimen name="editor_padding_top">0dip</dimen>
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index d81867085..e8ebf560a 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -179,6 +179,7 @@ public class ContactSaveService extends IntentService {
private final int MAX_EMAIL_LENGTH = 40;
private final int MAX_EN_LENGTH = 14;
private final int MAX_CH_LENGTH = 6;
+ private static final int BUFFER_LENGTH = 500;
// Only for request accessing SIM card
// when device is in the "AirPlane" mode.
@@ -258,6 +259,10 @@ public class ContactSaveService extends IntentService {
@Override
protected void onHandleIntent(Intent intent) {
+ if (intent == null) {
+ Log.d(TAG, "onHandleIntent: could not handle null intent");
+ return;
+ }
// Call an appropriate method. If we're sure it affects how incoming phone calls are
// handled, then notify the fact to in-call screen.
String action = intent.getAction();
@@ -796,8 +801,12 @@ public class ContactSaveService extends IntentService {
private long getInsertedRawContactId(
final ArrayList<ContentProviderOperation> diff,
final ContentProviderResult[] results) {
+ if (results == null) {
+ return -1;
+ }
final int diffSize = diff.size();
- for (int i = 0; i < diffSize; i++) {
+ final int numResults = results.length;
+ for (int i = 0; i < diffSize && i < numResults; i++) {
ContentProviderOperation operation = diff.get(i);
if (operation.getType() == ContentProviderOperation.TYPE_INSERT
&& operation.getUri().getEncodedPath().contains(
@@ -1008,49 +1017,58 @@ public class ContactSaveService extends IntentService {
if (rawContactsToAdd == null) {
return;
}
- for (long rawContactId : rawContactsToAdd) {
- try {
- final ArrayList<ContentProviderOperation> rawContactOperations =
- new ArrayList<ContentProviderOperation>();
-
- // Build an assert operation to ensure the contact is not already in the group
- final ContentProviderOperation.Builder assertBuilder = ContentProviderOperation
- .newAssertQuery(Data.CONTENT_URI);
- assertBuilder.withSelection(Data.RAW_CONTACT_ID + "=? AND " +
- Data.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?",
- new String[] { String.valueOf(rawContactId),
- GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId)});
- assertBuilder.withExpectedCount(0);
- rawContactOperations.add(assertBuilder.build());
-
- // Build an insert operation to add the contact to the group
- final ContentProviderOperation.Builder insertBuilder = ContentProviderOperation
- .newInsert(Data.CONTENT_URI);
- insertBuilder.withValue(Data.RAW_CONTACT_ID, rawContactId);
- insertBuilder.withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
- insertBuilder.withValue(GroupMembership.GROUP_ROW_ID, groupId);
- rawContactOperations.add(insertBuilder.build());
- if (DEBUG) {
- for (ContentProviderOperation operation : rawContactOperations) {
- Log.v(TAG, operation.toString());
- }
- }
+ ArrayList<Long> rawContactIdInDb = Lists.newArrayList();
+ final Cursor c = resolver.query(Data.CONTENT_URI, new String[] {Data.RAW_CONTACT_ID},
+ Data.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?",
+ new String[] {GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId)},
+ Data.RAW_CONTACT_ID);
+ try {
+ while (c != null && c.moveToNext()) {
+ final long id = c.getLong(0);
+ rawContactIdInDb.add(id);
+ }
+ } finally {
+ c.close();
+ }
- // Apply batch
- if (!rawContactOperations.isEmpty()) {
+ ArrayList<Long> rawContactIdToAdd = Lists.newArrayList();
+ for (long rawContactId : rawContactsToAdd) {
+ if (!rawContactIdInDb.contains(rawContactId)) {
+ rawContactIdToAdd.add(rawContactId);
+ }
+ }
+
+ final ArrayList<ContentProviderOperation> rawContactOperations =
+ new ArrayList<ContentProviderOperation>();
+ for (long rawContactId : rawContactIdToAdd) {
+ // Build an insert operation to add the contact to the group
+ final ContentProviderOperation.Builder insertBuilder = ContentProviderOperation
+ .newInsert(Data.CONTENT_URI);
+ insertBuilder.withValue(Data.RAW_CONTACT_ID, rawContactId);
+ insertBuilder.withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
+ insertBuilder.withValue(GroupMembership.GROUP_ROW_ID, groupId);
+ rawContactOperations.add(insertBuilder.build());
+
+ int size = rawContactOperations.size();
+ if (size > 0 && BUFFER_LENGTH - size < 10) {
+ try {
resolver.applyBatch(ContactsContract.AUTHORITY, rawContactOperations);
+ } catch (Exception e) {
+ Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
+ } finally {
+ rawContactOperations.clear();
}
- } catch (RemoteException e) {
- // Something went wrong, bail without success
- Log.e(TAG, "Problem persisting user edits for raw contact ID " +
- String.valueOf(rawContactId), e);
- } catch (OperationApplicationException e) {
- // The assert could have failed because the contact is already in the group,
- // just continue to the next contact
- Log.w(TAG, "Assert failed in adding raw contact ID " +
- String.valueOf(rawContactId) + ". Already exists in group " +
- String.valueOf(groupId), e);
+ }
+ }
+ // There maybe some sim operations left after the while loop
+ if (!rawContactOperations.isEmpty()) {
+ try {
+ resolver.applyBatch(ContactsContract.AUTHORITY, rawContactOperations);
+ } catch (Exception e) {
+ Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
+ } finally {
+ rawContactOperations.clear();
}
}
}
@@ -1098,6 +1116,9 @@ public class ContactSaveService extends IntentService {
// Undemote the contact if necessary
final Cursor c = getContentResolver().query(contactUri, new String[] {Contacts._ID},
null, null, null);
+ if (c == null) {
+ return;
+ }
try {
if (c.moveToFirst()) {
final long id = c.getLong(0);
@@ -1300,6 +1321,11 @@ public class ContactSaveService extends IntentService {
JoinContactQuery.PROJECTION,
JoinContactQuery.SELECTION,
new String[]{String.valueOf(contactId1), String.valueOf(contactId2)}, null);
+ if (c == null) {
+ Log.e(TAG, "Unable to open Contacts DB cursor");
+ showToast(R.string.contactSavedErrorToast);
+ return;
+ }
long rawContactIds[];
long verifiedNameRawContactId = -1;
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 24593f6d3..78b2e9cec 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -144,7 +144,7 @@ public class ActionBarAdapter implements OnCloseListener {
new OnClickListener() {
@Override
public void onClick(View v) {
- mSearchView.setText(null);
+ setQueryString(null);
}
});
mSearchContainer.findViewById(R.id.search_back_button).setOnClickListener(
@@ -256,9 +256,8 @@ public class ActionBarAdapter implements OnCloseListener {
}
if (mSearchMode) {
setFocusOnSearchView();
- } else {
- mSearchView.setText(null);
}
+ setQueryString(null);
} else if (flag) {
// Everything is already set up. Still make sure the keyboard is up
if (mSearchView != null) setFocusOnSearchView();
@@ -273,6 +272,10 @@ public class ActionBarAdapter implements OnCloseListener {
mQueryString = query;
if (mSearchView != null) {
mSearchView.setText(query);
+ // When programmatically entering text into the search view, the most reasonable
+ // place for the cursor is after all the text.
+ mSearchView.setSelection(mSearchView.getText() == null ?
+ 0 : mSearchView.getText().length());
}
}
diff --git a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
index bf3b1c138..08667b8c7 100644
--- a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.provider.ContactsContract.Intents;
+import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
@@ -117,6 +118,8 @@ public class ContactEditorAccountsChangedActivity extends Activity {
// This button allows the user to add a new account to the device and return to
// this app afterwards.
leftButton.setText(getString(R.string.add_new_account));
+ leftButton.setSingleLine(true);
+ leftButton.setEllipsize(TextUtils.TruncateAt.END_SMALL);
leftButton.setOnClickListener(mAddAccountClickListener);
// This button allows the user to continue creating the contact in the specified
diff --git a/src/com/android/contacts/activities/MultiPickContactActivity.java b/src/com/android/contacts/activities/MultiPickContactActivity.java
index 75085e112..fa31b0772 100644
--- a/src/com/android/contacts/activities/MultiPickContactActivity.java
+++ b/src/com/android/contacts/activities/MultiPickContactActivity.java
@@ -1228,8 +1228,12 @@ public class MultiPickContactActivity extends ListActivity implements
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
mAdapter.changeCursor(cursor);
if (cursor == null || cursor.getCount() == 0) {
- Toast.makeText(MultiPickContactActivity.this,
- R.string.listFoundAllContactsZero, Toast.LENGTH_SHORT).show();
+ if (isPickCall()) {
+ log("no call found");
+ } else {
+ Toast.makeText(MultiPickContactActivity.this,
+ R.string.listFoundAllContactsZero, Toast.LENGTH_SHORT).show();
+ }
}
}
}
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 90df2301a..5fe03897d 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -27,6 +27,7 @@ import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.IntentFilter;
+import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -65,8 +66,8 @@ import android.view.Window;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.ImageButton;
-import android.widget.Toolbar;
import android.widget.Toast;
+import android.widget.Toolbar;
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
@@ -1636,8 +1637,8 @@ public class PeopleActivity extends ContactsActivity implements
&& !Character.isWhitespace(unicodeChar)) {
String query = new String(new int[]{ unicodeChar }, 0, 1);
if (!mActionBarAdapter.isSearchMode()) {
- mActionBarAdapter.setQueryString(query);
mActionBarAdapter.setSearchMode(true);
+ mActionBarAdapter.setQueryString(query);
return true;
}
}
@@ -1711,7 +1712,12 @@ public class PeopleActivity extends ContactsActivity implements
if (extras != null) {
intent.putExtras(extras);
}
- startActivity(intent);
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException ex) {
+ Toast.makeText(PeopleActivity.this, R.string.missing_app,
+ Toast.LENGTH_SHORT).show();
+ }
break;
default:
Log.wtf(TAG, "Unexpected onClick event from " + view);
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 7a9f5e2ea..2ed1dc280 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -1111,6 +1111,8 @@ public class ContactEditorFragment extends Fragment implements
final MenuItem sendToVoiceMailMenu = menu.findItem(R.id.menu_send_to_voicemail);
final MenuItem ringToneMenu = menu.findItem(R.id.menu_set_ringtone);
final MenuItem deleteMenu = menu.findItem(R.id.menu_delete);
+ deleteMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ deleteMenu.setIcon(R.drawable.ic_trash_white_24);
// Set visibility of menus
doneMenu.setVisible(false);
@@ -1563,7 +1565,7 @@ public class ContactEditorFragment extends Fragment implements
public static interface Listener {
/**
* Contact was not found, so somehow close this fragment. This is raised after a contact
- * is removed via Menu/Delete (unless it was a new contact)
+ * is removed via Menu/Delete
*/
void onContactNotFound();
@@ -1920,7 +1922,7 @@ public class ContactEditorFragment extends Fragment implements
outState.putBoolean(KEY_EXISTING_CONTACT_READY, mExistingContactDataReady);
outState.putParcelableArrayList(KEY_RAW_CONTACTS,
mRawContacts == null ?
- Lists.<RawContact> newArrayList() : Lists.newArrayList(mRawContacts));
+ Lists.<RawContact>newArrayList() : Lists.newArrayList(mRawContacts));
outState.putBoolean(KEY_SEND_TO_VOICE_MAIL_STATE, mSendToVoicemailState);
outState.putString(KEY_CUSTOM_RINGTONE, mCustomRingtone);
outState.putBoolean(KEY_ARE_PHONE_OPTIONS_CHANGEABLE, mArePhoneOptionsChangable);
@@ -2087,8 +2089,9 @@ public class ContactEditorFragment extends Fragment implements
final long loaderCurrentTime = SystemClock.elapsedRealtime();
Log.v(TAG, "Time needed for loading: " + (loaderCurrentTime-mLoaderStartTime));
if (!data.isLoaded()) {
- // Item has been deleted
+ // Item has been deleted. Close activity without saving again.
Log.i(TAG, "No contact found. Closing activity");
+ mStatus = Status.CLOSING;
if (mListener != null) mListener.onContactNotFound();
return;
}
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index 50cc7d8fc..974093ba0 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -135,6 +135,8 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener {
private boolean mShowGroupActionInActionBar;
private boolean mOptionsMenuGroupDeletable;
private boolean mOptionsMenuGroupEditable;
+ private boolean mOptionsMenuRcsSupported;
+ private boolean mOptionsMenuRcsEnhanceScreenSupported;
private boolean mCloseActivityAfterDelete;
private String mGroupMembersPhones;
private ArrayList<String> mGroupMembersPhonesList = new ArrayList<String>();
@@ -474,6 +476,9 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener {
@Override
public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.view_group, menu);
+ mOptionsMenuRcsSupported = RCSUtil.getRcsSupport();
+ mOptionsMenuRcsEnhanceScreenSupported = mOptionsMenuRcsSupported
+ && RCSUtil.isEnhanceScreenInstalled(mContext);
}
public boolean isOptionsMenuChanged() {
@@ -493,16 +498,12 @@ public class GroupDetailFragment extends Fragment implements OnScrollListener {
public void onPrepareOptionsMenu(Menu menu) {
mOptionsMenuGroupDeletable = isGroupDeletable() && isVisible();
mOptionsMenuGroupEditable = isGroupEditableAndPresent() && isVisible();
- if (RCSUtil.getRcsSupport()) {
- final MenuItem optionsGroupChat = menu.findItem(R.id.menu_create_group_chat);
- optionsGroupChat.setVisible(true);
- final MenuItem optionsEnhancedscreen = menu.findItem(R.id.menu_enhancedscreen);
- if (RCSUtil.isEnhanceScreenInstalled(mContext)) {
- optionsEnhancedscreen.setVisible(true);
- } else {
- optionsEnhancedscreen.setVisible(false);
- }
- }
+
+ final MenuItem optionsGroupChat = menu.findItem(R.id.menu_create_group_chat);
+ optionsGroupChat.setVisible(mOptionsMenuRcsSupported);
+
+ final MenuItem optionsEnhancedscreen = menu.findItem(R.id.menu_enhancedscreen);
+ optionsEnhancedscreen.setVisible(mOptionsMenuRcsEnhanceScreenSupported);
final MenuItem editMenu = menu.findItem(R.id.menu_edit_group);
editMenu.setVisible(mOptionsMenuGroupEditable);
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index 184b1cb2e..ca8d5801a 100755
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -126,6 +126,9 @@ public abstract class ContactBrowseListFragment extends
Log.e(TAG, "Error: No contact ID or lookup key for contact " + mUri);
return null;
+ } catch (Exception e) {
+ Log.e(TAG, "Error loading the contact: " + mUri, e);
+ return null;
} finally {
if (cursor != null) {
cursor.close();
diff --git a/src/com/android/contacts/list/JoinContactListFragment.java b/src/com/android/contacts/list/JoinContactListFragment.java
index f3788a410..3e42fdf51 100644
--- a/src/com/android/contacts/list/JoinContactListFragment.java
+++ b/src/com/android/contacts/list/JoinContactListFragment.java
@@ -81,8 +81,11 @@ public class JoinContactListFragment extends ContactEntryListFragment<JoinContac
break;
}
case JoinContactListAdapter.PARTITION_ALL_CONTACTS: {
- Cursor suggestionsCursor = ((JoinContactLoaderResult) data).suggestionCursor;
- onContactListLoaded(suggestionsCursor, data);
+ if (data != null) {
+ final Cursor suggestionsCursor =
+ ((JoinContactLoaderResult) data).suggestionCursor;
+ onContactListLoaded(suggestionsCursor, data);
+ }
break;
}
}
diff --git a/src/com/android/contacts/list/JoinContactLoader.java b/src/com/android/contacts/list/JoinContactLoader.java
index beb52085e..075d789db 100644
--- a/src/com/android/contacts/list/JoinContactLoader.java
+++ b/src/com/android/contacts/list/JoinContactLoader.java
@@ -52,9 +52,13 @@ public class JoinContactLoader extends CursorLoader {
@Override
public void close() {
try {
- suggestionCursor.close();
+ if (suggestionCursor != null) {
+ suggestionCursor.close();
+ }
} finally {
- super.close();
+ if (super.getWrappedCursor() != null) {
+ super.close();
+ }
}
}
}
@@ -79,6 +83,23 @@ public class JoinContactLoader extends CursorLoader {
// to load the entire list
final Cursor suggestionsCursor = getContext().getContentResolver()
.query(mSuggestionUri, mProjection, null, null, null);
- return new JoinContactLoaderResult(super.loadInBackground(), suggestionsCursor);
+ if (suggestionsCursor == null) {
+ return null;
+ }
+ Cursor cursorToClose = suggestionsCursor;
+ try {
+ final Cursor baseCursor = super.loadInBackground();
+ if (baseCursor != null) {
+ final JoinContactLoaderResult result =
+ new JoinContactLoaderResult(baseCursor, suggestionsCursor);
+ cursorToClose = null;
+ return result;
+ }
+ } finally {
+ if (cursorToClose != null) {
+ cursorToClose.close();
+ }
+ }
+ return null;
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/contacts/quickcontact/MyQrcodeActivity.java b/src/com/android/contacts/quickcontact/MyQrcodeActivity.java
index eaa89edb6..d1b69820b 100644
--- a/src/com/android/contacts/quickcontact/MyQrcodeActivity.java
+++ b/src/com/android/contacts/quickcontact/MyQrcodeActivity.java
@@ -212,8 +212,7 @@ public class MyQrcodeActivity extends Activity {
myProfile = RCSUtil.createLocalProfile(mRawContact);
updateDisplayNumber(myProfile);
if (!decodeStringAndSetBitmap(imgString)) {
- if (null != myProfile.getFirstName()
- || !TextUtils.isEmpty(myProfile.getFirstName())) {
+ if (null != myProfile || !TextUtils.isEmpty(myProfile.getFirstName())) {
createProgressDialog();
// downloadProfile(myProfile);
getQRcodeFromService(myProfile);
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 58941808f..85d8f2fb0 100644..100755
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -171,10 +171,10 @@ import com.android.contacts.util.StructuredPostalUtils;
import com.android.contacts.widget.MultiShrinkScroller;
import com.android.contacts.widget.MultiShrinkScroller.MultiShrinkScrollerListener;
import com.android.contacts.widget.QuickContactImageView;
-import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.ImmutableList;
+import java.lang.SecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -184,6 +184,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Mostly translucent {@link Activity} that shows QuickContact dialog. It loads
@@ -348,7 +349,13 @@ public class QuickContactActivity extends ContactsActivity {
LOADER_SMS_ID,
LOADER_CALENDAR_ID,
LOADER_CALL_LOG_ID};
- private Map<Integer, List<ContactInteraction>> mRecentLoaderResults = new HashMap<>();
+ /**
+ * ConcurrentHashMap constructor params: 4 is initial table size, 0.9f is
+ * load factor before resizing, 1 means we only expect a single thread to
+ * write to the map so make only a single shard
+ */
+ private Map<Integer, List<ContactInteraction>> mRecentLoaderResults =
+ new ConcurrentHashMap<>(4, 0.9f, 1);
private static final String FRAGMENT_TAG_SELECT_ACCOUNT = "select_account_fragment";
@@ -416,7 +423,17 @@ public class QuickContactActivity extends ContactsActivity {
getWindow().setDimAmount(mWindowScrim.getAlpha() / DEFAULT_SCRIM_ALPHA);
mHasIntentLaunched = true;
- startActivity(intent);
+ try {
+ startActivity(intent);
+ } catch (SecurityException ex) {
+ Toast.makeText(QuickContactActivity.this, R.string.missing_app,
+ Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "QuickContacts does not have permission to launch "
+ + intent);
+ } catch (ActivityNotFoundException ex) {
+ Toast.makeText(QuickContactActivity.this, R.string.missing_app,
+ Toast.LENGTH_SHORT).show();
+ }
}
};
@@ -933,7 +950,11 @@ public class QuickContactActivity extends ContactsActivity {
QuickContact.MODE_LARGE);
final Uri oldLookupUri = mLookupUri;
- mLookupUri = Preconditions.checkNotNull(lookupUri, "missing lookupUri");
+ if (lookupUri == null) {
+ finish();
+ return;
+ }
+ mLookupUri = lookupUri;
mExcludeMimes = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
if (oldLookupUri == null) {
mContactLoader = (ContactLoader) getLoaderManager().initLoader(
@@ -941,16 +962,21 @@ public class QuickContactActivity extends ContactsActivity {
} else if (oldLookupUri != mLookupUri) {
// After copying a directory contact, the contact URI changes. Therefore,
// we need to restart the loader and reload the new contact.
- for (int interactionLoaderId : mRecentLoaderIds) {
- getLoaderManager().destroyLoader(interactionLoaderId);
- }
+ destroyInteractionLoaders();
mContactLoader = (ContactLoader) getLoaderManager().restartLoader(
LOADER_CONTACT_ID, null, mLoaderContactCallbacks);
+ mCachedCp2DataCardModel = null;
}
NfcHandler.register(this, mLookupUri);
}
+ private void destroyInteractionLoaders() {
+ for (int interactionLoaderId : mRecentLoaderIds) {
+ getLoaderManager().destroyLoader(interactionLoaderId);
+ }
+ }
+
private void runEntranceAnimation() {
if (mHasAlreadyBeenOpened) {
return;
@@ -1131,6 +1157,13 @@ public class QuickContactActivity extends ContactsActivity {
if (mContactCard != null) {
mContactCard.isFireWallInstalled(isFireWallInstalled);
}
+ // When exiting the activity and resuming, we want to force a full reload of all the
+ // interaction data in case something changed in the background. On screen rotation,
+ // we don't need to do this. And, mCachedCp2DataCardModel will be null, so we won't.
+ if (mCachedCp2DataCardModel != null) {
+ destroyInteractionLoaders();
+ startInteractionLoaders(mCachedCp2DataCardModel);
+ }
}
private void populateContactAndAboutCard(Cp2DataCardModel cp2DataCardModel) {
@@ -1837,7 +1870,7 @@ public class QuickContactActivity extends ContactsActivity {
@Override
protected MaterialPalette doInBackground(Void... params) {
- if (imageViewDrawable instanceof BitmapDrawable
+ if (imageViewDrawable instanceof BitmapDrawable && mContactData != null
&& mContactData.getThumbnailPhotoBinaryData() != null
&& mContactData.getThumbnailPhotoBinaryData().length > 0) {
// Perform the color analysis on the thumbnail instead of the full sized
@@ -1994,18 +2027,21 @@ public class QuickContactActivity extends ContactsActivity {
return;
}
if (data.isError()) {
- // This shouldn't ever happen, so throw an exception. The {@link ContactLoader}
- // should log the actual exception.
- throw new IllegalStateException("Failed to load contact", data.getException());
+ // This means either the contact is invalid or we had an
+ // internal error such as an acore crash.
+ Log.i(TAG, "Failed to load contact: " + ((ContactLoader)loader).getLookupUri());
+ Toast.makeText(QuickContactActivity.this, R.string.invalidContactMessage,
+ Toast.LENGTH_LONG).show();
+ finish();
}
if (data.isNotFound()) {
if (!mHasAlreadyBeenOpened) {
Log.i(TAG, "No contact found: " + ((ContactLoader)loader).getLookupUri());
Toast.makeText(QuickContactActivity.this, R.string.invalidContactMessage,
Toast.LENGTH_LONG).show();
- }
- finish();
- return;
+ }
+ finish();
+ return;
}
bindContactData(data);
@@ -2223,6 +2259,11 @@ public class QuickContactActivity extends ContactsActivity {
startActivityForResult(getEditContactIntent(), REQUEST_CODE_CONTACT_EDITOR_ACTIVITY);
}
+ private void deleteContact() {
+ final Uri contactUri = mContactData.getLookupUri();
+ ContactDeletionInteraction.start(this, contactUri, /* finishActivityWhenDone =*/ true);
+ }
+
private void toggleStar(MenuItem starredMenuItem) {
// Make sure there is a contact
if (mContactData != null) {
@@ -2327,6 +2368,9 @@ public class QuickContactActivity extends ContactsActivity {
}
private boolean isShortcutCreatable() {
+ if (mContactData == null || mContactData.isUserProfile()) {
+ return false;
+ }
final Intent createShortcutIntent = new Intent();
createShortcutIntent.setAction(ACTION_INSTALL_SHORTCUT);
final List<ResolveInfo> receivers = getPackageManager()
@@ -2455,6 +2499,9 @@ public class QuickContactActivity extends ContactsActivity {
insertContactFromQrcodMenuItem.setVisible(false);
}
+ final MenuItem deleteMenuItem = menu.findItem(R.id.menu_delete);
+ deleteMenuItem.setVisible(isContactEditable());
+
final MenuItem shareMenuItem = menu.findItem(R.id.menu_share);
shareMenuItem.setVisible(isContactShareable());
@@ -2620,8 +2667,13 @@ public class QuickContactActivity extends ContactsActivity {
editContact();
}
return true;
+ case R.id.menu_delete:
+ deleteContact();
+ return true;
case R.id.menu_share:
- shareContact();
+ if (isContactShareable()) {
+ shareContact();
+ }
return true;
case R.id.menu_send_via_sms: {
if (mContactData == null) {
diff --git a/src/com/android/contacts/util/ImageViewDrawableSetter.java b/src/com/android/contacts/util/ImageViewDrawableSetter.java
index a7dc10295..d3d3dca78 100644
--- a/src/com/android/contacts/util/ImageViewDrawableSetter.java
+++ b/src/com/android/contacts/util/ImageViewDrawableSetter.java
@@ -108,9 +108,10 @@ public class ImageViewDrawableSetter {
return previousBitmap();
}
- final Drawable newDrawable = (compressed == null)
- ? defaultDrawable(c,account)
- : decodedBitmapDrawable(compressed);
+ Drawable newDrawable = decodedBitmapDrawable(compressed);
+ if (newDrawable == null) {
+ newDrawable = defaultDrawable(c,account);
+ }
// Remember this for next time, so that we can check if it changed.
mCompressed = compressed;
@@ -171,8 +172,14 @@ public class ImageViewDrawableSetter {
}
private BitmapDrawable decodedBitmapDrawable(byte[] compressed) {
+ if (compressed == null) {
+ return null;
+ }
final Resources rsrc = mTarget.getResources();
Bitmap bitmap = BitmapFactory.decodeByteArray(compressed, 0, compressed.length);
+ if (bitmap == null) {
+ return null;
+ }
if (bitmap.getHeight() != bitmap.getWidth()) {
// Crop the bitmap into a square.
final int size = Math.min(bitmap.getWidth(), bitmap.getHeight());
diff --git a/src/com/android/contacts/widget/MultiShrinkScroller.java b/src/com/android/contacts/widget/MultiShrinkScroller.java
index dfec204b6..68303ed89 100644
--- a/src/com/android/contacts/widget/MultiShrinkScroller.java
+++ b/src/com/android/contacts/widget/MultiShrinkScroller.java
@@ -142,6 +142,7 @@ public class MultiShrinkScroller extends FrameLayout {
private final int mMaximumTitleMargin;
private final float mToolbarElevation;
private final boolean mIsTwoPanel;
+ private final float mLandscapePhotoRatio;
private final int mActionBarSize;
// Objects used to perform color filtering on the header. These are stored as fields for
@@ -251,6 +252,11 @@ public class MultiShrinkScroller extends FrameLayout {
mMaximumTitleMargin = (int) getResources().getDimension(
R.dimen.quickcontact_title_initial_margin);
+ final TypedValue photoRatio = new TypedValue();
+ getResources().getValue(R.dimen.quickcontact_landscape_photo_ratio, photoRatio,
+ /* resolveRefs = */ true);
+ mLandscapePhotoRatio = photoRatio.getFloat();
+
final TypedArray attributeArray = context.obtainStyledAttributes(
new int[]{android.R.attr.actionBarSize});
mActionBarSize = attributeArray.getDimensionPixelSize(0, 0);
@@ -317,9 +323,7 @@ public class MultiShrinkScroller extends FrameLayout {
mIntermediateHeaderHeight = (int) (mMaximumHeaderHeight
* INTERMEDIATE_HEADER_HEIGHT_RATIO);
}
- final boolean isLandscape = getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
- mMaximumPortraitHeaderHeight = isLandscape ? getHeight()
+ mMaximumPortraitHeaderHeight = mIsTwoPanel ? getHeight()
: mPhotoViewContainer.getWidth();
setHeaderHeight(getMaximumScrollableHeaderHeight());
mMaximumHeaderTextSize = mLargeTextView.getHeight();
@@ -329,13 +333,10 @@ public class MultiShrinkScroller extends FrameLayout {
mIntermediateHeaderHeight = mMaximumHeaderHeight;
// Permanently set photo width and height.
- final TypedValue photoRatio = new TypedValue();
- getResources().getValue(R.vals.quickcontact_photo_ratio, photoRatio,
- /* resolveRefs = */ true);
final ViewGroup.LayoutParams photoLayoutParams
= mPhotoViewContainer.getLayoutParams();
photoLayoutParams.height = mMaximumHeaderHeight;
- photoLayoutParams.width = (int) (mMaximumHeaderHeight * photoRatio.getFloat());
+ photoLayoutParams.width = (int) (mMaximumHeaderHeight * mLandscapePhotoRatio);
mPhotoViewContainer.setLayoutParams(photoLayoutParams);
// Permanently set title width and margin.
@@ -389,6 +390,11 @@ public class MultiShrinkScroller extends FrameLayout {
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(event);
+
// The only time we want to intercept touch events is when we are being dragged.
return shouldStartDrag(event);
}
@@ -1157,11 +1163,8 @@ public class MultiShrinkScroller extends FrameLayout {
}
private boolean motionShouldStartDrag(MotionEvent event) {
- final float deltaX = event.getX() - mLastEventPosition[0];
final float deltaY = event.getY() - mLastEventPosition[1];
- final boolean draggedX = (deltaX > mTouchSlop || deltaX < -mTouchSlop);
- final boolean draggedY = (deltaY > mTouchSlop || deltaY < -mTouchSlop);
- return draggedY && !draggedX;
+ return deltaY > mTouchSlop || deltaY < -mTouchSlop;
}
private float updatePositionAndComputeDelta(MotionEvent event) {