Fully colorize conversations.

1. Switch from 300 to 500 colors.

2. Colorize incoming conversation bubbles.

3. Colorize recipeint preference activity toolbar.

4. Support inverted colors in avatars.

5. Make status bar icons tint according to secondary color.

// FREEBIE
This commit is contained in:
Moxie Marlinspike 2015-06-29 15:33:36 -07:00
parent 99c9c73c9d
commit 78289ded8f
40 changed files with 270 additions and 128 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

View File

@ -6,7 +6,6 @@
android:orientation="vertical"> android:orientation="vertical">
<ListView android:id="@android:id/list" <ListView android:id="@android:id/list"
style="?android:attr/listViewWhiteStyle"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1.0" android:layout_weight="1.0"
@ -19,6 +18,6 @@
android:divider="@android:color/transparent" android:divider="@android:color/transparent"
android:dividerHeight="0dp" android:dividerHeight="0dp"
android:layout_marginBottom="1dip" android:layout_marginBottom="1dip"
android:cacheColorHint="?android:attr/windowBackground" /> android:cacheColorHint="?conversation_background" />
</LinearLayout> </LinearLayout>

View File

@ -65,7 +65,7 @@
android:paddingLeft="4dp" android:paddingLeft="4dp"
android:paddingRight="4dp" android:paddingRight="4dp"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?conversation_received_text_primary_color" android:textColor="?conversation_item_received_text_primary_color"
android:textSize="16sp" android:textSize="16sp"
android:autoLink="all" android:autoLink="all"
android:linksClickable="true" /> android:linksClickable="true" />
@ -117,9 +117,11 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:paddingRight="4dp" android:paddingRight="4dp"
android:paddingTop="2dp" android:paddingTop="2dp"
android:src="?menu_lock_icon_small_received" android:src="?menu_lock_icon_small"
android:contentDescription="@string/conversation_item__secure_message_description" android:contentDescription="@string/conversation_item__secure_message_description"
android:visibility="gone" /> android:visibility="gone"
android:tint="?conversation_item_received_text_secondary_color"
android:tintMode="multiply"/>
<FrameLayout android:id="@+id/pending_indicator_stub" <FrameLayout android:id="@+id/pending_indicator_stub"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -131,7 +133,7 @@
android:layout_gravity="left" android:layout_gravity="left"
android:paddingTop="1dip" android:paddingTop="1dip"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?conversation_received_text_secondary_color" android:textColor="?conversation_item_received_text_secondary_color"
android:textSize="@dimen/conversation_item_date_text_size" android:textSize="@dimen/conversation_item_date_text_size"
android:fontFamily="sans-serif-light" android:fontFamily="sans-serif-light"
android:autoLink="none" android:autoLink="none"

View File

@ -147,20 +147,24 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end" android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_done_grey600_18dp" android:src="@drawable/ic_done_white_18dp"
android:paddingLeft="2dp" android:paddingLeft="2dp"
android:paddingBottom="2dp" android:paddingBottom="2dp"
android:visibility="gone" android:visibility="gone"
android:tint="?conversation_item_sent_text_secondary_color"
android:tintMode="multiply"
android:contentDescription="@string/conversation_item_sent__delivered_description" /> android:contentDescription="@string/conversation_item_sent__delivered_description" />
<ImageView android:id="@+id/delivered_indicator" <ImageView android:id="@+id/delivered_indicator"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end" android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_done_all_grey600_18dp" android:src="@drawable/ic_done_all_white_18dp"
android:paddingLeft="2dp" android:paddingLeft="2dp"
android:paddingBottom="2dp" android:paddingBottom="2dp"
android:visibility="gone" android:visibility="gone"
android:tint="?conversation_item_sent_text_secondary_color"
android:tintMode="multiply"
android:contentDescription="@string/conversation_item_sent__delivered_description" /> android:contentDescription="@string/conversation_item_sent__delivered_description" />
<ImageView android:id="@+id/secure_indicator" <ImageView android:id="@+id/secure_indicator"
@ -171,6 +175,8 @@
android:layout_gravity="center_vertical|end" android:layout_gravity="center_vertical|end"
android:paddingLeft="2dp" android:paddingLeft="2dp"
android:paddingBottom="3dp" android:paddingBottom="3dp"
android:tint="?conversation_item_sent_text_secondary_color"
android:tintMode="multiply"
android:contentDescription="@string/conversation_item__secure_message_description" /> android:contentDescription="@string/conversation_item__secure_message_description" />
</LinearLayout> </LinearLayout>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -25,7 +26,8 @@
android:layout_height="50dp" android:layout_height="50dp"
android:cropToPadding="true" android:cropToPadding="true"
android:layout_marginLeft="0dp" android:layout_marginLeft="0dp"
android:layout_alignParentLeft="true"/> android:layout_alignParentLeft="true"
app:inverted="true"/>
<TextView android:id="@+id/name" <TextView android:id="@+id/name"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -39,20 +39,6 @@
<item>Ukrainian Український</item> <item>Ukrainian Український</item>
</string-array> </string-array>
<array name="avatar_colors">
<item>#6dcaec</item>
<item>#cf9fe7</item>
<item>#b6db49</item>
<item>#ffd060</item>
<item>#ff7979</item>
<item>#2cb1e1</item>
<item>#c182e0</item>
<item>#92c500</item>
<item>#ffb61c</item>
<item>#f83a3a</item>
</array>
<string-array name="language_values"> <string-array name="language_values">
<item>zz</item> <item>zz</item>
<item>en</item> <item>en</item>
@ -191,23 +177,23 @@
</string-array> </string-array>
<string-array name="default_color_choice_values" translatable="false"> <string-array name="default_color_choice_values" translatable="false">
<item>#ffE57373</item> <item>#ffF44336</item>
<item>#ffF06292</item> <item>#ffE91E63</item>
<item>#ffBA68C8</item> <item>#ff9C27B0</item>
<item>#ff9575CD</item> <item>#ff673AB7</item>
<item>#ff7986CB</item> <item>#ff3F51B5</item>
<item>#ff64B5F6</item> <item>#ff2196F3</item>
<item>#ff4FC3F7</item> <item>#ff03A9F4</item>
<item>#ff4DD0E1</item> <item>#ff00BCD4</item>
<item>#FF4DB6AC</item> <item>#ff009688</item>
<item>#FF81C784</item> <item>#ff4CAF50</item>
<item>#FFAED581</item> <item>#ff8BC34A</item>
<item>#FFDCE775</item> <!--<item>#FFCDDC39</item>-->
<item>#FFFFD54F</item> <item>#FFFFC107</item>
<item>#FFFFB74D</item> <item>#ffFF9800</item>
<item>#FFFF8A65</item> <item>#ffFF5722</item>
<item>#FFA1887F</item> <item>#ff795548</item>
<item>#FF90A4AE</item> <item>#ff607D8B</item>
</string-array> </string-array>
</resources> </resources>

