Merge "Increase opacity of text on primary call info to 100%." into nyc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 06f5795..c39bd5b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -32,6 +32,7 @@
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
@@ -68,8 +69,7 @@
android:supportsRtl="true"
android:backupAgent='com.android.dialer.DialerBackupAgent'
android:usesCleartextTraffic="false"
- android:forceDeviceEncrypted="true"
- android:encryptionAware="true">
+ android:forceDeviceEncrypted="false">
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAIBXgtCEKQ6W0PXVnW-ZVia2KmlV2AxsTw3GjAeQ" />
@@ -85,6 +85,7 @@
android:icon="@mipmap/ic_launcher_phone"
android:windowSoftInputMode="stateAlwaysHidden|adjustNothing"
android:resizeableActivity="true"
+ android:encryptionAware="true"
>
<intent-filter>
<action android:name="android.intent.action.DIAL" />
@@ -280,10 +281,12 @@
<service
android:name=".calllog.CallLogNotificationsService"
+ android:encryptionAware="true"
android:exported="false"
/>
- <receiver android:name=".calllog.MissedCallNotificationReceiver">
+ <receiver android:name=".calllog.MissedCallNotificationReceiver"
+ android:encryptionAware="true">
<intent-filter>
<action android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" />
</intent-filter>
@@ -317,11 +320,6 @@
android:resizeableActivity="true">
</activity>
- <!-- BroadcastReceiver for receiving Intents from Notification mechanism. -->
- <receiver android:name="com.android.incallui.NotificationBroadcastReceiver"
- android:encryptionAware="true"
- android:exported="false" />
-
<service android:name="com.android.incallui.InCallServiceImpl"
android:permission="android.permission.BIND_INCALL_SERVICE"
android:encryptionAware="true" >
@@ -333,6 +331,11 @@
</intent-filter>
</service>
+ <!-- BroadcastReceiver for receiving Intents from Notification mechanism. -->
+ <receiver android:name="com.android.incallui.NotificationBroadcastReceiver"
+ android:encryptionAware="true"
+ android:exported="false" />
+
<provider
android:name=".database.FilteredNumberProvider"
android:authorities="com.android.dialer.database.filterednumberprovider"
diff --git a/InCallUI/res/values-bs-rBA/strings.xml b/InCallUI/res/values-bs-rBA/strings.xml
index b64df21..21f4360 100644
--- a/InCallUI/res/values-bs-rBA/strings.xml
+++ b/InCallUI/res/values-bs-rBA/strings.xml
@@ -299,9 +299,7 @@
<skip />
<!-- no translation found for default_notification_description (4950807644546509965) -->
<skip />
- <!-- no translation found for ringtone_title (835582004693335905) -->
- <!-- no translation found for ringtone_title (5379026328015343686) -->
- <skip />
+ <string name="ringtone_title" msgid="835582004693335905">"Melodija zvona telefona"</string>
<!-- no translation found for vibrate_on_ring_title (5019791043398986665) -->
<skip />
<!-- no translation found for preference_category_ringtone (6246687516643676729) -->
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 946e0eb..173fe42 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -100,10 +100,7 @@
mNotificationManager =
(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mDialerRingtoneManager = new DialerRingtoneManager(
- new InCallTonePlayer(
- AudioModeProvider.getInstance(),
- new ToneGeneratorFactory(),
- new PausableExecutorImpl()),
+ new InCallTonePlayer(new ToneGeneratorFactory(), new PausableExecutorImpl()),
CallList.getInstance());
mCurrentNotification = NOTIFICATION_NONE;
}
diff --git a/InCallUI/src/com/android/incallui/ringtone/InCallTonePlayer.java b/InCallUI/src/com/android/incallui/ringtone/InCallTonePlayer.java
index d930a92..3a8b03d 100644
--- a/InCallUI/src/com/android/incallui/ringtone/InCallTonePlayer.java
+++ b/InCallUI/src/com/android/incallui/ringtone/InCallTonePlayer.java
@@ -21,20 +21,14 @@
import android.media.AudioManager;
import android.media.ToneGenerator;
-import android.provider.MediaStore.Audio;
import android.support.annotation.Nullable;
-import android.telecom.CallAudioState;
-import com.android.contacts.common.testing.NeededForTesting;
-import com.android.incallui.AudioModeProvider;
import com.android.incallui.Log;
import com.android.incallui.async.PausableExecutor;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import javax.annotation.concurrent.NotThreadSafe;
-
/**
* Class responsible for playing in-call related tones in a background thread. This class only
* allows one tone to be played at a time.
@@ -45,7 +39,6 @@
public static final int VOLUME_RELATIVE_HIGH_PRIORITY = 80;
- private final AudioModeProvider mAudioModeProvider;
private final ToneGeneratorFactory mToneGeneratorFactory;
private final PausableExecutor mExecutor;
private @Nullable CountDownLatch mNumPlayingTones;
@@ -53,23 +46,19 @@
/**
* Creates a new InCallTonePlayer.
*
- * @param audioModeProvider the {@link AudioModeProvider} used to determine through which stream
- * to play tones.
* @param toneGeneratorFactory the {@link ToneGeneratorFactory} used to create
* {@link ToneGenerator}s.
* @param executor the {@link PausableExecutor} used to play tones in a background thread.
* @throws NullPointerException if audioModeProvider, toneGeneratorFactory, or executor are
* {@code null}.
*/
- public InCallTonePlayer(AudioModeProvider audioModeProvider,
- ToneGeneratorFactory toneGeneratorFactory, PausableExecutor executor) {
- mAudioModeProvider = Preconditions.checkNotNull(audioModeProvider);
+ public InCallTonePlayer(ToneGeneratorFactory toneGeneratorFactory, PausableExecutor executor) {
mToneGeneratorFactory = Preconditions.checkNotNull(toneGeneratorFactory);
mExecutor = Preconditions.checkNotNull(executor);
}
/**
- * @return {@code true} if a tone is currently playing, {@code false} otherwise
+ * @return {@code true} if a tone is currently playing, {@code false} otherwise.
*/
public boolean isPlayingTone() {
return mNumPlayingTones != null && mNumPlayingTones.getCount() > 0;
@@ -79,8 +68,8 @@
* Plays the given tone in a background thread.
*
* @param tone the tone to play.
- * @throws IllegalStateException if a tone is already playing
- * @throws IllegalArgumentException if the tone is invalid
+ * @throws IllegalStateException if a tone is already playing.
+ * @throws IllegalArgumentException if the tone is invalid.
*/
public void play(int tone) {
if (isPlayingTone()) {
@@ -97,26 +86,24 @@
}
private ToneGeneratorInfo getToneGeneratorInfo(int tone) {
- int stream = getPlaybackStream();
switch (tone) {
case TONE_CALL_WAITING:
+ /*
+ * Call waiting tones play until they're stopped either by the user accepting or
+ * declining the call so the tone length is set at what's effectively forever. The
+ * tone is played at a high priority volume and through STREAM_VOICE_CALL since it's
+ * call related and using that stream will route it through bluetooth devices
+ * appropriately.
+ */
return new ToneGeneratorInfo(ToneGenerator.TONE_SUP_CALL_WAITING,
VOLUME_RELATIVE_HIGH_PRIORITY,
Integer.MAX_VALUE,
- stream);
+ AudioManager.STREAM_VOICE_CALL);
default:
throw new IllegalArgumentException("Bad tone: " + tone);
}
}
- private int getPlaybackStream() {
- if (mAudioModeProvider.getAudioMode() == CallAudioState.ROUTE_BLUETOOTH) {
- // TODO (maxwelb): b/26932998 play through bluetooth
- // return AudioManager.STREAM_BLUETOOTH_SCO;
- }
- return AudioManager.STREAM_VOICE_CALL;
- }
-
private void playOnBackgroundThread(ToneGeneratorInfo info) {
ToneGenerator toneGenerator = null;
try {
diff --git a/InCallUI/tests/src/com/android/incallui/ringtone/InCallTonePlayerTest.java b/InCallUI/tests/src/com/android/incallui/ringtone/InCallTonePlayerTest.java
index 59611f7..bde5c50 100644
--- a/InCallUI/tests/src/com/android/incallui/ringtone/InCallTonePlayerTest.java
+++ b/InCallUI/tests/src/com/android/incallui/ringtone/InCallTonePlayerTest.java
@@ -21,7 +21,6 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.incallui.AudioModeProvider;
import com.android.incallui.async.PausableExecutor;
import com.android.incallui.async.SingleProdThreadExecutor;
@@ -32,7 +31,6 @@
@SmallTest
public class InCallTonePlayerTest extends AndroidTestCase {
- @Mock private AudioModeProvider mAudioModeProvider;
@Mock private ToneGeneratorFactory mToneGeneratorFactory;
@Mock private ToneGenerator mToneGenerator;
private InCallTonePlayer mInCallTonePlayer;
@@ -52,8 +50,7 @@
Mockito.when(mToneGeneratorFactory.newInCallToneGenerator(Mockito.anyInt(),
Mockito.anyInt())).thenReturn(mToneGenerator);
mExecutor = new SingleProdThreadExecutor();
- mInCallTonePlayer = new InCallTonePlayer(mAudioModeProvider, mToneGeneratorFactory,
- mExecutor);
+ mInCallTonePlayer = new InCallTonePlayer(mToneGeneratorFactory, mExecutor);
}
@Override
@@ -92,10 +89,6 @@
} catch (IllegalStateException e) {}
}
- public void testPlay_BlueToothStream() {
- // TODO (maxwelb): b/26932998 play through bluetooth
- }
-
public void testPlay_VoiceCallStream() throws InterruptedException {
mInCallTonePlayer.play(InCallTonePlayer.TONE_CALL_WAITING);
mExecutor.awaitMilestoneForTesting();
diff --git a/build-app.gradle b/build-app.gradle
index 9e24136..2ea4376 100644
--- a/build-app.gradle
+++ b/build-app.gradle
@@ -12,6 +12,11 @@
manifest.srcFile 'AndroidManifest.xml'
res.srcDirs = ['res']
}
+
+ sourceSets.androidTest {
+ java.srcDirs = ['tests/src']
+ res.srcDirs = ['test/res']
+ }
}
dependencies {
diff --git a/res/drawable-hdpi/ic_share_white_24dp.png b/res/drawable-hdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..b09a692
--- /dev/null
+++ b/res/drawable-hdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_share_white_24dp.png b/res/drawable-mdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..e944fd7
--- /dev/null
+++ b/res/drawable-mdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_share_white_24dp.png b/res/drawable-xhdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..22a8783
--- /dev/null
+++ b/res/drawable-xhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_share_white_24dp.png b/res/drawable-xxhdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..a35b3cd
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_share_white_24dp.png b/res/drawable-xxxhdpi/ic_share_white_24dp.png
new file mode 100644
index 0000000..e351c7b
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/res/layout/voicemail_playback_layout.xml b/res/layout/voicemail_playback_layout.xml
index 984a52b..178e888 100644
--- a/res/layout/voicemail_playback_layout.xml
+++ b/res/layout/voicemail_playback_layout.xml
@@ -102,6 +102,19 @@
android:tint="@color/voicemail_icon_tint"
android:contentDescription="@string/call_log_trash_voicemail" />
+ <Space android:id="@+id/space_before_share_voicemail"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:visibility="gone" />
+
+ <ImageButton android:id="@+id/share_voicemail"
+ style="@style/VoicemailPlaybackLayoutButtonStyle"
+ android:src="@drawable/ic_share_white_24dp"
+ android:tint="@color/voicemail_icon_tint"
+ android:contentDescription="@string/call_log_share_voicemail"
+ android:visibility="gone" />
+
<Space android:id="@+id/space_before_archive_voicemail"
android:layout_width="0dp"
android:layout_height="match_parent"
diff --git a/res/values-bs-rBA/strings.xml b/res/values-bs-rBA/strings.xml
index 405ce3b..bc05f12 100644
--- a/res/values-bs-rBA/strings.xml
+++ b/res/values-bs-rBA/strings.xml
@@ -138,9 +138,7 @@
<skip />
<!-- no translation found for action_menu_call_history_description (9018442816219748968) -->
<skip />
- <!-- no translation found for action_menu_overflow_description (2303272250613084574) -->
- <!-- no translation found for action_menu_overflow_description (2295659037509008453) -->
- <skip />
+ <string name="action_menu_overflow_description" msgid="2303272250613084574">"Više opcija"</string>
<!-- no translation found for action_menu_dialpad_button (1425910318049008136) -->
<skip />
<!-- no translation found for menu_show_outgoing_only (1965570298133301970) -->
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index dc7a508..5516a24 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -222,7 +222,7 @@
<string name="list_delimeter" msgid="4571593167738725100">"、 "</string>
<string name="display_options_title" msgid="7812852361055667468">"显示选项"</string>
<string name="sounds_and_vibration_title" msgid="1692290115642160845">"提示音和振动"</string>
- <string name="accessibility_settings_title" msgid="6068141142874046249">"无障碍功能"</string>
+ <string name="accessibility_settings_title" msgid="6068141142874046249">"辅助功能"</string>
<string name="ringtone_title" msgid="760362035635084653">"手机铃声"</string>
<string name="vibrate_on_ring_title" msgid="3362916460327555241">"有来电时响铃并振动"</string>
<string name="dtmf_tone_enable_title" msgid="6571449695997521615">"拨号键盘提示音"</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 830af65..b8f8679 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -87,6 +87,9 @@
<!-- Menu item used to archive a voicemail. [CHAR LIMIT=30] -->
<string name="call_log_archive_voicemail">Archive voicemail</string>
+ <!-- Menu item used to send a voicemail through other applications [CHAR LIMIT=30] -->
+ <string name="call_log_share_voicemail">Share voicemail</string>
+
<!-- Text for snackbar to undo a voicemail delete. [CHAR LIMIT=30] -->
<string name="snackbar_voicemail_deleted">Voicemail deleted</string>
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 58a0474..d063fef 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -16,9 +16,6 @@
package com.android.dialer;
-import com.android.dialer.voicemail.VoicemailArchiveActivity;
-import com.google.common.annotations.VisibleForTesting;
-
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.ActivityNotFoundException;
@@ -65,7 +62,6 @@
import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
import com.android.contacts.common.util.PermissionsUtil;
import com.android.contacts.common.widget.FloatingActionButtonController;
-import com.android.contacts.commonbind.analytics.AnalyticsUtil;
import com.android.dialer.calllog.CallLogActivity;
import com.android.dialer.calllog.CallLogFragment;
import com.android.dialer.database.DialerDatabaseHelper;
@@ -85,18 +81,19 @@
import com.android.dialer.logging.Logger;
import com.android.dialer.logging.ScreenEvent;
import com.android.dialer.settings.DialerSettingsActivity;
+import com.android.dialer.util.Assert;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.IntentUtil;
import com.android.dialer.util.IntentUtil.CallIntentBuilder;
import com.android.dialer.util.TelecomUtil;
+import com.android.dialer.voicemail.VoicemailArchiveActivity;
import com.android.dialer.widget.ActionBarController;
import com.android.dialer.widget.SearchEditTextLayout;
import com.android.dialerbind.DatabaseHelperManager;
import com.android.dialerbind.ObjectFactory;
import com.android.phone.common.animation.AnimUtils;
import com.android.phone.common.animation.AnimationListenerAdapter;
-
-import junit.framework.Assert;
+import com.google.common.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
index 6abf241..1892631 100644
--- a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
+++ b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java
@@ -16,9 +16,7 @@
package com.android.dialer.calllog;
-import static android.Manifest.permission.READ_CALL_LOG;
-import static android.Manifest.permission.READ_CONTACTS;
-
+import android.Manifest;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
@@ -111,7 +109,7 @@
/**
* Given a number and number information (presentation and country ISO), get
* {@link ContactInfo}. If the name is empty but we have a special presentation, display that.
- * Otherwise attempt to look it up in the database or the cache.
+ * Otherwise attempt to look it up in the cache.
* If that fails, fall back to displaying the number.
*/
public @NonNull ContactInfo getContactInfo(@Nullable String number, int numberPresentation,
@@ -136,13 +134,7 @@
return contactInfo;
}
- // 2. Personal ContactsProvider phonelookup query.
- contactInfo.name = mNameLookupQuery.query(number);
- if (!TextUtils.isEmpty(contactInfo.name)) {
- return contactInfo;
- }
-
- // 3. Look it up in the cache.
+ // 2. Look it up in the cache.
ContactInfo cachedContactInfo = mContactInfoHelper.lookupNumber(number, countryIso);
if (cachedContactInfo != null && !TextUtils.isEmpty(cachedContactInfo.name)) {
@@ -150,13 +142,13 @@
}
if (!TextUtils.isEmpty(contactInfo.formattedNumber)) {
- // 4. If we cannot lookup the contact, use the formatted number instead.
+ // 3. If we cannot lookup the contact, use the formatted number instead.
contactInfo.name = contactInfo.formattedNumber;
} else if (!TextUtils.isEmpty(number)) {
- // 5. If number can't be formatted, use number.
+ // 4. If number can't be formatted, use number.
contactInfo.name = number;
} else {
- // 6. Otherwise, it's unknown number.
+ // 5. Otherwise, it's unknown number.
contactInfo.name = mContext.getResources().getString(R.string.unknown);
}
return contactInfo;
@@ -259,7 +251,7 @@
@Override
@Nullable
public List<NewCall> query(int type) {
- if (!PermissionsUtil.hasPermission(mContext, READ_CALL_LOG)) {
+ if (!PermissionsUtil.hasPermission(mContext, Manifest.permission.READ_CALL_LOG)) {
Log.w(TAG, "No READ_CALL_LOG permission, returning null for calls lookup.");
return null;
}
@@ -338,7 +330,7 @@
@Override
@Nullable
public String query(@Nullable String number) {
- if (!PermissionsUtil.hasPermission(mContext, READ_CONTACTS)) {
+ if (!PermissionsUtil.hasPermission(mContext, Manifest.permission.READ_CONTACTS)) {
Log.w(TAG, "No READ_CONTACTS permission, returning null for name lookup.");
return null;
}
diff --git a/src/com/android/dialer/calllog/MissedCallNotifier.java b/src/com/android/dialer/calllog/MissedCallNotifier.java
index a9dfd44..98d02d0 100644
--- a/src/com/android/dialer/calllog/MissedCallNotifier.java
+++ b/src/com/android/dialer/calllog/MissedCallNotifier.java
@@ -21,6 +21,7 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.provider.CallLog.Calls;
import android.text.TextUtils;
@@ -28,13 +29,15 @@
import com.android.contacts.common.ContactsUtils;
import com.android.contacts.common.util.PhoneNumberHelper;
-import com.android.dialer.calllog.CallLogNotificationsHelper.NewCall;
import com.android.dialer.DialtactsActivity;
+import com.android.dialer.R;
+import com.android.dialer.calllog.CallLogNotificationsHelper.NewCall;
+import com.android.dialer.contactinfo.ContactPhotoLoader;
+import com.android.dialer.compat.UserManagerCompat;
import com.android.dialer.list.ListsFragment;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.IntentUtil;
import com.android.dialer.util.IntentUtil.CallIntentBuilder;
-import com.android.dialer.R;
import java.util.List;
@@ -94,6 +97,7 @@
NewCall newestCall = useCallLog ? newCalls.get(0) : null;
long timeMs = useCallLog ? newestCall.dateMs : System.currentTimeMillis();
+ Notification.Builder builder = new Notification.Builder(mContext);
// Display the first line of the notification:
// 1 missed call: <caller name || handle>
// More than 1 missed call: <number of calls> + "missed calls"
@@ -110,6 +114,11 @@
: R.string.notification_missedCallTitle;
expandedText = contactInfo.name;
+ ContactPhotoLoader loader = new ContactPhotoLoader(mContext, contactInfo);
+ Bitmap photoIcon = loader.loadPhotoIcon();
+ if (photoIcon != null) {
+ builder.setLargeIcon(photoIcon);
+ }
} else {
titleResId = R.string.notification_missedCallsTitle;
expandedText =
@@ -132,7 +141,6 @@
.setDeleteIntent(createClearMissedCallsPendingIntent());
// Create the notification suitable for display when sensitive information is showing.
- Notification.Builder builder = new Notification.Builder(mContext);
builder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
.setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
.setContentTitle(mContext.getText(titleResId))
@@ -146,8 +154,8 @@
// sensitive notification information.
.setPublicVersion(publicBuilder.build());
- // Add additional actions when there is only 1 missed call, like call-back and SMS.
- if (count == 1) {
+ // Add additional actions when there is only 1 missed call and the user isn't locked
+ if (UserManagerCompat.isUserUnlocked(mContext) && count == 1) {
if (!TextUtils.isEmpty(number)
&& !TextUtils.equals(
number, mContext.getString(R.string.handle_restricted))) {
@@ -161,7 +169,6 @@
createSendSmsFromNotificationPendingIntent(number));
}
}
- //TODO: add photo
}
Notification notification = builder.build();
@@ -175,21 +182,24 @@
AsyncTask.execute(new Runnable() {
@Override
public void run() {
- // Clear the list of new missed calls from the call log.
- ContentValues values = new ContentValues();
- values.put(Calls.NEW, 0);
- values.put(Calls.IS_READ, 1);
- StringBuilder where = new StringBuilder();
- where.append(Calls.NEW);
- where.append(" = 1 AND ");
- where.append(Calls.TYPE);
- where.append(" = ?");
- try {
- mContext.getContentResolver().update(Calls.CONTENT_URI, values,
- where.toString(), new String[]{ Integer.toString(Calls.
- MISSED_TYPE) });
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "ContactsProvider update command failed", e);
+ // Call log is only accessible when unlocked. If that's the case, clear the list of
+ // new missed calls from the call log.
+ if (UserManagerCompat.isUserUnlocked(mContext)) {
+ ContentValues values = new ContentValues();
+ values.put(Calls.NEW, 0);
+ values.put(Calls.IS_READ, 1);
+ StringBuilder where = new StringBuilder();
+ where.append(Calls.NEW);
+ where.append(" = 1 AND ");
+ where.append(Calls.TYPE);
+ where.append(" = ?");
+ try {
+ mContext.getContentResolver().update(Calls.CONTENT_URI, values,
+ where.toString(), new String[]{Integer.toString(Calls.
+ MISSED_TYPE)});
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "ContactsProvider update command failed", e);
+ }
}
getNotificationMgr().cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
}
diff --git a/src/com/android/dialer/contactinfo/ContactPhotoLoader.java b/src/com/android/dialer/contactinfo/ContactPhotoLoader.java
new file mode 100644
index 0000000..f36c438
--- /dev/null
+++ b/src/com/android/dialer/contactinfo/ContactPhotoLoader.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package com.android.dialer.contactinfo;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.provider.MediaStore;
+import android.support.annotation.Nullable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import android.util.Log;
+
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.lettertiles.LetterTileDrawable;
+import com.android.dialer.R;
+import com.android.dialer.calllog.ContactInfo;
+import com.android.dialer.calllog.ContactInfoHelper;
+import com.android.dialer.util.Assert;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+
+import java.io.IOException;
+/**
+ * Class to create the appropriate contact icon from a ContactInfo.
+ * This class is for synchronous, blocking calls to generate bitmaps, while
+ * ContactCommons.ContactPhotoManager is to cache, manage and update a ImageView asynchronously.
+ */
+public class ContactPhotoLoader {
+
+ private static final String TAG = "ContactPhotoLoader";
+
+ private final Context mContext;
+ private final ContactInfo mContactInfo;
+
+ public ContactPhotoLoader(Context context, ContactInfo contactInfo) {
+ mContext = Preconditions.checkNotNull(context);
+ mContactInfo = Preconditions.checkNotNull(contactInfo);
+ }
+
+ /**
+ * Create a contact photo icon bitmap appropriate for the ContactInfo.
+ */
+ public Bitmap loadPhotoIcon() {
+ Assert.assertNotUiThread("ContactPhotoLoader#loadPhotoIcon called on UI thread");
+ int photoSize = mContext.getResources().getDimensionPixelSize(R.dimen.contact_photo_size);
+ return drawableToBitmap(getIcon(), photoSize, photoSize);
+ }
+
+ @VisibleForTesting
+ Drawable getIcon() {
+ Drawable drawable = createPhotoIconDrawable();
+ if (drawable == null) {
+ drawable = createLetterTileDrawable();
+ }
+ return drawable;
+ }
+
+ /**
+ * @return a {@link Drawable} of circular photo icon if the photo can be loaded, {@code null}
+ * otherwise.
+ */
+ @Nullable
+ private Drawable createPhotoIconDrawable() {
+ if (mContactInfo.photoUri == null) {
+ return null;
+ }
+ try {
+ Bitmap bitmap = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(),
+ mContactInfo.photoUri);
+ final RoundedBitmapDrawable drawable =
+ RoundedBitmapDrawableFactory.create(mContext.getResources(), bitmap);
+ drawable.setAntiAlias(true);
+ drawable.setCornerRadius(bitmap.getHeight() / 2);
+ return drawable;
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ return null;
+ }
+ }
+
+ /**
+ * @return a {@link LetterTileDrawable} based on the ContactInfo.
+ */
+ private Drawable createLetterTileDrawable() {
+ LetterTileDrawable drawable = new LetterTileDrawable(mContext.getResources());
+ drawable.setIsCircular(true);
+ ContactInfoHelper helper =
+ new ContactInfoHelper(mContext, GeoUtil.getCurrentCountryIso(mContext));
+ if (helper.isBusiness(mContactInfo.sourceType)) {
+ drawable.setContactType(LetterTileDrawable.TYPE_BUSINESS);
+ }
+ drawable.setLetterAndColorFromContactDetails(mContactInfo.name, mContactInfo.lookupKey);
+ return drawable;
+ }
+
+ private static Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
+
+}
diff --git a/src/com/android/dialer/util/Assert.java b/src/com/android/dialer/util/Assert.java
new file mode 100644
index 0000000..ec0a6cc
--- /dev/null
+++ b/src/com/android/dialer/util/Assert.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+package com.android.dialer.util;
+
+import android.os.Looper;
+
+public class Assert {
+ public static void assertNotUiThread(String msg) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ throw new AssertionError(msg);
+ }
+ }
+
+ public static void assertNotNull(Object object, String msg) {
+ if (object == null) {
+ throw new AssertionError(object);
+ }
+ }
+
+ public static void assertNotNull(Object object) {
+ assertNotNull(object, null);
+ }
+}
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
index 0cbe7f2..8c869d1 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
@@ -275,7 +275,11 @@
private ImageButton mPlaybackSpeakerphone;
private ImageButton mDeleteButton;
private ImageButton mArchiveButton;
+ private ImageButton mShareButton;
+
private Space mArchiveSpace;
+ private Space mShareSpace;
+
private TextView mStateText;
private TextView mPositionText;
private TextView mTotalDurationText;
@@ -304,6 +308,12 @@
updateArchiveUI(mVoicemailUri);
updateArchiveButton(mVoicemailUri);
}
+
+ if (ObjectFactory.isVoicemailShareEnabled(mContext)) {
+ // Show share button and space before it
+ mShareSpace.setVisibility(View.VISIBLE);
+ mShareButton.setVisibility(View.VISIBLE);
+ }
}
@Override
@@ -315,7 +325,11 @@
mPlaybackSpeakerphone = (ImageButton) findViewById(R.id.playback_speakerphone);
mDeleteButton = (ImageButton) findViewById(R.id.delete_voicemail);
mArchiveButton =(ImageButton) findViewById(R.id.archive_voicemail);
+ mShareButton = (ImageButton) findViewById(R.id.share_voicemail);
+
mArchiveSpace = (Space) findViewById(R.id.space_before_archive_voicemail);
+ mShareSpace = (Space) findViewById(R.id.space_before_share_voicemail);
+
mStateText = (TextView) findViewById(R.id.playback_state_text);
mPositionText = (TextView) findViewById(R.id.playback_position_text);
mTotalDurationText = (TextView) findViewById(R.id.total_duration_text);
diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java
index 935c9f7..9f75e39 100644
--- a/src/com/android/dialerbind/ObjectFactory.java
+++ b/src/com/android/dialerbind/ObjectFactory.java
@@ -48,6 +48,10 @@
return false;
}
+ public static boolean isVoicemailShareEnabled(Context context) {
+ return false;
+ }
+
@Nullable
public static ExtendedBlockingButtonRenderer newExtendedBlockingButtonRenderer(
Context context, ExtendedBlockingButtonRenderer.Listener listener) {
diff --git a/tests/src/com/android/dialer/contactinfo/ContactPhotoLoaderTest.java b/tests/src/com/android/dialer/contactinfo/ContactPhotoLoaderTest.java
new file mode 100644
index 0000000..42a5ae9
--- /dev/null
+++ b/tests/src/com/android/dialer/contactinfo/ContactPhotoLoaderTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+package com.android.dialer.contactinfo;
+
+import android.app.Instrumentation;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.test.AndroidTestCase;
+import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
+
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.lettertiles.LetterTileDrawable;
+import com.android.dialer.calllog.ContactInfo;
+import com.android.dialer.calllog.ContactInfoHelper;
+import com.android.dialer.tests.R;
+
+public class ContactPhotoLoaderTest extends InstrumentationTestCase {
+
+ private Context mContext;
+
+ @Override
+ public void setUp() {
+ mContext = getInstrumentation().getTargetContext();
+ }
+
+ public void testConstructor() {
+ ContactPhotoLoader loader = new ContactPhotoLoader(mContext, new ContactInfo());
+ }
+
+ public void testConstructor_NullContext() {
+ try {
+ ContactPhotoLoader loader = new ContactPhotoLoader(null, new ContactInfo());
+ fail();
+ } catch (NullPointerException e) {
+ //expected
+ }
+ }
+
+ public void testConstructor_NullContactInfo() {
+ try {
+ ContactPhotoLoader loader = new ContactPhotoLoader(mContext, null);
+ fail();
+ } catch (NullPointerException e) {
+ //expected
+ }
+ }
+
+ public void testGetIcon_Photo() {
+ ContactInfo info = getTestContactInfo();
+ info.photoUri = getResourceUri(R.drawable.phone_icon);
+ ContactPhotoLoader loader = new ContactPhotoLoader(mContext, info);
+ assertTrue(loader.getIcon() instanceof RoundedBitmapDrawable);
+ }
+
+ public void testGetIcon_Photo_Invalid() {
+ ContactInfo info = getTestContactInfo();
+ info.photoUri = Uri.parse("file://invalid/uri");
+ ContactPhotoLoader loader = new ContactPhotoLoader(mContext, info);
+ //Should fall back to LetterTileDrawable
+ assertTrue(loader.getIcon() instanceof LetterTileDrawable);
+ }
+
+ public void testGetIcon_LetterTile() {
+ ContactInfo info = getTestContactInfo();
+ ContactPhotoLoader loader = new ContactPhotoLoader(mContext, info);
+ assertTrue(loader.getIcon() instanceof LetterTileDrawable);
+ }
+
+ private Uri getResourceUri(int resId) {
+ Context testContext = getInstrumentation().getContext();
+ Resources resources = testContext.getResources();
+
+ assertNotNull(resources.getDrawable(resId));
+ return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
+ + testContext.getPackageName()
+ + '/' + resId);
+ }
+
+ private ContactInfo getTestContactInfo() {
+ ContactInfo info = new ContactInfo();
+ info.name = "foo";
+ info.lookupKey = "bar";
+ return info;
+ }
+}
\ No newline at end of file