User-selectable contact colors.

// FREEBIE
This commit is contained in:
Moxie Marlinspike
2015-06-24 13:17:58 -07:00
parent fb9f16ad29
commit 296796eb54
11 changed files with 423 additions and 4 deletions

View File

@@ -43,7 +43,6 @@ import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener;
import android.view.ViewStub;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
@@ -53,7 +52,6 @@ import android.widget.Toast;
import com.afollestad.materialdialogs.AlertDialogWrapper;
import com.google.protobuf.ByteString;
import com.readystatesoftware.systembartint.SystemBarTintManager;
import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
import org.thoughtcrime.securesms.components.AnimatingToggle;
@@ -806,7 +804,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void onModified(final Recipients recipients) {
Log.w(TAG, "onModified()");
titleView.post(new Runnable() {
@Override
public void run() {

View File

@@ -18,6 +18,7 @@ import android.support.v4.app.Fragment;
import android.support.v4.preference.PreferenceFragment;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
@@ -28,11 +29,13 @@ import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState;
import org.thoughtcrime.securesms.preferences.ColorPreference;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.whispersystems.libaxolotl.util.guava.Optional;
public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActivity implements Recipients.RecipientsModifiedListener
{
@@ -44,6 +47,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
private static final String PREFERENCE_TONE = "pref_key_recipient_ringtone";
private static final String PREFERENCE_VIBRATE = "pref_key_recipient_vibrate";
private static final String PREFERENCE_BLOCK = "pref_key_recipient_block";
private static final String PREFERENCE_COLOR = "pref_key_recipient_color";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@@ -156,6 +160,8 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
.setOnPreferenceClickListener(new MuteClickedListener());
this.findPreference(PREFERENCE_BLOCK)
.setOnPreferenceClickListener(new BlockClickedListener());
this.findPreference(PREFERENCE_COLOR)
.setOnPreferenceChangeListener(new ColorChangeListener());
}
@Override
@@ -174,6 +180,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
CheckBoxPreference mutePreference = (CheckBoxPreference) this.findPreference(PREFERENCE_MUTED);
RingtonePreference ringtonePreference = (RingtonePreference) this.findPreference(PREFERENCE_TONE);
ListPreference vibratePreference = (ListPreference) this.findPreference(PREFERENCE_VIBRATE);
ColorPreference colorPreference = (ColorPreference) this.findPreference(PREFERENCE_COLOR);
Preference blockPreference = this.findPreference(PREFERENCE_BLOCK);
mutePreference.setChecked(recipients.isMuted());
@@ -199,6 +206,14 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
vibratePreference.setValueIndex(2);
}
if (recipients.getColor().isPresent()) {
colorPreference.setValue(recipients.getColor().get());
colorPreference.setEnabled(true);
} else {
colorPreference.setValue(getResources().getColor(R.color.textsecure_primary));
colorPreference.setEnabled(false);
}
if (!recipients.isSingleRecipient() || recipients.isGroupRecipient()) {
blockPreference.setEnabled(false);
} else {
@@ -267,6 +282,28 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
}
}
private class ColorChangeListener implements Preference.OnPreferenceChangeListener {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final int value = (Integer)newValue;
if (value != recipients.getColor().get()) {
recipients.setColor(Optional.of(value));
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientPreferenceDatabase(getActivity())
.setColor(recipients, value);
return null;
}
}.execute();
}
return true;
}
}
private class MuteClickedListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {

View File

@@ -0,0 +1,292 @@
package org.thoughtcrime.securesms.preferences;
/*
* Copyright 2013 Google Inc.
*
* 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.
*/
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
import android.preference.Preference;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayout;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.thoughtcrime.securesms.R;
/**
* A preference that allows the user to choose an application or shortcut.
*/
public class ColorPreference extends Preference {
private int[] mColorChoices = {};
private int mValue = 0;
private int mItemLayoutId = R.layout.color_preference_item;
private int mNumColumns = 5;
private View mPreviewView;
public ColorPreference(Context context) {
super(context);
initAttrs(null, 0);
}
public ColorPreference(Context context, AttributeSet attrs) {
super(context, attrs);
initAttrs(attrs, 0);
}
public ColorPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initAttrs(attrs, defStyle);
}
private void initAttrs(AttributeSet attrs, int defStyle) {
TypedArray a = getContext().getTheme().obtainStyledAttributes(
attrs, R.styleable.ColorPreference, defStyle, defStyle);
try {
mItemLayoutId = a.getResourceId(R.styleable.ColorPreference_itemLayout, mItemLayoutId);
mNumColumns = a.getInteger(R.styleable.ColorPreference_numColumns, mNumColumns);
int choicesResId = a.getResourceId(R.styleable.ColorPreference_choices,
R.array.default_color_choice_values);
if (choicesResId > 0) {
String[] choices = a.getResources().getStringArray(choicesResId);
mColorChoices = new int[choices.length];
for (int i = 0; i < choices.length; i++) {
mColorChoices[i] = Color.parseColor(choices[i]);
}
}
} finally {
a.recycle();
}
setWidgetLayoutResource(mItemLayoutId);
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
mPreviewView = view.findViewById(R.id.color_view);
setColorViewValue(mPreviewView, mValue, false);
}
public void setValue(int value) {
if (callChangeListener(value)) {
mValue = value;
persistInt(value);
notifyChanged();
}
}
@Override
protected void onClick() {
super.onClick();
ColorDialogFragment fragment = ColorDialogFragment.newInstance();
fragment.setPreference(this);
((AppCompatActivity) getContext()).getSupportFragmentManager().beginTransaction()
.add(fragment, getFragmentTag())
.commit();
}
@Override
protected void onAttachedToActivity() {
super.onAttachedToActivity();
AppCompatActivity activity = (AppCompatActivity) getContext();
ColorDialogFragment fragment = (ColorDialogFragment) activity
.getSupportFragmentManager().findFragmentByTag(getFragmentTag());
if (fragment != null) {
// re-bind preference to fragment
fragment.setPreference(this);
}
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInt(index, 0);
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setValue(restoreValue ? getPersistedInt(0) : (Integer) defaultValue);
}
public String getFragmentTag() {
return "color_" + getKey();
}
public int getValue() {
return mValue;
}
public static class ColorDialogFragment extends android.support.v4.app.DialogFragment {
private ColorPreference mPreference;
private GridLayout mColorGrid;
public ColorDialogFragment() {
}
public static ColorDialogFragment newInstance() {
return new ColorDialogFragment();
}
public void setPreference(ColorPreference preference) {
mPreference = preference;
repopulateItems();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
repopulateItems();
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
View rootView = layoutInflater.inflate(R.layout.color_preference_items, null);
mColorGrid = (GridLayout) rootView.findViewById(R.id.color_grid);
mColorGrid.setColumnCount(mPreference.mNumColumns);
repopulateItems();
return new AlertDialog.Builder(getActivity())
.setView(rootView)
.create();
}
private void repopulateItems() {
if (mPreference == null || mColorGrid == null) {
return;
}
Context context = mColorGrid.getContext();
mColorGrid.removeAllViews();
for (final int color : mPreference.mColorChoices) {
View itemView = LayoutInflater.from(context)
.inflate(R.layout.color_preference_item, mColorGrid, false);
setColorViewValue(itemView.findViewById(R.id.color_view), color,
color == mPreference.getValue());
itemView.setClickable(true);
itemView.setFocusable(true);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPreference.setValue(color);
dismiss();
}
});
mColorGrid.addView(itemView);
}
sizeDialog();
}
@Override
public void onStart() {
super.onStart();
sizeDialog();
}
private void sizeDialog() {
if (mPreference == null || mColorGrid == null) {
return;
}
Dialog dialog = getDialog();
if (dialog == null) {
return;
}
final Resources res = mColorGrid.getContext().getResources();
DisplayMetrics dm = res.getDisplayMetrics();
// Can't use Integer.MAX_VALUE here (weird issue observed otherwise on 4.2)
mColorGrid.measure(
View.MeasureSpec.makeMeasureSpec(dm.widthPixels, View.MeasureSpec.AT_MOST),
View.MeasureSpec.makeMeasureSpec(dm.heightPixels, View.MeasureSpec.AT_MOST));
int width = mColorGrid.getMeasuredWidth();
int height = mColorGrid.getMeasuredHeight();
int extraPadding = res.getDimensionPixelSize(R.dimen.color_grid_extra_padding);
width += extraPadding;
height += extraPadding;
dialog.getWindow().setLayout(width, height);
}
}
private static void setColorViewValue(View view, int color, boolean selected) {
if (view instanceof ImageView) {
ImageView imageView = (ImageView) view;
Resources res = imageView.getContext().getResources();
Drawable currentDrawable = imageView.getDrawable();
GradientDrawable colorChoiceDrawable;
if (currentDrawable instanceof GradientDrawable) {
// Reuse drawable
colorChoiceDrawable = (GradientDrawable) currentDrawable;
} else {
colorChoiceDrawable = new GradientDrawable();
colorChoiceDrawable.setShape(GradientDrawable.OVAL);
}
// Set stroke to dark version of color
// int darkenedColor = Color.rgb(
// Color.red(color) * 192 / 256,
// Color.green(color) * 192 / 256,
// Color.blue(color) * 192 / 256);
colorChoiceDrawable.setColor(color);
// colorChoiceDrawable.setStroke((int) TypedValue.applyDimension(
// TypedValue.COMPLEX_UNIT_DIP, 2, res.getDisplayMetrics()), darkenedColor);
Drawable drawable = colorChoiceDrawable;
if (selected) {
BitmapDrawable checkmark = (BitmapDrawable) res.getDrawable(R.drawable.check);
checkmark.setGravity(Gravity.CENTER);
drawable = new LayerDrawable(new Drawable[]{
colorChoiceDrawable,
checkmark});
}
imageView.setImageDrawable(drawable);
} else if (view instanceof TextView) {
((TextView) view).setTextColor(color);
}
}
}