View File

@ -14,8 +14,7 @@
<attr name="conversation_sent_card_background" format="reference|color"/> <attr name="conversation_sent_card_background" format="reference|color"/>
<attr name="conversation_group_member_name" format="reference|color"/> <attr name="conversation_group_member_name" format="reference|color"/>
<attr name="conversation_received_card_background" format="reference|color"/> <attr name="conversation_received_card_background" format="reference|color"/>
<attr name="conversation_received_text_primary_color" format="reference|color"/>
<attr name="conversation_received_text_secondary_color" format="reference|color"/>
<attr name="fab_color" format="reference|color" /> <attr name="fab_color" format="reference|color" />
<attr name="lower_right_divet" format="reference" /> <attr name="lower_right_divet" format="reference" />
@ -57,6 +56,8 @@
<attr name="conversation_item_bubble_background" format="reference|color"/> <attr name="conversation_item_bubble_background" format="reference|color"/>
<attr name="conversation_item_sent_text_primary_color" format="reference|color"/> <attr name="conversation_item_sent_text_primary_color" format="reference|color"/>
<attr name="conversation_item_sent_text_secondary_color" format="reference|color"/> <attr name="conversation_item_sent_text_secondary_color" format="reference|color"/>
<attr name="conversation_item_received_text_primary_color" format="reference|color"/>
<attr name="conversation_item_received_text_secondary_color" format="reference|color"/>
<attr name="conversation_item_sent_text_indicator_tab_color" format="reference|color"/> <attr name="conversation_item_sent_text_indicator_tab_color" format="reference|color"/>
<attr name="conversation_item_sent_indicator_text_background" format="reference" /> <attr name="conversation_item_sent_indicator_text_background" format="reference" />
@ -87,7 +88,6 @@
<attr name="menu_unlock_icon" format="reference" /> <attr name="menu_unlock_icon" format="reference" />
<attr name="menu_lock_icon" format="reference" /> <attr name="menu_lock_icon" format="reference" />
<attr name="menu_lock_icon_small" format="reference" /> <attr name="menu_lock_icon_small" format="reference" />
<attr name="menu_lock_icon_small_received" format="reference" />
<attr name="menu_trash_icon" format="reference" /> <attr name="menu_trash_icon" format="reference" />
<attr name="menu_selectall_icon" format="reference" /> <attr name="menu_selectall_icon" format="reference" />
<attr name="menu_group_icon" format="reference" /> <attr name="menu_group_icon" format="reference" />
@ -130,4 +130,8 @@
<attr name="numColumns" format="integer" /> <attr name="numColumns" format="integer" />
</declare-styleable> </declare-styleable>
<declare-styleable name="AvatarImageView">
<attr name="inverted" format="boolean"/>
</declare-styleable>
</resources> </resources>

View File

@ -41,7 +41,7 @@
<item name="titleTextStyle">@style/TextSecure.TitleTextStyle</item> <item name="titleTextStyle">@style/TextSecure.TitleTextStyle</item>
<item name="subtitleTextStyle">@style/TextSecure.SubtitleTextStyle</item> <item name="subtitleTextStyle">@style/TextSecure.SubtitleTextStyle</item>
<item name="android:textColorPrimary">@color/white</item> <item name="android:textColorPrimary">@color/white</item>
<item name="android:textColorSecondary">#99ffffff</item> <item name="android:textColorSecondary">#BFffffff</item>
</style> </style>
<style name="TextSecure.DarkActionBar.TabBar" <style name="TextSecure.DarkActionBar.TabBar"
@ -61,7 +61,7 @@
</style> </style>
<style name="TextSecure.SubtitleTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Subtitle"> <style name="TextSecure.SubtitleTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Subtitle">
<item name="android:textColor">#99ffffff</item> <item name="android:textColor">#BFffffff</item>
</style> </style>
<style name="TextSecure.IntroActionBar" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse"> <style name="TextSecure.IntroActionBar" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse">

View File

