diff options
author | Matt Garnes <matt@cyngn.com> | 2015-04-30 10:33:15 -0700 |
---|---|---|
committer | Matt Garnes <matt@cyngn.com> | 2015-04-30 10:33:15 -0700 |
commit | 6825b2dc0c51e7cb9ce3ee2e5e32fec821c4c299 (patch) | |
tree | 3122cf62c8cc8e7f5b186754e61d80e8774b516b | |
parent | e00a2a6518e83990dd19c09d55fa2d59d4bfac6e (diff) | |
parent | 75209bbb8d7be819cf9dce121da371d90e5685e1 (diff) | |
download | packages_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
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 Binary files differnew file mode 100644 index 000000000..86e909905 --- /dev/null +++ b/res/drawable-hdpi/ic_trash_white_24.png diff --git a/res/drawable-mdpi/ic_trash_white_24.png b/res/drawable-mdpi/ic_trash_white_24.png Binary files differnew file mode 100644 index 000000000..fc67992ff --- /dev/null +++ b/res/drawable-mdpi/ic_trash_white_24.png diff --git a/res/drawable-xhdpi/ic_trash_white_24.png b/res/drawable-xhdpi/ic_trash_white_24.png Binary files differnew file mode 100644 index 000000000..df96462c2 --- /dev/null +++ b/res/drawable-xhdpi/ic_trash_white_24.png diff --git a/res/drawable-xxhdpi/ic_trash_white_24.png b/res/drawable-xxhdpi/ic_trash_white_24.png Binary files differnew file mode 100644 index 000000000..96acd7e9f --- /dev/null +++ b/res/drawable-xxhdpi/ic_trash_white_24.png 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) { |