am 610ead01: (-s ours) am 3cf92c74: Merge "Update Dialer permissions UI per mocks" into mnc-dev
* commit '610ead01e7c400f146d5256f51c685c274a0c512':
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9569c1d..4055b44 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,7 +16,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.dialer"
- coreApp="true">
+ coreApp="true"
+ android:versionCode="20210"
+ android:versionName="2.21">
<uses-sdk
android:minSdkVersion="23"
@@ -172,6 +174,14 @@
</intent-filter>
</activity>
+ <activity android:name="com.android.contacts.common.dialog.CallSubjectDialog"
+ android:theme="@style/Theme.CallSubjectDialogTheme"
+ android:windowSoftInputMode="stateVisible|adjustResize">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ </intent-filter>
+ </activity>
+
<!-- Backwards compatibility: "Phone" from Gingerbread and earlier -->
<activity-alias android:name="DialtactsActivity"
android:targetActivity=".DialtactsActivity"
diff --git a/res/layout/call_log_list_item_actions.xml b/res/layout/call_log_list_item_actions.xml
index b427206..16a712b 100644
--- a/res/layout/call_log_list_item_actions.xml
+++ b/res/layout/call_log_list_item_actions.xml
@@ -105,6 +105,20 @@
</LinearLayout>
<LinearLayout
+ android:id="@+id/call_with_note_action"
+ style="@style/CallLogActionStyle">
+
+ <ImageView
+ style="@style/CallLogActionIconStyle"
+ android:src="@drawable/ic_call_note_white_24dp" />
+
+ <TextView
+ style="@style/CallLogActionTextStyle"
+ android:text="@string/call_with_a_note" />
+
+ </LinearLayout>
+
+ <LinearLayout
android:id="@+id/details_action"
style="@style/CallLogActionStyle">
diff --git a/res/layout/empty_content_view.xml b/res/layout/empty_content_view.xml
index d8f27aa..97ac4c7 100644
--- a/res/layout/empty_content_view.xml
+++ b/res/layout/empty_content_view.xml
@@ -43,7 +43,6 @@
android:paddingLeft="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
- android:text="@string/permission_single_turn_on"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
style="@style/TextActionStyle" />
diff --git a/res/menu/dialpad_options.xml b/res/menu/dialpad_options.xml
index f0399a8..63fca07 100644
--- a/res/menu/dialpad_options.xml
+++ b/res/menu/dialpad_options.xml
@@ -23,5 +23,8 @@
android:id="@+id/menu_add_wait"
android:title="@string/add_wait"
android:showAsAction="withText" />
-
+ <item
+ android:id="@+id/menu_call_with_note"
+ android:title="@string/call_with_a_note"
+ android:showAsAction="withText" />
</menu>
diff --git a/res/values/ids.xml b/res/values/ids.xml
index e2f0ff5..d8f4aa8 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -17,4 +17,5 @@
<resources>
<item type="id" name="context_menu_copy_to_clipboard" />
<item type="id" name="context_menu_edit_before_call" />
+ <item type="id" name="settings_header_sounds_and_vibration" />
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 505e491..5e2476d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -797,7 +797,7 @@
<string name="permission_multiple_turn_on">Set permissions</string>
<!-- Shown as a prompt to turn on the contacts permission to enable speed dial -->
- <string name="permission_no_speeddial">To enable speed dial,\n turn on the the Contacts permission.</string>
+ <string name="permission_no_speeddial">To enable speed dial,\n turn on the Contacts permission.</string>
<!-- Shown as a prompt to turn on the phone permission to enable the call log -->
<string name="permission_no_calllog">To see your call log,\n turn on the Phone permission.</string>
@@ -808,9 +808,12 @@
<!-- Shown as a prompt to turn on the phone permission to show voicemails -->
<string name="permission_no_voicemail">To access your voicemail,\n turn on the Phone permission.</string>
- <!-- Shown as a prompt to turn on contacts and location permissions to allow contact and nearby places search -->
- <string name="permission_no_search">To search your contacts and nearby locations, turn on the Contacts and Location permissions.</string>
+ <!-- Shown as a prompt to turn on contacts permissions to allow contact search -->
+ <string name="permission_no_search">To search your contacts, turn on the Contacts permissions.</string>
<!-- Shown as a prompt to turn on the phone permission to allow a call to be placed -->
<string name="permission_place_call">To place a call,\n turn on the Phone permission.</string>
+
+ <!-- Shown as a message that notifies the user that the Phone app cannot write to system settings, which is why the system settings app is being launched directly instead.-->
+ <string name="toast_cannot_write_system_settings">Phone app does not have permission to write to system settings.</string>
</resources>
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index c6ec8e1..56f2cb1 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -50,6 +50,7 @@
import com.android.contacts.common.util.PermissionsUtil;
import com.android.contacts.common.GeoUtil;
import com.android.contacts.common.CallUtil;
+import com.android.contacts.common.util.UriUtils;
import com.android.dialer.calllog.CallDetailHistoryAdapter;
import com.android.dialer.calllog.CallLogAsyncTaskUtil.CallLogAsyncTaskListener;
import com.android.dialer.calllog.CallLogAsyncTaskUtil;
@@ -162,7 +163,7 @@
new CallDetailHistoryAdapter(mContext, mInflater, mCallTypeHelper, details));
String lookupKey = contactUri == null ? null
- : ContactInfoHelper.getLookupKeyFromUri(contactUri);
+ : UriUtils.getLookupKeyFromUri(contactUri);
final boolean isBusiness = mContactInfoHelper.isBusiness(firstDetails.sourceType);
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 85197a5..69cc146 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -103,6 +103,7 @@
DialpadFragment.OnDialpadQueryChangedListener,
OnListFragmentScrolledListener,
CallLogFragment.HostInterface,
+ DialpadFragment.HostInterface,
ListsFragment.HostInterface,
SpeedDialFragment.HostInterface,
SearchFragment.HostInterface,
@@ -486,8 +487,6 @@
}
});
- setupActivityOverlay();
-
Trace.endSection();
Trace.beginSection(TAG + " initialize smart dialing");
@@ -497,19 +496,6 @@
Trace.endSection();
}
- private void setupActivityOverlay() {
- final View activityOverlay = findViewById(R.id.activity_overlay);
- activityOverlay.setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (!mIsDialpadShown) {
- maybeExitSearchUi();
- }
- return false;
- }
- });
- }
-
@Override
protected void onResume() {
Trace.beginSection(TAG + " onResume");
@@ -622,7 +608,8 @@
public void onClick(View view) {
switch (view.getId()) {
case R.id.floating_action_button:
- if (mListsFragment.getCurrentTabIndex() == ListsFragment.TAB_INDEX_ALL_CONTACTS) {
+ if (mListsFragment.getCurrentTabIndex()
+ == ListsFragment.TAB_INDEX_ALL_CONTACTS && !mInRegularSearch) {
DialerUtils.startActivityWithErrorToast(
this,
IntentUtil.getNewContactIntent(),
@@ -1147,7 +1134,16 @@
} catch (Exception ignored) {
// Skip any exceptions for this piece of code
}
+ }
+ @Override
+ public boolean onDialpadSpacerTouchWithEmptyQuery() {
+ if (mInDialpadSearch && mSmartDialSearchFragment != null
+ && !mSmartDialSearchFragment.isShowingPermissionRequest()) {
+ hideDialpadFragment(true /* animate */, true /* clearDialpad */);
+ return true;
+ }
+ return false;
}
@Override
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 458c90b..5a87bc8 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -22,6 +22,8 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.support.v7.widget.RecyclerView;
import android.os.Bundle;
import android.os.Trace;
@@ -65,7 +67,8 @@
* Adapter class to fill in data for the Call Log.
*/
public class CallLogAdapter extends GroupingListAdapter
- implements ViewTreeObserver.OnPreDrawListener, CallLogGroupBuilder.GroupCreator {
+ implements CallLogGroupBuilder.GroupCreator,
+ VoicemailPlaybackPresenter.OnVoicemailDeletedListener {
/** Interface used to initiate a refresh of the content. */
public interface CallFetcher {
@@ -94,7 +97,6 @@
private final ContactInfoHelper mContactInfoHelper;
private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
private final CallFetcher mCallFetcher;
- private ViewTreeObserver mViewTreeObserver = null;
protected ContactInfoCache mContactInfoCache;
@@ -151,7 +153,7 @@
if (mVoicemailPlaybackPresenter != null) {
// Always reset the voicemail playback state on expand or collapse.
- mVoicemailPlaybackPresenter.reset();
+ mVoicemailPlaybackPresenter.resetAll();
}
if (viewHolder.getAdapterPosition() == mCurrentlyExpandedPosition) {
@@ -292,16 +294,6 @@
}
};
- @Override
- public boolean onPreDraw() {
- // We only wanted to listen for the first draw (and this is it).
- unregisterPreDrawListener();
- if (PermissionsUtil.hasContactsPermissions(mContext)) {
- mContactInfoCache.start();
- }
- return true;
- }
-
public CallLogAdapter(
Context context,
CallFetcher callFetcher,
@@ -314,6 +306,9 @@
mCallFetcher = callFetcher;
mContactInfoHelper = contactInfoHelper;
mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
+ if (mVoicemailPlaybackPresenter != null) {
+ mVoicemailPlaybackPresenter.setOnVoicemailDeletedListener(this);
+ }
mIsShowingRecentsTab = isShowingRecentsTab;
mContactInfoCache = new ContactInfoCache(
@@ -370,21 +365,14 @@
}
}
- /**
- * Stop receiving onPreDraw() notifications.
- */
- private void unregisterPreDrawListener() {
- if (mViewTreeObserver != null && mViewTreeObserver.isAlive()) {
- mViewTreeObserver.removeOnPreDrawListener(this);
- }
- mViewTreeObserver = null;
- }
-
public void invalidateCache() {
mContactInfoCache.invalidate();
+ }
- // Restart the request-processing thread after the next draw.
- unregisterPreDrawListener();
+ public void startCache() {
+ if (PermissionsUtil.hasPermission(mContext, android.Manifest.permission.READ_CONTACTS)) {
+ mContactInfoCache.start();
+ }
}
public void pauseCache() {
@@ -540,13 +528,16 @@
views.rowId = c.getLong(CallLogQuery.ID);
// Store values used when the actions ViewStub is inflated on expansion.
views.number = number;
+ views.displayNumber = details.displayNumber;
views.numberPresentation = numberPresentation;
views.callType = c.getInt(CallLogQuery.CALL_TYPE);
views.accountHandle = accountHandle;
views.voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
// Stash away the Ids of the calls so that we can support deleting a row in the call log.
views.callIds = getCallIds(c, count);
-
+ views.isBusiness = mContactInfoHelper.isBusiness(info.sourceType);
+ views.numberType = (String) Phone.getTypeLabel(mContext.getResources(), details.numberType,
+ details.numberLabel);
// Default case: an item in the call log.
views.primaryActionView.setVisibility(View.VISIBLE);
@@ -577,15 +568,9 @@
nameForDefaultImage = info.name;
}
views.setPhoto(info.photoId, info.photoUri, info.lookupUri, nameForDefaultImage,
- isVoicemailNumber, mContactInfoHelper.isBusiness(info.sourceType));
+ isVoicemailNumber, views.isBusiness);
mCallLogListItemHelper.setPhoneCallDetails(views, details);
-
- // Listen for the first draw
- if (mViewTreeObserver == null) {
- mViewTreeObserver = views.rootView.getViewTreeObserver();
- mViewTreeObserver.addOnPreDrawListener(this);
- }
}
@Override
@@ -619,6 +604,12 @@
return mIsShowingRecentsTab;
}
+ @Override
+ public void onVoicemailDeleted(Uri uri) {
+ mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
+ mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
+ }
+
/**
* Retrieves the day group of the previous call in the call log. Used to determine if the day
* group has changed and to trigger display of the day group text.
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index d1a9827..e7b7764 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -338,6 +338,7 @@
}
mHasReadCallLogPermission = hasReadCallLogPermission;
refreshData();
+ mAdapter.startCache();
}
@Override
@@ -486,7 +487,7 @@
}
@Override
- public void onEmptyViewActionButtonClicked(String[] permissions) {
+ public void onEmptyViewActionButtonClicked() {
final Activity activity = getActivity();
if (activity == null) {
return;
diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
index 361e1c7..0fa5e6d 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
@@ -16,6 +16,7 @@
package com.android.dialer.calllog;
+import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.content.Intent;
@@ -29,18 +30,16 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
-import android.view.ViewTreeObserver;
import android.widget.QuickContactBadge;
import android.widget.ImageView;
import android.widget.TextView;
-import com.android.contacts.common.CallUtil;
import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.dialog.CallSubjectDialog;
import com.android.contacts.common.testing.NeededForTesting;
import com.android.contacts.common.util.UriUtils;
import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogAsyncTaskUtil;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.PhoneNumberUtil;
import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
@@ -80,6 +79,7 @@
public View addToExistingContactButtonView;
public View sendMessageView;
public View detailsButtonView;
+ public View callWithNoteButtonView;
/**
* The row Id for the first call associated with the call log entry. Used as a key for the
@@ -100,12 +100,22 @@
public String number;
/**
+ * The formatted phone number to display.
+ */
+ public String displayNumber;
+
+ /**
* The phone number presentation for the current call log entry. Cached here as the call back
* intent is set only when the actions ViewStub is inflated.
*/
public int numberPresentation;
/**
+ * The type of the phone number (e.g. main, work, etc).
+ */
+ public String numberType;
+
+ /**
* The type of call for the current call log entry. Cached here as the call back
* intent is set only when the actions ViewStub is inflated.
*/
@@ -131,6 +141,11 @@
public CharSequence nameOrNumber;
/**
+ * Whether this row is for a business or not.
+ */
+ public boolean isBusiness;
+
+ /**
* The contact info for the contact displayed in this list item.
*/
public ContactInfo info;
@@ -245,6 +260,9 @@
detailsButtonView = actionsView.findViewById(R.id.details_action);
detailsButtonView.setOnClickListener(this);
+
+ callWithNoteButtonView = actionsView.findViewById(R.id.call_with_note_action);
+ callWithNoteButtonView.setOnClickListener(this);
}
bindActionButtons();
@@ -349,6 +367,13 @@
sendMessageView.setTag(IntentProvider.getSendSmsIntentProvider(number));
mCallLogListItemHelper.setActionContentDescriptions(this);
+
+ boolean supportsCallSubject =
+ mTelecomCallLogCache.doesAccountSupportCallSubject(accountHandle);
+ boolean isVoicemailNumber =
+ mTelecomCallLogCache.isVoicemailNumber(accountHandle, number);
+ callWithNoteButtonView.setVisibility(
+ supportsCallSubject && !isVoicemailNumber ? View.VISIBLE : View.GONE);
}
/**
@@ -403,7 +428,7 @@
String lookupKey = null;
if (contactUri != null) {
- lookupKey = ContactInfoHelper.getLookupKeyFromUri(contactUri);
+ lookupKey = UriUtils.getLookupKeyFromUri(contactUri);
}
DefaultImageRequest request = new DefaultImageRequest(
@@ -423,6 +448,19 @@
if (view.getId() == R.id.primary_action_button && !TextUtils.isEmpty(voicemailUri)) {
mVoicemailPrimaryActionButtonClicked = true;
mExpandCollapseListener.onClick(primaryActionView);
+ } else if (view.getId() == R.id.call_with_note_action) {
+ CallSubjectDialog.start(
+ (Activity) mContext,
+ info.photoId,
+ info.photoUri,
+ info.lookupUri,
+ (String) nameOrNumber /* top line of contact view in call subject dialog */,
+ isBusiness,
+ number, /* callable number used for ACTION_CALL intent */
+ TextUtils.isEmpty(info.name) ? null : displayNumber, /* second line of contact
+ view in dialog. */
+ numberType, /* phone number type (e.g. mobile) in second line of contact view */
+ accountHandle);
} else {
final IntentProvider intentProvider = (IntentProvider) view.getTag();
if (intentProvider != null) {
diff --git a/src/com/android/dialer/calllog/ContactInfoHelper.java b/src/com/android/dialer/calllog/ContactInfoHelper.java
index 9a660e1..2e07a03 100644
--- a/src/com/android/dialer/calllog/ContactInfoHelper.java
+++ b/src/com/android/dialer/calllog/ContactInfoHelper.java
@@ -298,6 +298,10 @@
*/
public void updateCallLogContactInfo(String number, String countryIso, ContactInfo updatedInfo,
ContactInfo callLogInfo) {
+ if (!PermissionsUtil.hasPermission(mContext, android.Manifest.permission.WRITE_CALL_LOG)) {
+ return;
+ }
+
final ContentValues values = new ContentValues();
boolean needsUpdate = false;
@@ -390,24 +394,6 @@
}
/**
- * Parses the given URI to determine the original lookup key of the contact.
- */
- public static String getLookupKeyFromUri(Uri lookupUri) {
- // Would be nice to be able to persist the lookup key somehow to avoid having to parse
- // the uri entirely just to retrieve the lookup key, but every uri is already parsed
- // once anyway to check if it is an encoded JSON uri, so this has negligible effect
- // on performance.
- if (lookupUri != null && !UriUtils.isEncodedContactUri(lookupUri)) {
- final List<String> segments = lookupUri.getPathSegments();
- // This returns the third path segment of the uri, where the lookup key is located.
- // See {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
- return (segments.size() < 3) ? null : Uri.encode(segments.get(2));
- } else {
- return null;
- }
- }
-
- /**
* Returns the contact information stored in an entry of the call log.
*
* @param c A cursor pointing to an entry in the call log.
diff --git a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
index d0553b4..a6d165e 100644
--- a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
+++ b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
@@ -302,6 +302,9 @@
newCalls[cursor.getPosition()] = createNewCallsFromCursor(cursor);
}
return newCalls;
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Exception when querying Contacts Provider for calls lookup");
+ return null;
} finally {
MoreCloseables.closeQuietly(cursor);
}
@@ -371,6 +374,9 @@
PROJECTION, null, null, null);
if (cursor == null || !cursor.moveToFirst()) return null;
return cursor.getString(DISPLAY_NAME_COLUMN_INDEX);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Exception when querying Contacts Provider for name lookup");
+ return null;
} finally {
if (cursor != null) {
cursor.close();
diff --git a/src/com/android/dialer/calllog/PhoneAccountUtils.java b/src/com/android/dialer/calllog/PhoneAccountUtils.java
index 7eaa523..143d13e 100644
--- a/src/com/android/dialer/calllog/PhoneAccountUtils.java
+++ b/src/com/android/dialer/calllog/PhoneAccountUtils.java
@@ -84,6 +84,21 @@
}
/**
+ * Determine whether a phone account supports call subjects.
+ *
+ * @return {@code true} if call subjects are supported, {@code false} otherwise.
+ */
+ public static boolean getAccountSupportsCallSubject(Context context,
+ PhoneAccountHandle accountHandle) {
+ TelecomManager telecomManager =
+ (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ final PhoneAccount account = telecomManager.getPhoneAccount(accountHandle);
+
+ return account == null ? false :
+ account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT);
+ }
+
+ /**
* Retrieve the account metadata, but if the account does not exist or the device has only a
* single registered and enabled account, return null.
*/
diff --git a/src/com/android/dialer/calllog/TelecomCallLogCache.java b/src/com/android/dialer/calllog/TelecomCallLogCache.java
index ec1d241..7071669 100644
--- a/src/com/android/dialer/calllog/TelecomCallLogCache.java
+++ b/src/com/android/dialer/calllog/TelecomCallLogCache.java
@@ -52,6 +52,7 @@
new HashMap<>();
private final Map<PhoneAccountHandle, String> mPhoneAccountLabelCache = new HashMap<>();
private final Map<PhoneAccountHandle, Integer> mPhoneAccountColorCache = new HashMap<>();
+ private final Map<PhoneAccountHandle, Boolean> mPhoneAccountCallWithNoteCache = new HashMap<>();
private boolean mHasCheckedForVideoEnabled;
private boolean mIsVideoEnabled;
@@ -64,6 +65,7 @@
mVoicemailQueryCache.clear();
mPhoneAccountLabelCache.clear();
mPhoneAccountColorCache.clear();
+ mPhoneAccountCallWithNoteCache.clear();
mHasCheckedForVideoEnabled = false;
mIsVideoEnabled = false;
@@ -121,4 +123,22 @@
}
return mIsVideoEnabled;
}
+
+ /**
+ * Determines if the PhoneAccount supports specifying a call subject (i.e. calling with a note)
+ * for outgoing calls.
+ *
+ * @param accountHandle The PhoneAccount handle.
+ * @return {@code true} if calling with a note is supported, {@code false} otherwise.
+ */
+ public boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle) {
+ if (mPhoneAccountCallWithNoteCache.containsKey(accountHandle)) {
+ return mPhoneAccountCallWithNoteCache.get(accountHandle);
+ } else {
+ Boolean supportsCallWithNote =
+ PhoneAccountUtils.getAccountSupportsCallSubject(mContext, accountHandle);
+ mPhoneAccountCallWithNoteCache.put(accountHandle, supportsCallWithNote);
+ return supportsCallWithNote;
+ }
+ }
}
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index b18069f..3792a1d 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -50,6 +50,7 @@
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -67,7 +68,9 @@
import android.widget.RelativeLayout;
import android.widget.TextView;
+import com.android.contacts.common.CallUtil;
import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.dialog.CallSubjectDialog;
import com.android.contacts.common.util.PermissionsUtil;
import com.android.contacts.common.util.PhoneNumberFormatter;
import com.android.contacts.common.util.StopWatch;
@@ -80,7 +83,6 @@
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.IntentUtil;
import com.android.phone.common.CallLogAsync;
-import com.android.phone.common.HapticFeedback;
import com.android.phone.common.animation.AnimUtils;
import com.android.phone.common.dialpad.DialpadKeyButton;
import com.android.phone.common.dialpad.DialpadView;
@@ -135,6 +137,15 @@
void onDialpadQueryChanged(String query);
}
+ public interface HostInterface {
+ /**
+ * Notifies the parent activity that the space above the dialpad has been tapped with
+ * no query in the dialpad present. In most situations this will cause the dialpad to
+ * be dismissed, unless there happens to be content showing.
+ */
+ boolean onDialpadSpacerTouchWithEmptyQuery();
+ }
+
private static final boolean DEBUG = DialtactsActivity.DEBUG;
// This is the amount of screen the dialpad fragment takes up when fully displayed
@@ -197,9 +208,6 @@
// determines if we want to playback local DTMF tones.
private boolean mDTMFToneEnabled;
- // Vibration (haptic feedback) for dialer key presses.
- private final HapticFeedback mHaptic = new HapticFeedback();
-
/** Identifier for the "Add Call" intent extra. */
private static final String ADD_CALL_MODE_KEY = "add_call_mode";
@@ -319,13 +327,6 @@
mCurrentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
- try {
- mHaptic.init(getActivity(),
- getResources().getBoolean(R.bool.config_enable_dialer_key_vibration));
- } catch (Resources.NotFoundException nfe) {
- Log.e(TAG, "Vibrate control bool missing.", nfe);
- }
-
mProhibitedPhoneNumberRegexp = getResources().getString(
R.string.config_prohibited_phone_number_regexp);
@@ -385,7 +386,9 @@
@Override
public boolean onTouch(View v, MotionEvent event) {
if (isDigitsEmpty()) {
- hideAndClearDialpad(true);
+ if (getActivity() != null) {
+ return ((HostInterface) getActivity()).onDialpadSpacerTouchWithEmptyQuery();
+ }
return true;
}
return false;
@@ -638,9 +641,6 @@
stopWatch.lap("dtwd");
- // Retrieve the haptic feedback setting.
- mHaptic.checkSystemSetting();
-
stopWatch.lap("hptc");
mPressedDialpadKeys.clear();
@@ -774,7 +774,7 @@
break;
}
- mHaptic.vibrate();
+ getView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
mDigits.onKeyDown(keyCode, event);
@@ -885,9 +885,12 @@
boolean enable = !isDigitsEmpty();
for (int i = 0; i < menu.size(); i++) {
- menu.getItem(i).setEnabled(enable);
+ MenuItem item = menu.getItem(i);
+ item.setEnabled(enable);
+ if (item.getItemId() == R.id.menu_call_with_note) {
+ item.setVisible(CallUtil.isCallWithSubjectSupported(getContext()));
+ }
}
-
super.show();
}
};
@@ -900,7 +903,7 @@
public void onClick(View view) {
switch (view.getId()) {
case R.id.dialpad_floating_action_button:
- mHaptic.vibrate();
+ view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
handleDialButtonPressed();
break;
case R.id.deleteButton: {
@@ -1459,6 +1462,10 @@
case R.id.menu_add_wait:
updateDialString(WAIT);
return true;
+ case R.id.menu_call_with_note:
+ CallSubjectDialog.start(getActivity(), mDigits.getText().toString());
+ hideAndClearDialpad(false);
+ return true;
default:
return false;
}
diff --git a/src/com/android/dialer/list/AllContactsFragment.java b/src/com/android/dialer/list/AllContactsFragment.java
index 19d8a43..0f31ff8 100644
--- a/src/com/android/dialer/list/AllContactsFragment.java
+++ b/src/com/android/dialer/list/AllContactsFragment.java
@@ -19,6 +19,9 @@
import static android.Manifest.permission.READ_CONTACTS;
import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
import android.content.Loader;
import android.content.pm.PackageManager;
import android.database.Cursor;
@@ -52,6 +55,17 @@
private EmptyContentView mEmptyListView;
+ /**
+ * Listen to broadcast events about permissions in order to be notified if the READ_CONTACTS
+ * permission is granted via the UI in another fragment.
+ */
+ private BroadcastReceiver mReadContactsPermissionGrantedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ reloadData();
+ }
+ };
+
public AllContactsFragment() {
setQuickContactEnabled(false);
setAdjustSelectionBoundsEnabled(true);
@@ -76,6 +90,20 @@
}
@Override
+ public void onStart() {
+ super.onStart();
+ PermissionsUtil.registerPermissionReceiver(getActivity(),
+ mReadContactsPermissionGrantedReceiver, READ_CONTACTS);
+ }
+
+ @Override
+ public void onStop() {
+ PermissionsUtil.unregisterPermissionReceiver(getActivity(),
+ mReadContactsPermissionGrantedReceiver);
+ super.onStop();
+ }
+
+ @Override
protected void startLoading() {
if (PermissionsUtil.hasPermission(getActivity(), READ_CONTACTS)) {
super.startLoading();
@@ -133,7 +161,7 @@
}
@Override
- public void onEmptyViewActionButtonClicked(String[] permissions) {
+ public void onEmptyViewActionButtonClicked() {
final Activity activity = getActivity();
if (activity == null) {
return;
diff --git a/src/com/android/dialer/list/RegularSearchFragment.java b/src/com/android/dialer/list/RegularSearchFragment.java
index 19c7321..b7e26d6 100644
--- a/src/com/android/dialer/list/RegularSearchFragment.java
+++ b/src/com/android/dialer/list/RegularSearchFragment.java
@@ -15,16 +15,29 @@
*/
package com.android.dialer.list;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.READ_CONTACTS;
+
+import android.app.Activity;
+import android.content.pm.PackageManager;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import com.android.contacts.common.list.ContactEntryListAdapter;
import com.android.contacts.common.list.PinnedHeaderListView;
+import com.android.contacts.common.util.PermissionsUtil;
import com.android.contacts.commonbind.analytics.AnalyticsUtil;
import com.android.dialerbind.ObjectFactory;
-import com.android.dialer.service.CachedNumberLookupService;
-public class RegularSearchFragment extends SearchFragment {
+import com.android.dialer.R;
+import com.android.dialer.service.CachedNumberLookupService;
+import com.android.dialer.widget.EmptyContentView;
+import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
+
+public class RegularSearchFragment extends SearchFragment
+ implements OnEmptyViewActionButtonClickedListener {
+
+ private static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 1;
private static final int SEARCH_DIRECTORY_RESULT_LIMIT = 5;
@@ -68,4 +81,38 @@
adapter.getContactInfo(mCachedNumberLookupService, position));
}
}
+
+ @Override
+ protected void setupEmptyView() {
+ if (mEmptyView != null && getActivity() != null) {
+ if (!PermissionsUtil.hasPermission(getActivity(), READ_CONTACTS)) {
+ mEmptyView.setImage(R.drawable.empty_contacts);
+ mEmptyView.setActionLabel(R.string.permission_single_turn_on);
+ mEmptyView.setDescription(R.string.permission_no_search);
+ mEmptyView.setActionClickedListener(this);
+ } else {
+ mEmptyView.setImage(EmptyContentView.NO_IMAGE);
+ mEmptyView.setActionLabel(EmptyContentView.NO_LABEL);
+ mEmptyView.setDescription(EmptyContentView.NO_LABEL);
+ }
+ }
+ }
+
+ @Override
+ public void onEmptyViewActionButtonClicked() {
+ final Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ requestPermissions(new String[] {READ_CONTACTS}, READ_CONTACTS_PERMISSION_REQUEST_CODE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ if (requestCode == READ_CONTACTS_PERMISSION_REQUEST_CODE) {
+ setupEmptyView();
+ }
+ }
}
diff --git a/src/com/android/dialer/list/SearchFragment.java b/src/com/android/dialer/list/SearchFragment.java
index 77ab291..315cfb9 100644
--- a/src/com/android/dialer/list/SearchFragment.java
+++ b/src/com/android/dialer/list/SearchFragment.java
@@ -15,6 +15,8 @@
*/
package com.android.dialer.list;
+import static android.Manifest.permission.READ_CONTACTS;
+
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
@@ -50,6 +52,7 @@
import com.android.dialer.R;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.IntentUtil;
+import com.android.dialer.widget.EmptyContentView;
import com.android.phone.common.animation.AnimUtils;
public class SearchFragment extends PhoneNumberPickerFragment {
@@ -79,6 +82,8 @@
private HostInterface mActivity;
+ protected EmptyContentView mEmptyView;
+
public interface HostInterface {
public boolean isActionBarShowing();
public boolean isDialpadShown();
@@ -125,6 +130,13 @@
final ListView listView = getListView();
+ if (mEmptyView == null) {
+ mEmptyView = new EmptyContentView(getActivity());
+ ((ViewGroup) getListView().getParent()).addView(mEmptyView);
+ getListView().setEmptyView(mEmptyView);
+ setupEmptyView();
+ }
+
listView.setBackgroundColor(res.getColor(R.color.background_dialer_results));
listView.setClipToPadding(false);
setVisibleScrollbarEnabled(false);
@@ -341,7 +353,7 @@
@Override
protected void startLoading() {
- if (PermissionsUtil.hasContactsPermissions(getActivity())) {
+ if (PermissionsUtil.hasPermission(getActivity(), READ_CONTACTS)) {
super.startLoading();
} else if (TextUtils.isEmpty(getQueryString())) {
// Clear out any existing call shortcuts.
@@ -354,6 +366,8 @@
// list.
getAdapter().notifyDataSetChanged();
}
+
+ setupEmptyView();
}
public void setOnTouchListener(View.OnTouchListener onTouchListener) {
@@ -371,4 +385,6 @@
}
return parent;
}
+
+ protected void setupEmptyView() {}
}
diff --git a/src/com/android/dialer/list/SmartDialSearchFragment.java b/src/com/android/dialer/list/SmartDialSearchFragment.java
index 082bc43..72d3abf 100644
--- a/src/com/android/dialer/list/SmartDialSearchFragment.java
+++ b/src/com/android/dialer/list/SmartDialSearchFragment.java
@@ -15,21 +15,33 @@
*/
package com.android.dialer.list;
+import static android.Manifest.permission.CALL_PHONE;
+
+import android.app.Activity;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
+import android.view.View;
import com.android.contacts.common.list.ContactEntryListAdapter;
+import com.android.contacts.common.util.PermissionsUtil;
import com.android.dialer.dialpad.SmartDialCursorLoader;
+import com.android.dialer.R;
+import com.android.dialer.widget.EmptyContentView;
+
+import java.util.ArrayList;
/**
* Implements a fragment to load and display SmartDial search results.
*/
-public class SmartDialSearchFragment extends SearchFragment {
+public class SmartDialSearchFragment extends SearchFragment
+ implements EmptyContentView.OnEmptyViewActionButtonClickedListener {
private static final String TAG = SmartDialSearchFragment.class.getSimpleName();
+ private static final int CALL_PHONE_PERMISSION_REQUEST_CODE = 1;
+
/**
* Creates a SmartDialListAdapter to display and operate on search results.
*/
@@ -69,4 +81,42 @@
final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
return adapter.getDataUri(position);
}
+
+ @Override
+ protected void setupEmptyView() {
+ if (mEmptyView != null && getActivity() != null) {
+ if (!PermissionsUtil.hasPermission(getActivity(), CALL_PHONE)) {
+ mEmptyView.setImage(R.drawable.empty_contacts);
+ mEmptyView.setActionLabel(R.string.permission_single_turn_on);
+ mEmptyView.setDescription(R.string.permission_place_call);
+ mEmptyView.setActionClickedListener(this);
+ } else {
+ mEmptyView.setImage(EmptyContentView.NO_IMAGE);
+ mEmptyView.setActionLabel(EmptyContentView.NO_LABEL);
+ mEmptyView.setDescription(EmptyContentView.NO_LABEL);
+ }
+ }
+ }
+
+ @Override
+ public void onEmptyViewActionButtonClicked() {
+ final Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ requestPermissions(new String[] {CALL_PHONE}, CALL_PHONE_PERMISSION_REQUEST_CODE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ if (requestCode == CALL_PHONE_PERMISSION_REQUEST_CODE) {
+ setupEmptyView();
+ }
+ }
+
+ public boolean isShowingPermissionRequest() {
+ return mEmptyView != null && mEmptyView.isShowingContent();
+ }
}
diff --git a/src/com/android/dialer/list/SpeedDialFragment.java b/src/com/android/dialer/list/SpeedDialFragment.java
index aead1c8..324caef 100644
--- a/src/com/android/dialer/list/SpeedDialFragment.java
+++ b/src/com/android/dialer/list/SpeedDialFragment.java
@@ -25,6 +25,7 @@
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Loader;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Rect;
import android.net.Uri;
@@ -64,6 +65,8 @@
PhoneFavoritesTileAdapter.OnDataSetChangedForAnimationListener,
EmptyContentView.OnEmptyViewActionButtonClickedListener {
+ private static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 1;
+
/**
* By default, the animation code assumes that all items in a list view are of the same height
* when animating new list items into view (e.g. from the bottom of the screen into view).
@@ -463,17 +466,27 @@
}
@Override
- public void onEmptyViewActionButtonClicked(String[] permissions) {
+ public void onEmptyViewActionButtonClicked() {
final Activity activity = getActivity();
if (activity == null) {
return;
}
if (!PermissionsUtil.hasPermission(activity, READ_CONTACTS)) {
- requestPermissions(new String[] {READ_CONTACTS}, 0);
+ requestPermissions(new String[] {READ_CONTACTS}, READ_CONTACTS_PERMISSION_REQUEST_CODE);
} else {
// Switch tabs
((HostInterface) activity).showAllContactsTab();
}
}
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ if (requestCode == READ_CONTACTS_PERMISSION_REQUEST_CODE) {
+ if (grantResults.length == 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
+ PermissionsUtil.notifyPermissionGranted(getActivity(), READ_CONTACTS);
+ }
+ }
+ }
}
diff --git a/src/com/android/dialer/settings/DefaultRingtonePreference.java b/src/com/android/dialer/settings/DefaultRingtonePreference.java
index c12e717..a174381 100644
--- a/src/com/android/dialer/settings/DefaultRingtonePreference.java
+++ b/src/com/android/dialer/settings/DefaultRingtonePreference.java
@@ -16,12 +16,17 @@
package com.android.dialer.settings;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.preference.RingtonePreference;
+import android.provider.Settings;
import android.util.AttributeSet;
+import android.widget.Toast;
+
+import com.android.dialer.R;
/**
* RingtonePreference which doesn't show default ringtone setting.
@@ -44,6 +49,13 @@
@Override
protected void onSaveRingtone(Uri ringtoneUri) {
+ if (!Settings.System.canWrite(getContext())) {
+ Toast.makeText(
+ getContext(),
+ getContext().getResources().getString(R.string.toast_cannot_write_system_settings),
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
RingtoneManager.setActualDefaultRingtoneUri(getContext(), getRingtoneType(), ringtoneUri);
}
diff --git a/src/com/android/dialer/settings/DialerSettingsActivity.java b/src/com/android/dialer/settings/DialerSettingsActivity.java
index d54053b..c459d35 100644
--- a/src/com/android/dialer/settings/DialerSettingsActivity.java
+++ b/src/com/android/dialer/settings/DialerSettingsActivity.java
@@ -1,23 +1,22 @@
package com.android.dialer.settings;
-import com.google.common.collect.Lists;
-
-import android.content.ComponentName;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.os.UserHandle;
+import android.os.Process;
import android.os.UserManager;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
-import android.preference.PreferenceActivity.Header;
+import android.provider.Settings;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
-import android.text.TextUtils;
+import android.util.Log;
import android.view.MenuItem;
+import android.widget.Toast;
-import com.android.dialer.DialtactsActivity;
+import com.android.contacts.common.util.PermissionsUtil;
import com.android.dialer.R;
import java.util.List;
@@ -26,8 +25,6 @@
protected SharedPreferences mPreferences;
- private static final int OWNER_HANDLE_ID = 0;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -44,6 +41,7 @@
Header soundSettingsHeader = new Header();
soundSettingsHeader.titleRes = R.string.sounds_and_vibration_title;
soundSettingsHeader.fragment = SoundSettingsFragment.class.getName();
+ soundSettingsHeader.id = R.id.settings_header_sounds_and_vibration;
target.add(soundSettingsHeader);
Header quickResponseSettingsHeader = new Header();
@@ -91,6 +89,24 @@
}
@Override
+ public void onHeaderClick(Header header, int position) {
+ if (header.id == R.id.settings_header_sounds_and_vibration) {
+ // If we don't have the permission to write to system settings, go to system sound
+ // settings instead. Otherwise, perform the super implementation (which launches our
+ // own preference fragment.
+ if (!Settings.System.canWrite(this)) {
+ Toast.makeText(
+ this,
+ getResources().getString(R.string.toast_cannot_write_system_settings),
+ Toast.LENGTH_SHORT).show();
+ startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
+ return;
+ }
+ }
+ super.onHeaderClick(header, position);
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
diff --git a/src/com/android/dialer/settings/SoundSettingsFragment.java b/src/com/android/dialer/settings/SoundSettingsFragment.java
index 7fc9394..8384700 100644
--- a/src/com/android/dialer/settings/SoundSettingsFragment.java
+++ b/src/com/android/dialer/settings/SoundSettingsFragment.java
@@ -16,7 +16,9 @@
package com.android.dialer.settings;
+import android.app.AppOpsManager;
import android.content.Context;
+import android.content.Intent;
import android.media.RingtoneManager;
import android.os.Bundle;
import android.os.Handler;
@@ -30,7 +32,10 @@
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
+import android.view.MenuItem;
+import android.widget.Toast;
+import com.android.contacts.common.util.PermissionsUtil;
import com.android.dialer.R;
import com.android.phone.common.util.SettingsUtil;
@@ -127,6 +132,13 @@
public void onResume() {
super.onResume();
+ if (!Settings.System.canWrite(getContext())) {
+ // If the user launches this setting fragment, then toggles the WRITE_SYSTEM_SETTINGS
+ // AppOp, then close the fragment since there is nothing useful to do.
+ getActivity().onBackPressed();
+ return;
+ }
+
if (mVibrateWhenRinging != null) {
mVibrateWhenRinging.setChecked(shouldVibrateWhenRinging());
}
@@ -143,6 +155,14 @@
*/
@Override
public boolean onPreferenceChange(Preference preference, Object objValue) {
+ if (!Settings.System.canWrite(getContext())) {
+ // A user shouldn't be able to get here, but this protects against monkey crashes.
+ Toast.makeText(
+ getContext(),
+ getResources().getString(R.string.toast_cannot_write_system_settings),
+ Toast.LENGTH_SHORT).show();
+ return true;
+ }
if (preference == mVibrateWhenRinging) {
boolean doVibrate = (Boolean) objValue;
Settings.System.putInt(getActivity().getContentResolver(),
@@ -161,6 +181,13 @@
*/
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ if (!Settings.System.canWrite(getContext())) {
+ Toast.makeText(
+ getContext(),
+ getResources().getString(R.string.toast_cannot_write_system_settings),
+ Toast.LENGTH_SHORT).show();
+ return true;
+ }
if (preference == mPlayDtmfTone) {
Settings.System.putInt(getActivity().getContentResolver(),
Settings.System.DTMF_TONE_WHEN_DIALING,
diff --git a/src/com/android/dialer/util/IntentUtil.java b/src/com/android/dialer/util/IntentUtil.java
index 7ed99c9..2ce3bd1 100644
--- a/src/com/android/dialer/util/IntentUtil.java
+++ b/src/com/android/dialer/util/IntentUtil.java
@@ -146,7 +146,7 @@
}
public static Intent getSendSmsIntent(CharSequence phoneNumber) {
- return new Intent(Intent.ACTION_VIEW, Uri.parse(SMS_URI_PREFIX + phoneNumber));
+ return new Intent(Intent.ACTION_SENDTO, Uri.parse(SMS_URI_PREFIX + phoneNumber));
}
public static Intent getNewContactIntent() {
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
index 70d6c61..158ed58 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
@@ -185,6 +185,7 @@
}
mPresenter.pausePlayback();
CallLogAsyncTaskUtil.deleteVoicemail(mContext, mVoicemailUri, null);
+ mPresenter.onVoicemailDeleted();
}
};
@@ -278,7 +279,7 @@
}
disableUiElements();
- mPositionText.setText(getString(R.string.voicemail_playback_error));
+ mStateText.setText(getString(R.string.voicemail_playback_error));
}
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
index 90617df..9319b6e 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
@@ -92,6 +92,10 @@
void setPresenter(VoicemailPlaybackPresenter presenter, Uri voicemailUri);
}
+ public interface OnVoicemailDeletedListener {
+ void onVoicemailDeleted(Uri uri);
+ }
+
/** The enumeration of {@link AsyncTask} objects we use in this class. */
public enum Tasks {
CHECK_FOR_CONTENT,
@@ -155,6 +159,8 @@
private PowerManager.WakeLock mProximityWakeLock;
private AudioManager mAudioManager;
+ private OnVoicemailDeletedListener mOnVoicemailDeletedListener;
+
/**
* Obtain singleton instance of this class. Use a single instance to provide a consistent
* listener to the AudioManager when requesting and abandoning audio focus.
@@ -260,7 +266,17 @@
}
/**
- * Reset the presenter for playback.
+ * Reset the presenter for playback back to its original state.
+ */
+ public void resetAll() {
+ reset();
+
+ mView = null;
+ mVoicemailUri = null;
+ }
+
+ /**
+ * Reset the presenter such that it is as if the voicemail has not been played.
*/
public void reset() {
if (mMediaPlayer != null) {
@@ -270,13 +286,15 @@
disableProximitySensor(false /* waitForFarState */);
- mView = null;
- mVoicemailUri = null;
-
mIsPrepared = false;
mIsPlaying = false;
mPosition = 0;
mDuration.set(0);
+
+ if (mView != null) {
+ mView.onPlaybackStopped();
+ mView.setClipPosition(0, mDuration.get());
+ }
}
/**
@@ -291,16 +309,11 @@
}
// Release the media player, otherwise there may be failures.
- if (mMediaPlayer != null) {
- mMediaPlayer.release();
- mMediaPlayer = null;
- mIsPrepared = false;
- }
+ reset();
if (mActivity != null) {
mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
}
- disableProximitySensor(false /* waitForFarState */);
}
/**
@@ -404,9 +417,11 @@
super(handler);
mFetchResultHandler = handler;
- mContext.getContentResolver().registerContentObserver(
- voicemailUri, false, this);
- mFetchResultHandler.postDelayed(this, FETCH_CONTENT_TIMEOUT_MS);
+ if (mContext != null) {
+ mContext.getContentResolver().registerContentObserver(
+ voicemailUri, false, this);
+ mFetchResultHandler.postDelayed(this, FETCH_CONTENT_TIMEOUT_MS);
+ }
}
/**
@@ -708,10 +723,21 @@
return mAudioManager.isSpeakerphoneOn();
}
+ public void setOnVoicemailDeletedListener(OnVoicemailDeletedListener listener) {
+ mOnVoicemailDeletedListener = listener;
+ }
+
public int getMediaPlayerPosition() {
return mIsPrepared && mMediaPlayer != null ? mMediaPlayer.getCurrentPosition() : 0;
}
+ /* package */ void onVoicemailDeleted() {
+ // Trampoline the event notification to the interested listener
+ if (mOnVoicemailDeletedListener != null) {
+ mOnVoicemailDeletedListener.onVoicemailDeleted(mVoicemailUri);
+ }
+ }
+
private static synchronized ScheduledExecutorService getScheduledExecutorServiceInstance() {
if (mScheduledExecutorService == null) {
mScheduledExecutorService = Executors.newScheduledThreadPool(NUMBER_OF_THREADS_IN_POOL);
diff --git a/src/com/android/dialer/widget/EmptyContentView.java b/src/com/android/dialer/widget/EmptyContentView.java
index 67c9ce1..f248967 100644
--- a/src/com/android/dialer/widget/EmptyContentView.java
+++ b/src/com/android/dialer/widget/EmptyContentView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
@@ -29,15 +30,15 @@
public class EmptyContentView extends LinearLayout implements View.OnClickListener {
public static final int NO_LABEL = 0;
+ public static final int NO_IMAGE = 0;
private ImageView mImageView;
private TextView mDescriptionView;
private TextView mActionView;
- private String[] mPermissions = new String[] {};
private OnEmptyViewActionButtonClickedListener mOnActionButtonClickedListener;
public interface OnEmptyViewActionButtonClickedListener {
- public void onEmptyViewActionButtonClicked(String[] permissions);
+ public void onEmptyViewActionButtonClicked();
}
public EmptyContentView(Context context) {
@@ -60,11 +61,8 @@
final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.empty_content_view, this);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
+ // Don't let touches fall through the empty view.
+ setClickable(true);
mImageView = (ImageView) findViewById(R.id.emptyListViewImage);
mDescriptionView = (TextView) findViewById(R.id.emptyListViewMessage);
mActionView = (TextView) findViewById(R.id.emptyListViewAction);
@@ -72,11 +70,22 @@
}
public void setDescription(int resourceId) {
- mDescriptionView.setText(resourceId);
+ if (resourceId == NO_LABEL) {
+ mDescriptionView.setText(null);
+ mDescriptionView.setVisibility(View.GONE);
+ } else {
+ mDescriptionView.setText(resourceId);
+ mDescriptionView.setVisibility(View.VISIBLE);
+ }
}
public void setImage(int resourceId) {
mImageView.setImageResource(resourceId);
+ if (resourceId == NO_LABEL) {
+ mImageView.setVisibility(View.GONE);
+ } else {
+ mImageView.setVisibility(View.VISIBLE);
+ }
}
public void setActionLabel(int resourceId) {
@@ -89,6 +98,12 @@
}
}
+ public boolean isShowingContent() {
+ return mImageView.getVisibility() == View.VISIBLE
+ || mDescriptionView.getVisibility() == View.VISIBLE
+ || mActionView.getVisibility() == View.VISIBLE;
+ }
+
public void setActionClickedListener(OnEmptyViewActionButtonClickedListener listener) {
mOnActionButtonClickedListener = listener;
}
@@ -96,7 +111,7 @@
@Override
public void onClick(View v) {
if (mOnActionButtonClickedListener != null) {
- mOnActionButtonClickedListener.onEmptyViewActionButtonClicked(mPermissions);
+ mOnActionButtonClickedListener.onEmptyViewActionButtonClicked();
}
}
}