@ -78,8 +78,6 @@
<item name="lower_right_divet">@drawable/divet_lower_right_dark</item> <item name="lower_right_divet">@drawable/divet_lower_right_dark</item>
<item name="conversation_group_member_name">#99000000</item> <item name="conversation_group_member_name">#99000000</item>
<item name="conversation_received_text_primary_color">#ff333333</item>
<item name="conversation_received_text_secondary_color">#99333333</item>
<item name="contact_selection_push_user">#ff000000</item> <item name="contact_selection_push_user">#ff000000</item>
<item name="contact_selection_lay_user">#a0000000</item> <item name="contact_selection_lay_user">#a0000000</item>
@ -116,10 +114,13 @@
<item name="emoji_category_symbol">@drawable/emoji_category_symbol_light</item> <item name="emoji_category_symbol">@drawable/emoji_category_symbol_light</item>
<item name="emoji_category_emoticons">@drawable/emoji_category_emoticons_light</item> <item name="emoji_category_emoticons">@drawable/emoji_category_emoticons_light</item>
<item name="conversation_item_sent_text_primary_color">#99000000</item>
<item name="conversation_item_bubble_background">@color/white</item> <item name="conversation_item_bubble_background">@color/white</item>
<item name="conversation_item_sent_text_primary_color">#99000000</item>
<item name="conversation_item_sent_text_secondary_color">#bb000000</item> <item name="conversation_item_sent_text_secondary_color">#bb000000</item>
<item name="conversation_item_sent_text_indicator_tab_color">#99000000</item> <item name="conversation_item_sent_text_indicator_tab_color">#99000000</item>
<item name="conversation_item_received_text_primary_color">@color/white</item>
<item name="conversation_item_received_text_secondary_color">#BFffffff</item>
<item name="conversation_item_background">@drawable/conversation_item_background</item> <item name="conversation_item_background">@drawable/conversation_item_background</item>
<item name="conversation_item_sent_indicator_text_background">@drawable/conversation_item_sent_indicator_text_shape</item> <item name="conversation_item_sent_indicator_text_background">@drawable/conversation_item_sent_indicator_text_shape</item>
@ -140,8 +141,7 @@
<item name="menu_popup_expand">@drawable/ic_launch_white_24dp</item> <item name="menu_popup_expand">@drawable/ic_launch_white_24dp</item>
<item name="menu_unlock_icon">@drawable/ic_unlocked_white_24dp</item> <item name="menu_unlock_icon">@drawable/ic_unlocked_white_24dp</item>
<item name="menu_lock_icon">@drawable/ic_lock_white_24dp</item> <item name="menu_lock_icon">@drawable/ic_lock_white_24dp</item>
<item name="menu_lock_icon_small">@drawable/ic_lock_black_18dp</item> <item name="menu_lock_icon_small">@drawable/ic_lock_white_18dp</item>
<item name="menu_lock_icon_small_received">@drawable/ic_lock_black_18dp</item>
<item name="menu_trash_icon">@drawable/ic_delete_white_24dp</item> <item name="menu_trash_icon">@drawable/ic_delete_white_24dp</item>
<item name="menu_selectall_icon">@drawable/ic_select_all_white_24dp</item> <item name="menu_selectall_icon">@drawable/ic_select_all_white_24dp</item>
<item name="menu_split_icon">@drawable/ic_call_split_white_24dp</item> <item name="menu_split_icon">@drawable/ic_call_split_white_24dp</item>
@ -188,8 +188,6 @@
<item name="share_list_item_divider">@drawable/share_list_divider_shape_dark</item> <item name="share_list_item_divider">@drawable/share_list_divider_shape_dark</item>
<item name="conversation_group_member_name">#99ffffff</item> <item name="conversation_group_member_name">#99ffffff</item>
<item name="conversation_received_text_primary_color">#ffeeeeee</item>
<item name="conversation_received_text_secondary_color">#99eeeeee</item>
<item name="contact_selection_push_user">#ffeeeeee</item> <item name="contact_selection_push_user">#ffeeeeee</item>
<item name="contact_selection_lay_user">#afeeeeee</item> <item name="contact_selection_lay_user">#afeeeeee</item>
@ -201,6 +199,8 @@
<item name="conversation_item_sent_text_primary_color">#ffffffff</item> <item name="conversation_item_sent_text_primary_color">#ffffffff</item>
<item name="conversation_item_sent_text_secondary_color">#aaeeeeee</item> <item name="conversation_item_sent_text_secondary_color">#aaeeeeee</item>
<item name="conversation_item_sent_text_indicator_tab_color">#99ffffff</item> <item name="conversation_item_sent_text_indicator_tab_color">#99ffffff</item>
<item name="conversation_item_received_text_primary_color">@color/white</item>
<item name="conversation_item_received_text_secondary_color">#BFffffff</item>
<item name="conversation_item_sent_indicator_text_background">@drawable/conversation_item_sent_indicator_text_shape_dark</item> <item name="conversation_item_sent_indicator_text_background">@drawable/conversation_item_sent_indicator_text_shape_dark</item>
<item name="dialog_info_icon">@drawable/ic_info_outline_dark</item> <item name="dialog_info_icon">@drawable/ic_info_outline_dark</item>
@ -254,7 +254,6 @@
<item name="menu_unlock_icon">@drawable/ic_unlocked_white_24dp</item> <item name="menu_unlock_icon">@drawable/ic_unlocked_white_24dp</item>
<item name="menu_lock_icon">@drawable/ic_lock_white_24dp</item> <item name="menu_lock_icon">@drawable/ic_lock_white_24dp</item>
<item name="menu_lock_icon_small">@drawable/ic_lock_white_18dp</item> <item name="menu_lock_icon_small">@drawable/ic_lock_white_18dp</item>
<item name="menu_lock_icon_small_received">@drawable/ic_lock_white_18dp</item>
<item name="menu_trash_icon">@drawable/ic_delete_white_24dp</item> <item name="menu_trash_icon">@drawable/ic_delete_white_24dp</item>
<item name="menu_selectall_icon">@drawable/ic_select_all_white_24dp</item> <item name="menu_selectall_icon">@drawable/ic_select_all_white_24dp</item>
<item name="menu_split_icon">@drawable/ic_call_split_white_24dp</item> <item name="menu_split_icon">@drawable/ic_call_split_white_24dp</item>

