Colorize conversations.

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2015-06-23 15:10:50 -07:00
parent 64df85f3ee
commit fb9f16ad29
16 changed files with 251 additions and 95 deletions

View File

@ -6,15 +6,15 @@
android:layout_height="?android:attr/listPreferredItemHeight" android:layout_height="?android:attr/listPreferredItemHeight"
android:paddingRight="50dp"> android:paddingRight="50dp">
<ImageView <org.thoughtcrime.securesms.components.AvatarImageView
android:id="@+id/contact_photo_image" android:id="@+id/contact_photo_image"
android:layout_width="@dimen/contact_selection_photo_size" android:layout_width="@dimen/contact_selection_photo_size"
android:layout_height="@dimen/contact_selection_photo_size" android:layout_height="@dimen/contact_selection_photo_size"
android:foreground="@drawable/contact_photo_background"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginLeft="10dp" android:layout_marginLeft="10dp"
android:cropToPadding="true" android:cropToPadding="true"
android:scaleType="centerCrop"
android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" /> android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" />
<LinearLayout <LinearLayout

View File

@ -16,14 +16,17 @@
*/ */
package org.thoughtcrime.securesms; package org.thoughtcrime.securesms;
import android.annotation.TargetApi;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -40,6 +43,7 @@ import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener; import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener; import android.view.View.OnKeyListener;
import android.view.ViewStub; import android.view.ViewStub;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.Button; import android.widget.Button;
@ -49,6 +53,7 @@ import android.widget.Toast;
import com.afollestad.materialdialogs.AlertDialogWrapper; import com.afollestad.materialdialogs.AlertDialogWrapper;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.readystatesoftware.systembartint.SystemBarTintManager;
import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener; import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
import org.thoughtcrime.securesms.components.AnimatingToggle; import org.thoughtcrime.securesms.components.AnimatingToggle;
@ -58,6 +63,7 @@ import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
import org.thoughtcrime.securesms.components.emoji.EmojiToggle; import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
import org.thoughtcrime.securesms.contacts.ContactAccessor; import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SecurityEvent; import org.thoughtcrime.securesms.crypto.SecurityEvent;
@ -167,6 +173,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private DynamicTheme dynamicTheme = new DynamicTheme(); private DynamicTheme dynamicTheme = new DynamicTheme();
private DynamicLanguage dynamicLanguage = new DynamicLanguage(); private DynamicLanguage dynamicLanguage = new DynamicLanguage();
@TargetApi(Build.VERSION_CODES.KITKAT)
@Override @Override
protected void onPreCreate() { protected void onPreCreate() {
dynamicTheme.onCreate(this); dynamicTheme.onCreate(this);
@ -220,6 +227,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
initializeIme(); initializeIme();
titleView.setTitle(recipients); titleView.setTitle(recipients);
setActionBarColor(recipients.getColor());
setBlockedUserState(recipients); setBlockedUserState(recipients);
calculateCharactersRemaining(); calculateCharactersRemaining();
@ -798,11 +806,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onModified(final Recipients recipients) { public void onModified(final Recipients recipients) {
Log.w(TAG, "onModified()");
titleView.post(new Runnable() { titleView.post(new Runnable() {
@Override @Override
public void run() { public void run() {
titleView.setTitle(recipients); titleView.setTitle(recipients);
setBlockedUserState(recipients); setBlockedUserState(recipients);
setActionBarColor(recipients.getColor());
} }
}); });
} }
@ -993,6 +1003,18 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return future; return future;
} }
private void setActionBarColor(Optional<Integer> color) {
int colorPrimary = getResources().getColor(R.color.textsecure_primary);
int colorPrimaryDark = getResources().getColor(R.color.textsecure_primary_dark);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.or(colorPrimary)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (color.isPresent()) getWindow().setStatusBarColor(ContactColors.getStatusTinted(color.get()).or(colorPrimaryDark));
else getWindow().setStatusBarColor(colorPrimaryDark);
}
}
private void setBlockedUserState(Recipients recipients) { private void setBlockedUserState(Recipients recipients) {
if (recipients.isBlocked()) { if (recipients.isBlocked()) {
unblockButton.setVisibility(View.VISIBLE); unblockButton.setVisibility(View.VISIBLE);

View File

@ -8,6 +8,7 @@ import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
@ -27,10 +28,11 @@ public class AvatarImageView extends ImageView {
public void setAvatar(@Nullable Recipients recipients, boolean quickContactEnabled) { public void setAvatar(@Nullable Recipients recipients, boolean quickContactEnabled) {
if (recipients != null) { if (recipients != null) {
setImageDrawable(recipients.getContactPhoto().asDrawable(getContext())); int backgroundColor = recipients.getColor().or(ContactColors.UNKNOWN_COLOR);
setImageDrawable(recipients.getContactPhoto().asDrawable(getContext(), backgroundColor));
setAvatarClickHandler(recipients, quickContactEnabled); setAvatarClickHandler(recipients, quickContactEnabled);
} else { } else {
setImageDrawable(ContactPhotoFactory.getDefaultContactPhoto(null).asDrawable(getContext())); setImageDrawable(ContactPhotoFactory.getDefaultContactPhoto(null).asDrawable(getContext(), ContactColors.UNKNOWN_COLOR));
setOnClickListener(null); setOnClickListener(null);
} }
} }

View File

@ -1,21 +1,20 @@
package org.thoughtcrime.securesms.contacts; package org.thoughtcrime.securesms.contacts;
import android.content.Context; import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
public class ContactSelectionListItem extends RelativeLayout implements Recipient.RecipientModifiedListener { public class ContactSelectionListItem extends RelativeLayout implements Recipients.RecipientsModifiedListener {
private ImageView contactPhotoImage; private AvatarImageView contactPhotoImage;
private TextView numberView; private TextView numberView;
private TextView nameView; private TextView nameView;
private TextView labelView; private TextView labelView;
@ -23,7 +22,7 @@ public class ContactSelectionListItem extends RelativeLayout implements Recipien
private long id; private long id;
private String number; private String number;
private Recipient recipient; private Recipients recipients;
public ContactSelectionListItem(Context context) { public ContactSelectionListItem(Context context) {
super(context); super(context);
@ -39,7 +38,8 @@ public class ContactSelectionListItem extends RelativeLayout implements Recipien
@Override @Override
protected void onFinishInflate() { protected void onFinishInflate() {
this.contactPhotoImage = (ImageView) findViewById(R.id.contact_photo_image); super.onFinishInflate();
this.contactPhotoImage = (AvatarImageView) findViewById(R.id.contact_photo_image);
this.numberView = (TextView) findViewById(R.id.number); this.numberView = (TextView) findViewById(R.id.number);
this.labelView = (TextView) findViewById(R.id.label); this.labelView = (TextView) findViewById(R.id.label);
this.nameView = (TextView) findViewById(R.id.name); this.nameView = (TextView) findViewById(R.id.name);
@ -51,15 +51,15 @@ public class ContactSelectionListItem extends RelativeLayout implements Recipien
this.number = number; this.number = number;
if (number != null) { if (number != null) {
this.recipient = RecipientFactory.getRecipientsFromString(getContext(), number, true) this.recipients = RecipientFactory.getRecipientsFromString(getContext(), number, true);
.getPrimaryRecipient(); this.recipients.addListener(this);
} }
this.nameView.setTextColor(color); this.nameView.setTextColor(color);
this.numberView.setTextColor(color); this.numberView.setTextColor(color);
this.contactPhotoImage.setAvatar(recipients, false);
setText(type, name, number, label); setText(type, name, number, label);
setContactPhotoImage(recipient);
if (multiSelect) this.checkBox.setVisibility(View.VISIBLE); if (multiSelect) this.checkBox.setVisibility(View.VISIBLE);
else this.checkBox.setVisibility(View.GONE); else this.checkBox.setVisibility(View.GONE);
@ -70,9 +70,9 @@ public class ContactSelectionListItem extends RelativeLayout implements Recipien
} }
public void unbind() { public void unbind() {
if (recipient != null) { if (recipients != null) {
recipient.removeListener(this); recipients.removeListener(this);
recipient = null; recipients = null;
} }
} }
@ -95,28 +95,6 @@ public class ContactSelectionListItem extends RelativeLayout implements Recipien
this.nameView.setText(name); this.nameView.setText(name);
} }
private void setContactPhotoImage(@Nullable Recipient recipient) {
if (recipient!= null) {
contactPhotoImage.setImageDrawable(recipient.getContactPhoto().asDrawable(getContext()));
recipient.addListener(this);
} else {
contactPhotoImage.setImageDrawable(null);
}
}
@Override
public void onModified(final Recipient recipient) {
if (this.recipient == recipient) {
recipient.removeListener(this);
this.contactPhotoImage.post(new Runnable() {
@Override
public void run() {
contactPhotoImage.setImageDrawable(recipient.getContactPhoto().asDrawable(getContext()));
}
});
}
}
public long getContactId() { public long getContactId() {
return id; return id;
} }
@ -124,4 +102,16 @@ public class ContactSelectionListItem extends RelativeLayout implements Recipien
public String getNumber() { public String getNumber() {
return number; return number;
} }
@Override
public void onModified(final Recipients recipients) {
if (this.recipients == recipients) {
this.contactPhotoImage.post(new Runnable() {
@Override
public void run() {
contactPhotoImage.setAvatar(recipients, false);
}
});
}
}
} }

View File

@ -16,7 +16,7 @@ public class BitmapContactPhoto implements ContactPhoto {
} }
@Override @Override
public Drawable asDrawable(Context context) { public Drawable asDrawable(Context context, int background) {
return RoundedDrawable.fromBitmap(bitmap) return RoundedDrawable.fromBitmap(bitmap)
.setScaleType(ImageView.ScaleType.CENTER_CROP) .setScaleType(ImageView.ScaleType.CENTER_CROP)
.setOval(true); .setOval(true);

View File

@ -0,0 +1,107 @@
package org.thoughtcrime.securesms.contacts.avatars;
import android.support.annotation.NonNull;
import android.util.SparseIntArray;
import com.amulyakhare.textdrawable.util.ColorGenerator;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ContactColors {
public static final int UNKNOWN_COLOR = 0xff9E9E9E;
private static final int RED_300 = 0xffE57373;
private static final int RED_700 = 0xFFD32F2F;
private static final int PINK_300 = 0xffF06292;
private static final int PINK_700 = 0xFFC2185B;
private static final int PURPLE_300 = 0xffBA68C8;
private static final int PURPLE_700 = 0xFF7B1FA2;
private static final int DEEP_PURPLE_300 = 0xff9575CD;
private static final int DEEP_PURPLE_700 = 0xFF512DA8;
private static final int INDIGO_300 = 0xff7986CB;
private static final int INDIGO_700 = 0xFF303F9F;
private static final int BLUE_300 = 0xff64B5F6;
private static final int BLUE_700 = 0xFF1976D2;
private static final int LIGHT_BLUE_300 = 0xff4FC3F7;
private static final int LIGHT_BLUE_700 = 0xFF0288D1;
private static final int CYAN_300 = 0xff4DD0E1;
private static final int CYAN_700 = 0xFF0097A7;
private static final int TEAL_300 = 0xFF4DB6AC;
private static final int TEAL_700 = 0xFF00796B;
private static final int GREEN_300 = 0xFF81C784;
private static final int GREEN_700 = 0xFF388E3C;
private static final int LIGHT_GREEN_300 = 0xFFAED581;
private static final int LIGHT_GREEN_700 = 0xFF689F38;
private static final int LIME_300 = 0xFFDCE775;
private static final int LIME_700 = 0xFFAFB42B;
private static final int YELLOW_300 = 0xFFFFF176;
private static final int YELLOW_700 = 0xFFFBC02D;
private static final int AMBER_300 = 0xFFFFD54F;
private static final int AMBER_700 = 0xFFFFA000;
private static final int ORANGE_300 = 0xFFFFB74D;
private static final int ORANGE_700 = 0xFFF57C00;
private static final int DEEP_ORANGE_300 = 0xFFFF8A65;
private static final int DEEP_ORANGE_700 = 0xFFE64A19;
private static final int BROWN_300 = 0xFFA1887F;
private static final int BROWN_700 = 0xFF5D4037;
private static final int BLUE_GREY_300 = 0xFF90A4AE;
private static final int BLUE_GREY_700 = 0xFF455A64;
private static final List<Integer> MATERIAL_300 = new ArrayList<>(Arrays.asList(
RED_300,
PINK_300,
PURPLE_300,
DEEP_PURPLE_300,
INDIGO_300,
BLUE_300,
LIGHT_BLUE_300,
CYAN_300,
TEAL_300,
GREEN_300,
LIGHT_GREEN_300,
LIME_300,
AMBER_300,
ORANGE_300,
DEEP_ORANGE_300,
BROWN_300,
BLUE_GREY_300)
);
private static final SparseIntArray MATERIAL_300_TO_700 = new SparseIntArray() {{
put(RED_300, RED_700);
put(PINK_300, PINK_700);
put(PURPLE_300, PURPLE_700);
put(DEEP_PURPLE_300, DEEP_PURPLE_700);
put(INDIGO_300, INDIGO_700);
put(BLUE_300, BLUE_700);
put(LIGHT_BLUE_300, LIGHT_BLUE_700);
put(CYAN_300, CYAN_700);
put(TEAL_300, TEAL_700);
put(GREEN_300, GREEN_700);
put(LIGHT_GREEN_300, LIGHT_GREEN_700);
put(LIME_300, LIME_700);
put(AMBER_300, AMBER_700);
put(ORANGE_300, ORANGE_700);
put(DEEP_ORANGE_300, DEEP_ORANGE_700);
put(BROWN_300, BROWN_700);
put(BLUE_GREY_300, BLUE_GREY_700);
}};
private static final ColorGenerator MATERIAL_GENERATOR = ColorGenerator.create(MATERIAL_300);
public static int generateFor(@NonNull String name) {
return MATERIAL_GENERATOR.getColor(name);
}
public static Optional<Integer> getStatusTinted(int color) {
int statusTinted = MATERIAL_300_TO_700.get(color, -1);
return statusTinted == -1 ? Optional.<Integer>absent() : Optional.of(statusTinted);
}
}

View File

@ -5,7 +5,7 @@ import android.graphics.drawable.Drawable;
public interface ContactPhoto { public interface ContactPhoto {
public Drawable asDrawable(Context context); public Drawable asDrawable(Context context, int background);
} }

View File

@ -19,19 +19,17 @@ import java.io.InputStream;
public class ContactPhotoFactory { public class ContactPhotoFactory {
private static final String TAG = ContactPhotoFactory.class.getSimpleName(); private static final String TAG = ContactPhotoFactory.class.getSimpleName();
private static final int UNKNOWN_COLOR = 0xff9E9E9E;
public static ContactPhoto getLoadingPhoto() { public static ContactPhoto getLoadingPhoto() {
return new TransparentContactPhoto(); return new TransparentContactPhoto();
} }
public static ContactPhoto getDefaultContactPhoto(@Nullable String name) { public static ContactPhoto getDefaultContactPhoto(@Nullable String name) {
if (!TextUtils.isEmpty(name)) return new GeneratedContactPhoto(name); if (!TextUtils.isEmpty(name)) return new GeneratedContactPhoto(name);
else return new GeneratedContactPhoto("#", UNKNOWN_COLOR); else return new GeneratedContactPhoto("#");
} }
public static ContactPhoto getDefaultGroupPhoto() { public static ContactPhoto getDefaultGroupPhoto() {
return new ResourceContactPhoto(R.drawable.ic_group_white_24dp, UNKNOWN_COLOR); return new ResourceContactPhoto(R.drawable.ic_group_white_24dp);
} }
public static ContactPhoto getContactPhoto(Context context, Uri uri, String name) { public static ContactPhoto getContactPhoto(Context context, Uri uri, String name) {

View File

@ -12,19 +12,13 @@ import org.thoughtcrime.securesms.R;
public class GeneratedContactPhoto implements ContactPhoto { public class GeneratedContactPhoto implements ContactPhoto {
private final String name; private final String name;
private final int color;
GeneratedContactPhoto(@NonNull String name) { GeneratedContactPhoto(@NonNull String name) {
this(name, ColorGenerator.MATERIAL.getColor(name));
}
GeneratedContactPhoto(@NonNull String name, int color) {
this.name = name; this.name = name;
this.color = color;
} }
@Override @Override
public Drawable asDrawable(Context context) { public Drawable asDrawable(Context context, int background) {
int targetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size); int targetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
return TextDrawable.builder() return TextDrawable.builder()
@ -32,6 +26,6 @@ public class GeneratedContactPhoto implements ContactPhoto {
.width(targetSize) .width(targetSize)
.height(targetSize) .height(targetSize)
.endConfig() .endConfig()
.buildRound(String.valueOf(name.charAt(0)), color); .buildRound(String.valueOf(name.charAt(0)), background);
} }
} }

View File

@ -11,16 +11,14 @@ import com.makeramen.roundedimageview.RoundedDrawable;
public class ResourceContactPhoto implements ContactPhoto { public class ResourceContactPhoto implements ContactPhoto {
private final int resourceId; private final int resourceId;
private final int color;
ResourceContactPhoto(int resourceId, int color) { ResourceContactPhoto(int resourceId) {
this.resourceId = resourceId; this.resourceId = resourceId;
this.color = color;
} }
@Override @Override
public Drawable asDrawable(Context context) { public Drawable asDrawable(Context context, int backgroundColor) {
Drawable background = TextDrawable.builder().buildRound(" ", color); Drawable background = TextDrawable.builder().buildRound(" ", backgroundColor);
RoundedDrawable foreground = (RoundedDrawable) RoundedDrawable.fromDrawable(context.getResources().getDrawable(resourceId)); RoundedDrawable foreground = (RoundedDrawable) RoundedDrawable.fromDrawable(context.getResources().getDrawable(resourceId));
foreground.setScaleType(ImageView.ScaleType.CENTER); foreground.setScaleType(ImageView.ScaleType.CENTER);

View File

@ -10,7 +10,7 @@ public class TransparentContactPhoto implements ContactPhoto {
TransparentContactPhoto() {} TransparentContactPhoto() {}
@Override @Override
public Drawable asDrawable(Context context) { public Drawable asDrawable(Context context, int background) {
return RoundedDrawable.fromDrawable(context.getResources().getDrawable(android.R.color.transparent)); return RoundedDrawable.fromDrawable(context.getResources().getDrawable(android.R.color.transparent));
} }
} }

View File

@ -29,6 +29,7 @@ public class RecipientPreferenceDatabase extends Database {
private static final String NOTIFICATION = "notification"; private static final String NOTIFICATION = "notification";
private static final String VIBRATE = "vibrate"; private static final String VIBRATE = "vibrate";
private static final String MUTE_UNTIL = "mute_until"; private static final String MUTE_UNTIL = "mute_until";
private static final String COLOR = "color";
public enum VibrateState { public enum VibrateState {
DEFAULT(0), ENABLED(1), DISABLED(2); DEFAULT(0), ENABLED(1), DISABLED(2);
@ -55,7 +56,8 @@ public class RecipientPreferenceDatabase extends Database {
BLOCK + " INTEGER DEFAULT 0," + BLOCK + " INTEGER DEFAULT 0," +
NOTIFICATION + " TEXT DEFAULT NULL, " + NOTIFICATION + " TEXT DEFAULT NULL, " +
VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " + VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
MUTE_UNTIL + " INTEGER DEFAULT 0);"; MUTE_UNTIL + " INTEGER DEFAULT 0, " +
COLOR + " INTEGER DEFAULT -1);";
public RecipientPreferenceDatabase(Context context, SQLiteOpenHelper databaseHelper) { public RecipientPreferenceDatabase(Context context, SQLiteOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
@ -87,13 +89,16 @@ public class RecipientPreferenceDatabase extends Database {
String notification = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION)); String notification = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION));
int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE)); int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE));
long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL)); long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL));
int color = cursor.getInt(cursor.getColumnIndexOrThrow(COLOR));
Uri notificationUri = notification == null ? null : Uri.parse(notification); Uri notificationUri = notification == null ? null : Uri.parse(notification);
Log.w(TAG, "Muted until: " + muteUntil); Log.w(TAG, "Muted until: " + muteUntil);
return Optional.of(new RecipientsPreferences(blocked, muteUntil, return Optional.of(new RecipientsPreferences(blocked, muteUntil,
VibrateState.fromId(vibrateState), VibrateState.fromId(vibrateState),
notificationUri)); notificationUri,
color == -1 ? Optional.<Integer>absent() :
Optional.of(color)));
} }
return Optional.absent(); return Optional.absent();
@ -102,6 +107,12 @@ public class RecipientPreferenceDatabase extends Database {
} }
} }
public void setColor(Recipients recipients, int color) {
ContentValues values = new ContentValues();
values.put(COLOR, color);
updateOrInsert(recipients, values);
}
public void setBlocked(Recipients recipients, boolean blocked) { public void setBlocked(Recipients recipients, boolean blocked) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(BLOCK, blocked ? 1 : 0); values.put(BLOCK, blocked ? 1 : 0);
@ -151,12 +162,20 @@ public class RecipientPreferenceDatabase extends Database {
private final long muteUntil; private final long muteUntil;
private final VibrateState vibrateState; private final VibrateState vibrateState;
private final Uri notification; private final Uri notification;
private final Optional<Integer> color;
public RecipientsPreferences(boolean blocked, long muteUntil, VibrateState vibrateState, Uri notification) { public RecipientsPreferences(boolean blocked, long muteUntil, VibrateState vibrateState,
Uri notification, Optional<Integer> color)
{
this.blocked = blocked; this.blocked = blocked;
this.muteUntil = muteUntil; this.muteUntil = muteUntil;
this.vibrateState = vibrateState; this.vibrateState = vibrateState;
this.notification = notification; this.notification = notification;
this.color = color;
}
public Optional<Integer> getColor() {
return color;
} }
public boolean isBlocked() { public boolean isBlocked() {

View File

@ -46,6 +46,7 @@ import android.util.Log;
import org.thoughtcrime.securesms.ConversationActivity; import org.thoughtcrime.securesms.ConversationActivity;
import org.thoughtcrime.securesms.ConversationListActivity; import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsSmsDatabase; import org.thoughtcrime.securesms.database.MmsSmsDatabase;
@ -192,8 +193,10 @@ public class MessageNotifier {
List<NotificationItem> notifications = notificationState.getNotifications(); List<NotificationItem> notifications = notificationState.getNotifications();
NotificationCompat.Builder builder = new NotificationCompat.Builder(context); NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
Recipient recipient = notifications.get(0).getIndividualRecipient(); Recipient recipient = notifications.get(0).getIndividualRecipient();
Drawable recipientPhoto = recipient.getContactPhoto().asDrawable(context); Recipients recipients = notifications.get(0).getRecipients();
int largeIconTargetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size); int largeIconTargetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
Drawable recipientPhoto = recipient.getContactPhoto().asDrawable(context, recipients == null ? ContactColors.UNKNOWN_COLOR :
recipients.getColor().or(ContactColors.UNKNOWN_COLOR));
if (recipientPhoto != null) { if (recipientPhoto != null) {
Bitmap recipientPhotoBitmap = BitmapUtil.createFromDrawable(recipientPhoto, largeIconTargetSize, largeIconTargetSize); Bitmap recipientPhotoBitmap = BitmapUtil.createFromDrawable(recipientPhoto, largeIconTargetSize, largeIconTargetSize);

View File

@ -18,6 +18,7 @@ package org.thoughtcrime.securesms.recipients;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
@ -92,7 +93,7 @@ public class Recipient {
return this.contactUri; return this.contactUri;
} }
public synchronized String getName() { public synchronized @Nullable String getName() {
return this.name; return this.name;
} }

View File

@ -178,12 +178,14 @@ public class RecipientProvider {
} }
public static class RecipientDetails { public static class RecipientDetails {
public final String name; @Nullable public final String name;
public final String number; @NonNull public final String number;
public final ContactPhoto avatar; @NonNull public final ContactPhoto avatar;
public final Uri contactUri; @Nullable public final Uri contactUri;
public RecipientDetails(String name, String number, Uri contactUri, ContactPhoto avatar) { public RecipientDetails(@Nullable String name, @NonNull String number,
@Nullable Uri contactUri, @NonNull ContactPhoto avatar)
{
this.name = name; this.name = name;
this.number = number; this.number = number;
this.avatar = avatar; this.avatar = avatar;

View File

@ -22,6 +22,7 @@ import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import android.util.Patterns; import android.util.Patterns;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
@ -32,6 +33,7 @@ import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.ListenableFutureTask; import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.thoughtcrime.securesms.util.NumberUtil; import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -53,6 +55,7 @@ public class Recipients implements Iterable<Recipient>, RecipientModifiedListene
private long mutedUntil = 0; private long mutedUntil = 0;
private boolean blocked = false; private boolean blocked = false;
private VibrateState vibrate = VibrateState.DEFAULT; private VibrateState vibrate = VibrateState.DEFAULT;
private Optional<Integer> color = Optional.absent();
Recipients() { Recipients() {
this(new LinkedList<Recipient>(), (RecipientsPreferences)null); this(new LinkedList<Recipient>(), (RecipientsPreferences)null);
@ -66,6 +69,7 @@ public class Recipients implements Iterable<Recipient>, RecipientModifiedListene
mutedUntil = preferences.getMuteUntil(); mutedUntil = preferences.getMuteUntil();
vibrate = preferences.getVibrateState(); vibrate = preferences.getVibrateState();
blocked = preferences.isBlocked(); blocked = preferences.isBlocked();
color = preferences.getColor();
} }
} }
@ -84,6 +88,7 @@ public class Recipients implements Iterable<Recipient>, RecipientModifiedListene
mutedUntil = result.getMuteUntil(); mutedUntil = result.getMuteUntil();
vibrate = result.getVibrateState(); vibrate = result.getVibrateState();
blocked = result.isBlocked(); blocked = result.isBlocked();
color = result.getColor();
localListeners = new HashSet<>(listeners); localListeners = new HashSet<>(listeners);
} }
@ -101,6 +106,21 @@ public class Recipients implements Iterable<Recipient>, RecipientModifiedListene
}); });
} }
public synchronized Optional<Integer> getColor() {
if (color.isPresent()) return color;
else if (isGroupRecipient()) return Optional.absent();
else if (recipients.get(0).getName() == null) return Optional.absent();
else return Optional.of(ContactColors.generateFor(recipients.get(0).getName()));
}
public void setColor(Optional<Integer> color) {
synchronized (this) {
this.color = color;
}
notifyListeners();
}
public synchronized @Nullable Uri getRingtone() { public synchronized @Nullable Uri getRingtone() {
return ringtone; return ringtone;
} }