Merge "Protect against class cast exception" into klp-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 55cdea4..3723fd7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -519,6 +519,9 @@
<!-- Message displayed when there is no application available to handle the add contact menu option. [CHAR LIMIT=NONE] -->
<string name="add_contact_not_available">Re-enable the People application to use this feature.</string>
+ <!-- Message displayed when there is no application available to handle voice search. [CHAR LIMIT=NONE] -->
+ <string name="voice_search_not_available">Voice search is not available.</string>
+
<!-- Hint displayed in dialer search box when there is no query that is currently typed.
[CHAR LIMIT=30] -->
<string name="dialer_hint_find_contact">Type a name or phone number</string>
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index a24940d..a8d9d6f 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -643,7 +644,13 @@
mMainActionPushLayerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- startActivity(actionIntent);
+ try {
+ startActivity(actionIntent);
+ } catch (ActivityNotFoundException e) {
+ final Toast toast = Toast.makeText(CallDetailActivity.this,
+ R.string.add_contact_not_available, Toast.LENGTH_SHORT);
+ toast.show();
+ }
}
});
mMainActionPushLayerView.setContentDescription(actionDescription);
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 04934c7..0c0fcda 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -27,6 +27,8 @@
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
@@ -48,6 +50,7 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView.OnScrollListener;
@@ -76,6 +79,7 @@
import com.android.internal.telephony.ITelephony;
import java.util.ArrayList;
+import java.util.List;
/**
* The dialer tab's title is 'phone', a more common name (see strings.xml).
@@ -330,7 +334,7 @@
hideDialpadFragment(false, true);
mInCallDialpadUp = false;
}
-
+ prepareVoiceSearchButton();
mFirstLaunch = false;
mDialerDatabaseHelper.startSmartDialUpdateThread();
}
@@ -447,8 +451,13 @@
}
break;
case R.id.voice_search_button:
- final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
- startActivityForResult(voiceIntent, ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
+ try {
+ startActivityForResult(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
+ ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(DialtactsActivity.this, R.string.voice_search_not_available,
+ Toast.LENGTH_SHORT).show();
+ }
break;
default: {
Log.wtf(TAG, "Unexpected onClick event from " + view);
@@ -507,8 +516,7 @@
mSearchViewContainer = findViewById(R.id.search_view_container);
mSearchViewCloseButton = findViewById(R.id.search_close_button);
mSearchViewCloseButton.setOnClickListener(this);
- mVoiceSearchButton = findViewById(R.id.voice_search_button);
- mVoiceSearchButton.setOnClickListener(this);
+
mSearchView = (EditText) findViewById(R.id.search_view);
mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
@@ -524,6 +532,19 @@
ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mSearchView.setHint(ssb);
+
+ prepareVoiceSearchButton();
+ }
+
+ private void prepareVoiceSearchButton() {
+ mVoiceSearchButton = findViewById(R.id.voice_search_button);
+ final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+ if (canIntentBeHandled(voiceIntent)) {
+ mVoiceSearchButton.setVisibility(View.VISIBLE);
+ mVoiceSearchButton.setOnClickListener(this);
+ } else {
+ mVoiceSearchButton.setVisibility(View.GONE);
+ }
}
final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
@@ -887,12 +908,6 @@
} else if (getInSearchUi()) {
mSearchView.setText(null);
mDialpadFragment.clearDialpad();
- } else if (isTaskRoot()) {
- // Instead of stopping, simply push this to the back of the stack.
- // This is only done when running at the top of the stack;
- // otherwise, we have been launched by someone else so need to
- // allow the user to go back to the caller.
- moveTaskToBack(false);
} else {
super.onBackPressed();
}
@@ -955,4 +970,11 @@
intent.putExtra(Intents.Insert.NAME, text);
return intent;
}
+
+ private boolean canIntentBeHandled(Intent intent) {
+ final PackageManager packageManager = getPackageManager();
+ final List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return resolveInfo != null && resolveInfo.size() > 0;
+ }
}
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsService.java b/src/com/android/dialer/calllog/CallLogNotificationsService.java
index 3270963..ccd9335 100644
--- a/src/com/android/dialer/calllog/CallLogNotificationsService.java
+++ b/src/com/android/dialer/calllog/CallLogNotificationsService.java
@@ -70,6 +70,10 @@
@Override
protected void onHandleIntent(Intent intent) {
+ if (intent == null) {
+ Log.d(TAG, "onHandleIntent: could not handle null intent");
+ return;
+ }
if (ACTION_MARK_NEW_VOICEMAILS_AS_OLD.equals(intent.getAction())) {
mCallLogQueryHandler.markNewVoicemailsAsOld();
} else if (ACTION_UPDATE_NOTIFICATIONS.equals(intent.getAction())) {
diff --git a/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java b/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java
index 9913c20..3eacfec 100644
--- a/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java
+++ b/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java
@@ -50,7 +50,7 @@
* mock-out this, it is not a static method.
*/
public boolean isVoicemailNumber(CharSequence number) {
- return PhoneNumberUtils.isVoiceMailNumber(number.toString());
+ return number!= null && PhoneNumberUtils.isVoiceMailNumber(number.toString());
}
/**
@@ -58,7 +58,7 @@
* static method.
*/
public boolean isSipNumber(CharSequence number) {
- return PhoneNumberUtils.isUriNumber(number.toString());
+ return number != null && PhoneNumberUtils.isUriNumber(number.toString());
}
public static boolean isUnknownNumberThatCanBeLookedUp(CharSequence number, int presentation) {
@@ -77,13 +77,13 @@
if (new PhoneNumberUtilsWrapper().isVoicemailNumber(number)) {
return false;
}
- if (isLegacyUnknownNumbers(number.toString())) {
+ if (isLegacyUnknownNumbers(number)) {
return false;
}
return true;
}
public static boolean isLegacyUnknownNumbers(CharSequence number) {
- return LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
+ return number != null && LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
}
}
diff --git a/src/com/android/dialer/database/DialerDatabaseHelper.java b/src/com/android/dialer/database/DialerDatabaseHelper.java
index b9e4b9a..852b7c1 100644
--- a/src/com/android/dialer/database/DialerDatabaseHelper.java
+++ b/src/com/android/dialer/database/DialerDatabaseHelper.java
@@ -628,10 +628,26 @@
updatedContactCursor.moveToPosition(-1);
while (updatedContactCursor.moveToNext()) {
- insert.bindLong(1, updatedContactCursor.getLong(PhoneQuery.PHONE_ID));
- insert.bindString(2, updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER));
- insert.bindLong(3, updatedContactCursor.getLong(PhoneQuery.PHONE_CONTACT_ID));
- insert.bindString(4, updatedContactCursor.getString(PhoneQuery.PHONE_LOOKUP_KEY));
+ insert.clearBindings();
+
+ // Handle string columns which can possibly be null first. In the case of certain
+ // null columns (due to malformed rows possibly inserted by third-party apps
+ // or sync adapters), skip the phone number row.
+ final String number = updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
+ if (TextUtils.isEmpty(number)) {
+ continue;
+ } else {
+ insert.bindString(2, number);
+ }
+
+ final String lookupKey = updatedContactCursor.getString(
+ PhoneQuery.PHONE_LOOKUP_KEY);
+ if (TextUtils.isEmpty(lookupKey)) {
+ continue;
+ } else {
+ insert.bindString(4, lookupKey);
+ }
+
final String displayName = updatedContactCursor.getString(
PhoneQuery.PHONE_DISPLAY_NAME);
if (displayName == null) {
@@ -639,6 +655,9 @@
} else {
insert.bindString(5, displayName);
}
+
+ insert.bindLong(1, updatedContactCursor.getLong(PhoneQuery.PHONE_ID));
+ insert.bindLong(3, updatedContactCursor.getLong(PhoneQuery.PHONE_CONTACT_ID));
insert.bindLong(6, updatedContactCursor.getLong(PhoneQuery.PHONE_PHOTO_ID));
insert.bindLong(7, updatedContactCursor.getLong(PhoneQuery.PHONE_LAST_TIME_USED));
insert.bindLong(8, updatedContactCursor.getInt(PhoneQuery.PHONE_TIMES_USED));
@@ -648,8 +667,6 @@
insert.bindLong(12, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_PRIMARY));
insert.bindLong(13, currentMillis);
insert.executeInsert();
- insert.clearBindings();
-
final String contactPhoneNumber =
updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
final ArrayList<String> numberPrefixes =
diff --git a/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java b/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
index 30d01d2..c7e4c5f 100644
--- a/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
+++ b/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
@@ -41,16 +41,16 @@
if (TextUtils.isEmpty(number)) {
return;
}
- final long id = getContactIdFromPhoneNumber(context, number);
- if (id != NO_CONTACT_FOUND) {
- final Thread thread = new Thread() {
- @Override
- public void run() {
+ final Thread thread = new Thread() {
+ @Override
+ public void run() {
+ final long id = getContactIdFromPhoneNumber(context, number);
+ if (id != NO_CONTACT_FOUND) {
undemoteContactWithId(context, id);
}
- };
- thread.start();
- }
+ }
+ };
+ thread.start();
}
}
@@ -68,6 +68,9 @@
Uri.encode(number));
final Cursor cursor = context.getContentResolver().query(contactUri, new String[] {
PhoneLookup._ID}, null, null, null);
+ if (cursor == null) {
+ return NO_CONTACT_FOUND;
+ }
try {
if (cursor.moveToFirst()) {
final long id = cursor.getLong(0);
diff --git a/src/com/android/dialer/list/PhoneFavoriteFragment.java b/src/com/android/dialer/list/PhoneFavoriteFragment.java
index 9e38394..81d9bfd 100644
--- a/src/com/android/dialer/list/PhoneFavoriteFragment.java
+++ b/src/com/android/dialer/list/PhoneFavoriteFragment.java
@@ -453,8 +453,11 @@
}
private void saveHorizontalOffsets(ContactTileRow row, ArrayList<ContactEntry> list) {
- for (int i = 0; i < list.size(); i++) {
+ for (int i = 0; i < list.size() && i < row.getChildCount(); i++) {
final View child = row.getChildAt(i);
+ if (child == null) {
+ continue;
+ }
final ContactEntry entry = list.get(i);
final long itemId = mContactTileAdapter.getAdjustedItemId(entry.id);
if (DEBUG) {
diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
index e28fd73..cd4fb00 100644
--- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
@@ -88,6 +88,7 @@
private long mIdToKeepInPlace = -1;
private boolean mAwaitingRemove = false;
+ private boolean mDelayCursorUpdates = false;
private ContactPhotoManager mPhotoManager;
protected int mNumFrequents;
@@ -178,6 +179,7 @@
* @param inDragging Boolean variable indicating whether there is a drag in process.
*/
public void setInDragging(boolean inDragging) {
+ mDelayCursorUpdates = inDragging;
mInDragging = inDragging;
}
@@ -224,7 +226,7 @@
* Else use {@link ContactTileLoaderFactory}
*/
public void setContactCursor(Cursor cursor) {
- if (cursor != null && !cursor.isClosed()) {
+ if (!mDelayCursorUpdates && cursor != null && !cursor.isClosed()) {
mNumStarred = getNumStarredContacts(cursor);
if (mAwaitingRemove) {
mDataSetChangedListener.cacheOffsetsForDatasetChange();