View File

@ -190,8 +190,16 @@ public class ConversationItem extends LinearLayout {
private void setBubbleState(MessageRecord messageRecord) { private void setBubbleState(MessageRecord messageRecord) {
int[] attributes = new int[]{R.attr.conversation_item_bubble_background}; int[] attributes = new int[]{R.attr.conversation_item_bubble_background};
TypedArray colors = context.obtainStyledAttributes(attributes); TypedArray colors = context.obtainStyledAttributes(attributes);
int defaultColor = colors.getColor(0, 0xFFFFFF);
bodyBubble.getBackground().setColorFilter(colors.getColor(0, 0xFFFFFFFF), PorterDuff.Mode.MULTIPLY); if (messageRecord.isOutgoing()) {
bodyBubble.getBackground().setColorFilter(defaultColor, PorterDuff.Mode.MULTIPLY);
} else {
bodyBubble.getBackground().setColorFilter(messageRecord.getIndividualRecipient()
.getColor()
.or(defaultColor),
PorterDuff.Mode.MULTIPLY);
}
colors.recycle(); colors.recycle();
} }

View File

@ -6,6 +6,7 @@ import android.media.Ringtone;
import android.media.RingtoneManager; import android.media.RingtoneManager;
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.os.Handler; import android.os.Handler;
import android.preference.CheckBoxPreference; import android.preference.CheckBoxPreference;
@ -18,7 +19,6 @@ import android.support.v4.app.Fragment;
import android.support.v4.preference.PreferenceFragment; import android.support.v4.preference.PreferenceFragment;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
@ -26,6 +26,7 @@ import android.widget.TextView;
import com.afollestad.materialdialogs.AlertDialogWrapper; import com.afollestad.materialdialogs.AlertDialogWrapper;
import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.AvatarImageView;
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.RecipientPreferenceDatabase.VibrateState; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState;
@ -53,6 +54,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private AvatarImageView avatar; private AvatarImageView avatar;
private Toolbar toolbar;
private TextView title; private TextView title;
private TextView blockedIndicator; private TextView blockedIndicator;
@ -103,7 +105,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
} }
private void initializeToolbar() { private void initializeToolbar() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); this.toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@ -115,8 +117,18 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
} }
private void setHeader(Recipients recipients) { private void setHeader(Recipients recipients) {
Optional<Integer> color = recipients.getColor();
this.avatar.setAvatar(recipients, true); this.avatar.setAvatar(recipients, true);
this.title.setText(recipients.toShortString()); this.title.setText(recipients.toShortString());
this.toolbar.setBackgroundColor(color.or(getResources().getColor(R.color.textsecure_primary)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int primaryDark = getResources().getColor(R.color.textsecure_primary_dark);
if (color.isPresent()) getWindow().setStatusBarColor(ContactColors.getStatusTinted(color.get()).or(primaryDark));
else getWindow().setStatusBarColor(primaryDark);
}
if (recipients.isBlocked()) this.blockedIndicator.setVisibility(View.VISIBLE); if (recipients.isBlocked()) this.blockedIndicator.setVisibility(View.VISIBLE);
else this.blockedIndicator.setVisibility(View.GONE); else this.blockedIndicator.setVisibility(View.GONE);
@ -207,11 +219,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
} }
if (recipients.getColor().isPresent()) { if (recipients.getColor().isPresent()) {
colorPreference.setValue(recipients.getColor().get());
colorPreference.setEnabled(true); colorPreference.setEnabled(true);
colorPreference.setValue(recipients.getColor().get());
} else { } else {
colorPreference.setValue(getResources().getColor(R.color.textsecure_primary));
colorPreference.setEnabled(false); colorPreference.setEnabled(false);
colorPreference.setValue(getResources().getColor(R.color.textsecure_primary));
} }
if (!recipients.isSingleRecipient() || recipients.isGroupRecipient()) { if (!recipients.isSingleRecipient() || recipients.isGroupRecipient()) {
@ -288,7 +300,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
final int value = (Integer)newValue; final int value = (Integer)newValue;
if (value != recipients.getColor().get()) { if (preference.isEnabled() && value != recipients.getColor().get()) {
recipients.setColor(Optional.of(value)); recipients.setColor(Optional.of(value));
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {

View File

@ -2,12 +2,14 @@ package org.thoughtcrime.securesms.components;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.TypedArray;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors; 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;
@ -16,6 +18,8 @@ import org.thoughtcrime.securesms.recipients.Recipients;
public class AvatarImageView extends ImageView { public class AvatarImageView extends ImageView {
private boolean inverted;
public AvatarImageView(Context context) { public AvatarImageView(Context context) {
super(context); super(context);
setScaleType(ScaleType.CENTER_CROP); setScaleType(ScaleType.CENTER_CROP);
@ -24,15 +28,21 @@ public class AvatarImageView extends ImageView {
public AvatarImageView(Context context, AttributeSet attrs) { public AvatarImageView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
setScaleType(ScaleType.CENTER_CROP); setScaleType(ScaleType.CENTER_CROP);
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AvatarImageView, 0, 0);
inverted = typedArray.getBoolean(0, false);
typedArray.recycle();
}
} }
public void setAvatar(@Nullable Recipients recipients, boolean quickContactEnabled) { public void setAvatar(@Nullable Recipients recipients, boolean quickContactEnabled) {
if (recipients != null) { if (recipients != null) {
int backgroundColor = recipients.getColor().or(ContactColors.UNKNOWN_COLOR); int backgroundColor = recipients.getColor().or(ContactColors.UNKNOWN_COLOR);
setImageDrawable(recipients.getContactPhoto().asDrawable(getContext(), backgroundColor)); setImageDrawable(recipients.getContactPhoto().asDrawable(getContext(), backgroundColor, inverted));
setAvatarClickHandler(recipients, quickContactEnabled); setAvatarClickHandler(recipients, quickContactEnabled);
} else { } else {
setImageDrawable(ContactPhotoFactory.getDefaultContactPhoto(null).asDrawable(getContext(), ContactColors.UNKNOWN_COLOR)); setImageDrawable(ContactPhotoFactory.getDefaultContactPhoto(null).asDrawable(getContext(), ContactColors.UNKNOWN_COLOR, inverted));
setOnClickListener(null); setOnClickListener(null);
} }
} }

View File

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

View File

@ -16,40 +16,58 @@ public class ContactColors {
public static final int UNKNOWN_COLOR = 0xff9E9E9E; public static final int UNKNOWN_COLOR = 0xff9E9E9E;
private static final int RED_300 = 0xffE57373; private static final int RED_300 = 0xffE57373;
private static final int RED_500 = 0xffF44336;
private static final int RED_700 = 0xFFD32F2F; private static final int RED_700 = 0xFFD32F2F;
private static final int PINK_300 = 0xffF06292; private static final int PINK_300 = 0xffF06292;
private static final int PINK_500 = 0xffE91E63;
private static final int PINK_700 = 0xFFC2185B; private static final int PINK_700 = 0xFFC2185B;
private static final int PURPLE_300 = 0xffBA68C8; private static final int PURPLE_300 = 0xffBA68C8;
private static final int PURPLE_500 = 0Xff9C27B0;
private static final int PURPLE_700 = 0xFF7B1FA2; private static final int PURPLE_700 = 0xFF7B1FA2;
private static final int DEEP_PURPLE_300 = 0xff9575CD; private static final int DEEP_PURPLE_300 = 0xff9575CD;
private static final int DEEP_PURPLE_500 = 0xff673AB7;
private static final int DEEP_PURPLE_700 = 0xFF512DA8; private static final int DEEP_PURPLE_700 = 0xFF512DA8;
private static final int INDIGO_300 = 0xff7986CB; private static final int INDIGO_300 = 0xff7986CB;
private static final int INDIGO_700 = 0xFF303F9F; private static final int INDIGO_500 = 0xff3F51B5;
private static final int INDIGO_700 = 0xff303F9F;
private static final int BLUE_300 = 0xff64B5F6; private static final int BLUE_300 = 0xff64B5F6;
private static final int BLUE_500 = 0xff2196F3;
private static final int BLUE_700 = 0xFF1976D2; private static final int BLUE_700 = 0xFF1976D2;
private static final int LIGHT_BLUE_300 = 0xff4FC3F7; private static final int LIGHT_BLUE_300 = 0xff4FC3F7;
private static final int LIGHT_BLUE_500 = 0xff03A9F4;
private static final int LIGHT_BLUE_700 = 0xFF0288D1; private static final int LIGHT_BLUE_700 = 0xFF0288D1;
private static final int CYAN_300 = 0xff4DD0E1; private static final int CYAN_300 = 0xff4DD0E1;
private static final int CYAN_500 = 0xff00BCD4;
private static final int CYAN_700 = 0xFF0097A7; private static final int CYAN_700 = 0xFF0097A7;
private static final int TEAL_300 = 0xFF4DB6AC; private static final int TEAL_300 = 0xFF4DB6AC;
private static final int TEAL_500 = 0xff009688;
private static final int TEAL_700 = 0xFF00796B; private static final int TEAL_700 = 0xFF00796B;
private static final int GREEN_300 = 0xFF81C784; private static final int GREEN_300 = 0xFF81C784;
private static final int GREEN_500 = 0xff4CAF50;
private static final int GREEN_700 = 0xFF388E3C; private static final int GREEN_700 = 0xFF388E3C;
private static final int LIGHT_GREEN_300 = 0xFFAED581; private static final int LIGHT_GREEN_300 = 0xFFAED581;
private static final int LIGHT_GREEN_500 = 0xff8BC34A;
private static final int LIGHT_GREEN_700 = 0xFF689F38; private static final int LIGHT_GREEN_700 = 0xFF689F38;
private static final int LIME_300 = 0xFFDCE775; private static final int LIME_300 = 0xFFDCE775;
private static final int LIME_500 = 0XFFCDDC39;
private static final int LIME_700 = 0xFFAFB42B; private static final int LIME_700 = 0xFFAFB42B;
private static final int YELLOW_300 = 0xFFFFF176; private static final int YELLOW_300 = 0xFFFFF176;
private static final int YELLOW_500 = 0xffFFEB3B;
private static final int YELLOW_700 = 0xFFFBC02D; private static final int YELLOW_700 = 0xFFFBC02D;
private static final int AMBER_300 = 0xFFFFD54F; private static final int AMBER_300 = 0xFFFFD54F;
private static final int AMBER_500 = 0XFFFFC107;
private static final int AMBER_700 = 0xFFFFA000; private static final int AMBER_700 = 0xFFFFA000;
private static final int ORANGE_300 = 0xFFFFB74D; private static final int ORANGE_300 = 0xFFFFB74D;
private static final int ORANGE_500 = 0xffFF9800;
private static final int ORANGE_700 = 0xFFF57C00; private static final int ORANGE_700 = 0xFFF57C00;
private static final int DEEP_ORANGE_300 = 0xFFFF8A65; private static final int DEEP_ORANGE_300 = 0xFFFF8A65;
private static final int DEEP_ORANGE_500 = 0xffFF5722;
private static final int DEEP_ORANGE_700 = 0xFFE64A19; private static final int DEEP_ORANGE_700 = 0xFFE64A19;
private static final int BROWN_300 = 0xFFA1887F; private static final int BROWN_300 = 0xFFA1887F;
private static final int BROWN_500 = 0xff795548;
private static final int BROWN_700 = 0xFF5D4037; private static final int BROWN_700 = 0xFF5D4037;
private static final int BLUE_GREY_300 = 0xFF90A4AE; private static final int BLUE_GREY_300 = 0xFF90A4AE;
private static final int BLUE_GREY_500 = 0xff607D8B;
private static final int BLUE_GREY_700 = 0xFF455A64; private static final int BLUE_GREY_700 = 0xFF455A64;
private static final List<Integer> MATERIAL_300 = new ArrayList<>(Arrays.asList( private static final List<Integer> MATERIAL_300 = new ArrayList<>(Arrays.asList(
@ -72,6 +90,46 @@ public class ContactColors {
BLUE_GREY_300) BLUE_GREY_300)
); );
private static final List<Integer> MATERIAL_500 = new ArrayList<>(Arrays.asList(
RED_500,
PINK_500,
PURPLE_500,
DEEP_PURPLE_500,
INDIGO_500,
BLUE_500,
LIGHT_BLUE_500,
CYAN_500,
TEAL_500,
GREEN_500,
LIGHT_GREEN_500,
// LIME_500,
AMBER_500,
ORANGE_500,
DEEP_ORANGE_500,
BROWN_500,
BLUE_GREY_500)
);
private static final SparseIntArray MATERIAL_500_TO_700 = new SparseIntArray() {{
put(RED_500, RED_700);
put(PINK_500, PINK_700);
put(PURPLE_500, PURPLE_700);
put(DEEP_PURPLE_500, DEEP_PURPLE_700);
put(INDIGO_500, INDIGO_700);
put(BLUE_500, BLUE_700);
put(LIGHT_BLUE_500, LIGHT_BLUE_700);
put(CYAN_500, CYAN_700);
put(TEAL_500, TEAL_700);
put(GREEN_500, GREEN_700);
put(LIGHT_GREEN_500, LIGHT_GREEN_700);
// put(LIME_500, LIME_700);
put(AMBER_500, AMBER_700);
put(ORANGE_500, ORANGE_700);
put(DEEP_ORANGE_500, DEEP_ORANGE_700);
put(BROWN_500, BROWN_700);
put(BLUE_GREY_500, BLUE_GREY_700);
}};
private static final SparseIntArray MATERIAL_300_TO_700 = new SparseIntArray() {{ private static final SparseIntArray MATERIAL_300_TO_700 = new SparseIntArray() {{
put(RED_300, RED_700); put(RED_300, RED_700);
put(PINK_300, PINK_700); put(PINK_300, PINK_700);
@ -92,15 +150,14 @@ public class ContactColors {
put(BLUE_GREY_300, BLUE_GREY_700); put(BLUE_GREY_300, BLUE_GREY_700);
}}; }};
private static final ColorGenerator MATERIAL_GENERATOR = ColorGenerator.create(MATERIAL_500);
private static final ColorGenerator MATERIAL_GENERATOR = ColorGenerator.create(MATERIAL_300);
public static int generateFor(@NonNull String name) { public static int generateFor(@NonNull String name) {
return MATERIAL_GENERATOR.getColor(name); return MATERIAL_GENERATOR.getColor(name);
} }
public static Optional<Integer> getStatusTinted(int color) { public static Optional<Integer> getStatusTinted(int color) {
int statusTinted = MATERIAL_300_TO_700.get(color, -1); int statusTinted = MATERIAL_500_TO_700.get(color, -1);
return statusTinted == -1 ? Optional.<Integer>absent() : Optional.of(statusTinted); return statusTinted == -1 ? Optional.<Integer>absent() : Optional.of(statusTinted);
} }

View File

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

View File

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.contacts.avatars; package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -18,14 +19,20 @@ public class GeneratedContactPhoto implements ContactPhoto {
} }
@Override @Override
public Drawable asDrawable(Context context, int background) { public Drawable asDrawable(Context context, int color) {
return asDrawable(context, color, false);
}
@Override
public Drawable asDrawable(Context context, int color, boolean inverted) {
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()
.beginConfig() .beginConfig()
.width(targetSize) .width(targetSize)
.height(targetSize) .height(targetSize)
.textColor(inverted ? color : Color.WHITE)
.endConfig() .endConfig()
.buildRound(String.valueOf(name.charAt(0)), background); .buildRound(String.valueOf(name.charAt(0)), inverted ? Color.WHITE : color);
} }
} }

View File

@ -1,8 +1,12 @@
package org.thoughtcrime.securesms.contacts.avatars; package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.LayerDrawable;
import android.support.v4.graphics.ColorUtils;
import android.widget.ImageView; import android.widget.ImageView;
import com.amulyakhare.textdrawable.TextDrawable; import com.amulyakhare.textdrawable.TextDrawable;
@ -17,11 +21,21 @@ public class ResourceContactPhoto implements ContactPhoto {
} }
@Override @Override
public Drawable asDrawable(Context context, int backgroundColor) { public Drawable asDrawable(Context context, int color) {
Drawable background = TextDrawable.builder().buildRound(" ", backgroundColor); return asDrawable(context, color, false);
}
@Override
public Drawable asDrawable(Context context, int color, boolean inverted) {
Drawable background = TextDrawable.builder().buildRound(" ", inverted ? Color.WHITE : color);
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);
if (inverted) {
foreground.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
return new ExpandingLayerDrawable(new Drawable[] {background, foreground}); return new ExpandingLayerDrawable(new Drawable[] {background, foreground});
} }

View File

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

View File

@ -21,12 +21,14 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
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.recipients.RecipientProvider.RecipientDetails; import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails;
import org.thoughtcrime.securesms.util.FutureTaskListener; import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.ListenableFutureTask; import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -46,31 +48,28 @@ public class Recipient {
private ContactPhoto contactPhoto; private ContactPhoto contactPhoto;
private Uri contactUri; private Uri contactUri;
private Optional<Integer> color;
Recipient(long recipientId, String number, ListenableFutureTask<RecipientDetails> future) Recipient(long recipientId, String number, ListenableFutureTask<RecipientDetails> future)
{ {
this.recipientId = recipientId; this.recipientId = recipientId;
this.number = number; this.number = number;
this.contactPhoto = ContactPhotoFactory.getLoadingPhoto(); this.contactPhoto = ContactPhotoFactory.getLoadingPhoto();
this.color = Optional.absent();
future.addListener(new FutureTaskListener<RecipientDetails>() { future.addListener(new FutureTaskListener<RecipientDetails>() {
@Override @Override
public void onSuccess(RecipientDetails result) { public void onSuccess(RecipientDetails result) {
if (result != null) { if (result != null) {
Set<RecipientModifiedListener> localListeners;
synchronized (Recipient.this) { synchronized (Recipient.this) {
Recipient.this.name = result.name; Recipient.this.name = result.name;
Recipient.this.number = result.number; Recipient.this.number = result.number;
Recipient.this.contactUri = result.contactUri; Recipient.this.contactUri = result.contactUri;
Recipient.this.contactPhoto = result.avatar; Recipient.this.contactPhoto = result.avatar;
Recipient.this.color = result.color;
localListeners = new HashSet<>(listeners);
listeners.clear();
} }
for (RecipientModifiedListener listener : localListeners) notifyListeners();
listener.onModified(Recipient.this);
} }
} }
@ -87,6 +86,7 @@ public class Recipient {
this.contactUri = details.contactUri; this.contactUri = details.contactUri;
this.name = details.name; this.name = details.name;
this.contactPhoto = details.avatar; this.contactPhoto = details.avatar;
this.color = details.color;
} }
public synchronized Uri getContactUri() { public synchronized Uri getContactUri() {
@ -97,6 +97,20 @@ public class Recipient {
return this.name; return this.name;
} }
public synchronized @NonNull Optional<Integer> getColor() {
if (color.isPresent()) return color;
else if (name != null) return Optional.of(ContactColors.generateFor(name));
else return Optional.of(ContactColors.UNKNOWN_COLOR);
}
public void setColor(Optional<Integer> color) {
synchronized (this) {
this.color = color;
}
notifyListeners();
}
public String getNumber() { public String getNumber() {
return number; return number;
} }
@ -126,7 +140,9 @@ public class Recipient {
} }
public static Recipient getUnknownRecipient() { public static Recipient getUnknownRecipient() {
return new Recipient(-1, new RecipientDetails("Unknown", "Unknown", null, ContactPhotoFactory.getDefaultContactPhoto("Unknown"))); return new Recipient(-1, new RecipientDetails("Unknown", "Unknown", null,
ContactPhotoFactory.getDefaultContactPhoto("Unknown"),
Optional.<Integer>absent()));
} }
@Override @Override
@ -144,6 +160,17 @@ public class Recipient {
return 31 + (int)this.recipientId; return 31 + (int)this.recipientId;
} }
private void notifyListeners() {
Set<RecipientModifiedListener> localListeners;
synchronized (this) {
localListeners = new HashSet<>(listeners);
}
for (RecipientModifiedListener listener : localListeners)
listener.onModified(Recipient.this);
}
public interface RecipientModifiedListener { public interface RecipientModifiedListener {
public void onModified(Recipient recipient); public void onModified(Recipient recipient);
} }

View File

@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.LRUCache; import org.thoughtcrime.securesms.util.LRUCache;
import org.thoughtcrime.securesms.util.ListenableFutureTask; import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -67,9 +68,9 @@ public class RecipientProvider {
String number = CanonicalAddressDatabase.getInstance(context).getAddressFromId(recipientId); String number = CanonicalAddressDatabase.getInstance(context).getAddressFromId(recipientId);
if (asynchronous) { if (asynchronous) {
cachedRecipient = new Recipient(recipientId, number, getRecipientDetailsAsync(context, number)); cachedRecipient = new Recipient(recipientId, number, getRecipientDetailsAsync(context, recipientId, number));
} else { } else {
cachedRecipient = new Recipient(recipientId, getRecipientDetailsSync(context, number)); cachedRecipient = new Recipient(recipientId, getRecipientDetailsSync(context, recipientId, number));
} }
recipientCache.put(recipientId, cachedRecipient); recipientCache.put(recipientId, cachedRecipient);
@ -99,12 +100,13 @@ public class RecipientProvider {
} }
private @NonNull ListenableFutureTask<RecipientDetails> getRecipientDetailsAsync(final Context context, private @NonNull ListenableFutureTask<RecipientDetails> getRecipientDetailsAsync(final Context context,
final long recipientId,
final String number) final String number)
{ {
Callable<RecipientDetails> task = new Callable<RecipientDetails>() { Callable<RecipientDetails> task = new Callable<RecipientDetails>() {
@Override @Override
public RecipientDetails call() throws Exception { public RecipientDetails call() throws Exception {
return getRecipientDetailsSync(context, number); return getRecipientDetailsSync(context, recipientId, number);
} }
}; };
@ -113,12 +115,14 @@ public class RecipientProvider {
return future; return future;
} }
private @NonNull RecipientDetails getRecipientDetailsSync(Context context, String number) { private @NonNull RecipientDetails getRecipientDetailsSync(Context context, long recipientId, String number) {
if (GroupUtil.isEncodedGroup(number)) return getGroupRecipientDetails(context, number); if (GroupUtil.isEncodedGroup(number)) return getGroupRecipientDetails(context, number);
else return getIndividualRecipientDetails(context, number); else return getIndividualRecipientDetails(context, recipientId, number);
} }
private @NonNull RecipientDetails getIndividualRecipientDetails(Context context, String number) { private @NonNull RecipientDetails getIndividualRecipientDetails(Context context, long recipientId, String number) {
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(new long[]{recipientId});
Optional<Integer> color = preferences.isPresent() ? preferences.get().getColor() : Optional.<Integer>absent();
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
Cursor cursor = context.getContentResolver().query(uri, CALLER_ID_PROJECTION, Cursor cursor = context.getContentResolver().query(uri, CALLER_ID_PROJECTION,
null, null, null); null, null, null);
@ -131,14 +135,14 @@ public class RecipientProvider {
Uri.withAppendedPath(Contacts.CONTENT_URI, cursor.getLong(2) + ""), Uri.withAppendedPath(Contacts.CONTENT_URI, cursor.getLong(2) + ""),
name); name);
return new RecipientDetails(cursor.getString(0), cursor.getString(3), contactUri, contactPhoto); return new RecipientDetails(cursor.getString(0), cursor.getString(3), contactUri, contactPhoto, color);
} }
} finally { } finally {
if (cursor != null) if (cursor != null)
cursor.close(); cursor.close();
} }
return new RecipientDetails(null, number, null, ContactPhotoFactory.getDefaultContactPhoto(null)); return new RecipientDetails(null, number, null, ContactPhotoFactory.getDefaultContactPhoto(null), color);
} }
private @NonNull RecipientDetails getGroupRecipientDetails(Context context, String groupId) { private @NonNull RecipientDetails getGroupRecipientDetails(Context context, String groupId) {
@ -148,13 +152,13 @@ public class RecipientProvider {
if (record != null) { if (record != null) {
ContactPhoto contactPhoto = ContactPhotoFactory.getGroupContactPhoto(record.getAvatar()); ContactPhoto contactPhoto = ContactPhotoFactory.getGroupContactPhoto(record.getAvatar());
return new RecipientDetails(record.getTitle(), groupId, null, contactPhoto); return new RecipientDetails(record.getTitle(), groupId, null, contactPhoto, Optional.<Integer>absent());
} }
return new RecipientDetails(null, groupId, null, ContactPhotoFactory.getDefaultGroupPhoto()); return new RecipientDetails(null, groupId, null, ContactPhotoFactory.getDefaultGroupPhoto(), Optional.<Integer>absent());
} catch (IOException e) { } catch (IOException e) {
Log.w("RecipientProvider", e); Log.w("RecipientProvider", e);
return new RecipientDetails(null, groupId, null, ContactPhotoFactory.getDefaultGroupPhoto()); return new RecipientDetails(null, groupId, null, ContactPhotoFactory.getDefaultGroupPhoto(), Optional.<Integer>absent());
} }
} }
@ -182,14 +186,17 @@ public class RecipientProvider {
@NonNull public final String number; @NonNull public final String number;
@NonNull public final ContactPhoto avatar; @NonNull public final ContactPhoto avatar;
@Nullable public final Uri contactUri; @Nullable public final Uri contactUri;
@NonNull public final Optional<Integer> color;
public RecipientDetails(@Nullable String name, @NonNull String number, public RecipientDetails(@Nullable String name, @NonNull String number,
@Nullable Uri contactUri, @NonNull ContactPhoto avatar) @Nullable Uri contactUri, @NonNull ContactPhoto avatar,
@NonNull Optional<Integer> color)
{ {
this.name = name; this.name = name;
this.number = number; this.number = number;
this.avatar = avatar; this.avatar = avatar;
this.contactUri = contactUri; this.contactUri = contactUri;
this.color = color;
} }
} }

View File

@ -22,7 +22,6 @@ 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;
@ -55,7 +54,6 @@ 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);
@ -69,7 +67,6 @@ 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();
} }
} }
@ -88,7 +85,6 @@ 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);
} }
@ -106,21 +102,6 @@ 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;
} }
@ -169,12 +150,22 @@ public class Recipients implements Iterable<Recipient>, RecipientModifiedListene
notifyListeners(); notifyListeners();
} }
public @NonNull public @NonNull ContactPhoto getContactPhoto() {
ContactPhoto getContactPhoto() {
if (recipients.size() == 1) return recipients.get(0).getContactPhoto(); if (recipients.size() == 1) return recipients.get(0).getContactPhoto();
else return ContactPhotoFactory.getDefaultGroupPhoto(); else return ContactPhotoFactory.getDefaultGroupPhoto();
} }
public synchronized @NonNull Optional<Integer> getColor() {
if (!isSingleRecipient() || isGroupRecipient()) return Optional.absent();
else if (isEmpty()) return Optional.absent();
else return recipients.get(0).getColor();
}
public synchronized void setColor(Optional<Integer> color) {
if (!isSingleRecipient() || isGroupRecipient()) throw new AssertionError("Groups don't have colors!");
else if (!isEmpty()) recipients.get(0).setColor(color);
}
public synchronized void addListener(RecipientsModifiedListener listener) { public synchronized void addListener(RecipientsModifiedListener listener) {
if (listeners.isEmpty()) { if (listeners.isEmpty()) {
for (Recipient recipient : recipients) { for (Recipient recipient : recipients) {