diff --git a/app/src/main/java/eu/faircode/email/ContactInfo.java b/app/src/main/java/eu/faircode/email/ContactInfo.java
index 2d138db05f..204bd3cecf 100644
--- a/app/src/main/java/eu/faircode/email/ContactInfo.java
+++ b/app/src/main/java/eu/faircode/email/ContactInfo.java
@@ -27,13 +27,6 @@ import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -168,54 +161,20 @@ public class ContactInfo {
boolean identicon = false;
if (info.bitmap == null) {
int dp = Helper.dp2pixels(context, 96);
- boolean dark = Helper.isDarkTheme(context);
boolean generated = prefs.getBoolean("generated_icons", true);
if (generated) {
boolean identicons = prefs.getBoolean("identicons", false);
if (identicons) {
identicon = true;
- info.bitmap = ImageHelper.generateIdenticon(key, dp, 5, dark);
+ info.bitmap = ImageHelper.generateIdenticon(key, dp, 5, context);
} else
- info.bitmap = ImageHelper.generateLetterIcon(key, dp, dark);
+ info.bitmap = ImageHelper.generateLetterIcon(key, dp, context);
}
}
boolean circular = prefs.getBoolean("circular", true);
- if (info.bitmap != null) {
- int w = info.bitmap.getWidth();
- int h = info.bitmap.getHeight();
-
- Rect source;
- if (w > h) {
- int off = (w - h) / 2;
- source = new Rect(off, 0, w - off, h);
- } else if (w < h) {
- int off = (h - w) / 2;
- source = new Rect(0, off, w, h - off);
- } else
- source = new Rect(0, 0, w, h);
-
- Rect dest = new Rect(0, 0, source.width(), source.height());
-
- Bitmap round = Bitmap.createBitmap(source.width(), source.height(), Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(round);
-
- Paint paint = new Paint();
- paint.setAntiAlias(true);
- canvas.drawARGB(0, 0, 0, 0);
- paint.setColor(Color.GRAY);
- if (circular && !identicon)
- canvas.drawOval(new RectF(dest), paint);
- else {
- float radius = Helper.dp2pixels(context, 3);
- canvas.drawRoundRect(new RectF(dest), radius, radius, paint);
- }
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(info.bitmap, source, dest, paint);
-
- info.bitmap.recycle();
- info.bitmap = round;
- }
+ info.bitmap = ImageHelper.makeCircular(info.bitmap,
+ circular && !identicon ? null : Helper.dp2pixels(context, 3));
if (info.displayName == null)
info.displayName = address.getPersonal();
diff --git a/app/src/main/java/eu/faircode/email/FragmentOptions.java b/app/src/main/java/eu/faircode/email/FragmentOptions.java
index 025a8717e1..d9a2808eed 100644
--- a/app/src/main/java/eu/faircode/email/FragmentOptions.java
+++ b/app/src/main/java/eu/faircode/email/FragmentOptions.java
@@ -40,7 +40,7 @@ public class FragmentOptions extends FragmentBase {
static String[] OPTIONS_RESTART = new String[]{
"subscriptions",
"startup", "cards", "date", "threading", "indentation", "highlight_unread",
- "avatars", "generated_icons", "identicons", "circular",
+ "avatars", "generated_icons", "identicons", "circular", "saturation", "brightness",
"name_email", "distinguish_contacts", "authentication",
"subject_top", "subject_italic", "subject_ellipsize",
"flags", "flags_background", "preview", "preview_italic",
diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java b/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java
index eac0b11ff8..612dcba758 100644
--- a/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java
+++ b/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java
@@ -20,7 +20,9 @@ package eu.faircode.email;
*/
import android.app.Dialog;
+import android.content.Context;
import android.content.SharedPreferences;
+import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -31,7 +33,9 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CompoundButton;
+import android.widget.ImageView;
import android.widget.RadioGroup;
+import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.Toast;
@@ -54,6 +58,11 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
private SwitchCompat swGeneratedIcons;
private SwitchCompat swIdenticons;
private SwitchCompat swCircular;
+ private ImageView ivRed;
+ private ImageView ivGreen;
+ private ImageView ivBlue;
+ private SeekBar sbSaturation;
+ private SeekBar sbBrightness;
private SwitchCompat swNameEmail;
private SwitchCompat swDistinguishContacts;
private SwitchCompat swAuthentication;
@@ -77,7 +86,8 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
private final static String[] RESET_OPTIONS = new String[]{
"theme", "startup", "cards", "date", "threading", "indentation", "highlight_unread",
- "avatars", "generated_icons", "identicons", "circular", "name_email", "distinguish_contacts", "authentication",
+ "avatars", "generated_icons", "identicons", "circular", "saturation", "brightness",
+ "name_email", "distinguish_contacts", "authentication",
"subject_top", "subject_italic", "subject_ellipsize",
"flags", "flags_background", "preview", "preview_italic", "addresses", "attachments_alt",
"contrast", "monospaced", "text_color",
@@ -105,6 +115,11 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
swGeneratedIcons = view.findViewById(R.id.swGeneratedIcons);
swIdenticons = view.findViewById(R.id.swIdenticons);
swCircular = view.findViewById(R.id.swCircular);
+ ivRed = view.findViewById(R.id.ivRed);
+ ivGreen = view.findViewById(R.id.ivGreen);
+ ivBlue = view.findViewById(R.id.ivBlue);
+ sbSaturation = view.findViewById(R.id.sbSaturation);
+ sbBrightness = view.findViewById(R.id.sbBrightness);
swNameEmail = view.findViewById(R.id.swNameEmail);
swDistinguishContacts = view.findViewById(R.id.swDistinguishContacts);
swAuthentication = view.findViewById(R.id.swAuthentication);
@@ -216,10 +231,49 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("circular", checked).apply();
+ updateColor();
ContactInfo.clearCache();
}
});
+ sbSaturation.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ prefs.edit().putInt("saturation", progress).apply();
+ updateColor();
+ ContactInfo.clearCache();
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ // Do nothing
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ // Do nothing
+ }
+ });
+
+ sbBrightness.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ prefs.edit().putInt("brightness", progress).apply();
+ updateColor();
+ ContactInfo.clearCache();
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ // Do nothing
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ // Do nothing
+ }
+ });
+
swNameEmail.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@@ -426,6 +480,8 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
swIdenticons.setChecked(prefs.getBoolean("identicons", false));
swIdenticons.setEnabled(swGeneratedIcons.isChecked());
swCircular.setChecked(prefs.getBoolean("circular", true));
+ sbSaturation.setProgress(prefs.getInt("saturation", 100));
+ sbBrightness.setProgress(prefs.getInt("brightness", 100));
swNameEmail.setChecked(prefs.getBoolean("name_email", false));
swDistinguishContacts.setChecked(prefs.getBoolean("distinguish_contacts", false));
swAuthentication.setChecked(prefs.getBoolean("authentication", true));
@@ -454,6 +510,29 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer
swImagesInline.setChecked(prefs.getBoolean("inline_images", false));
swSeekbar.setChecked(prefs.getBoolean("seekbar", false));
swActionbar.setChecked(prefs.getBoolean("actionbar", true));
+
+ updateColor();
+ }
+
+ private void updateColor() {
+ Context context = getContext();
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ boolean circular = prefs.getBoolean("circular", true);
+
+ int size = Helper.dp2pixels(context, 36);
+ Integer radius = (circular ? null : Helper.dp2pixels(context, 3));
+
+ Bitmap red = ImageHelper.generateLetterIcon(0f, "A", size, context);
+ Bitmap green = ImageHelper.generateLetterIcon(120f, "B", size, context);
+ Bitmap blue = ImageHelper.generateLetterIcon(240f, "C", size, context);
+
+ red = ImageHelper.makeCircular(red, radius);
+ green = ImageHelper.makeCircular(green, radius);
+ blue = ImageHelper.makeCircular(blue, radius);
+
+ ivRed.setImageBitmap(red);
+ ivGreen.setImageBitmap(green);
+ ivBlue.setImageBitmap(blue);
}
public static class FragmentDialogTheme extends FragmentDialogBase {
diff --git a/app/src/main/java/eu/faircode/email/ImageHelper.java b/app/src/main/java/eu/faircode/email/ImageHelper.java
index 96ea86e33e..37ddaedc65 100644
--- a/app/src/main/java/eu/faircode/email/ImageHelper.java
+++ b/app/src/main/java/eu/faircode/email/ImageHelper.java
@@ -28,6 +28,8 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageDecoder;
import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
@@ -64,10 +66,17 @@ class ImageHelper {
private static final ExecutorService executor =
Helper.getBackgroundExecutor(1, "image");
- static Bitmap generateIdenticon(@NonNull String email, int size, int pixels, boolean dark) {
+ static Bitmap generateIdenticon(@NonNull String email, int size, int pixels, Context context) {
byte[] hash = getHash(email);
- int color = Color.HSVToColor(127, new float[]{Math.abs(email.hashCode()) % 360, 1, 1});
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ int saturation = prefs.getInt("saturation", 100);
+ int brightness = prefs.getInt("brightness", 100);
+
+ int color = Color.HSVToColor(new float[]{
+ Math.abs(email.hashCode()) % 360,
+ saturation / 100f,
+ brightness / 100f});
Paint paint = new Paint();
paint.setColor(color);
@@ -91,34 +100,43 @@ class ImageHelper {
return bitmap;
}
- static Bitmap generateLetterIcon(@NonNull String email, int size, boolean dark) {
- String text = null;
+ static Bitmap generateLetterIcon(@NonNull String email, int size, Context context) {
+ String letter = null;
for (int i = 0; i < email.length(); i++) {
char kar = email.charAt(i);
if (Character.isAlphabetic(kar)) {
- text = email.substring(i, i + 1).toUpperCase();
+ letter = email.substring(i, i + 1).toUpperCase();
break;
}
}
- if (text == null)
+ if (letter == null)
return null;
- int color = Color.HSVToColor(127, new float[]{Math.abs(email.hashCode()) % 360, 1, 1});
+ float h = Math.abs(email.hashCode()) % 360f;
+ return generateLetterIcon(h, letter, size, context);
+ }
+
+ static Bitmap generateLetterIcon(float h, String letter, int size, Context context) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ float s = prefs.getInt("saturation", 100) / 100f;
+ float v = prefs.getInt("brightness", 100) / 100f;
+
+ int bg = Color.HSVToColor(new float[]{h, s, v});
+
+ double lum = ColorUtils.calculateLuminance(bg);
+ int fg = Color.HSVToColor(new float[]{0, 0, lum < 0.5 ? v : 0});
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
- canvas.drawColor(color);
-
- double lum = ColorUtils.calculateLuminance(color);
+ canvas.drawColor(bg);
Paint paint = new Paint();
- paint.setColor(lum < 0.5 ? Color.WHITE : Color.BLACK);
+ paint.setColor(fg);
paint.setTextSize(size / 2f);
paint.setTypeface(Typeface.DEFAULT_BOLD);
- canvas.drawText(
- text,
- size / 2f - paint.measureText(text) / 2,
+ canvas.drawText(letter,
+ size / 2f - paint.measureText(letter) / 2,
size / 2f - (paint.descent() + paint.ascent()) / 2, paint);
return bitmap;
@@ -132,6 +150,43 @@ class ImageHelper {
}
}
+ static Bitmap makeCircular(Bitmap bitmap, Integer radius) {
+ if (bitmap == null)
+ return null;
+
+ int w = bitmap.getWidth();
+ int h = bitmap.getHeight();
+
+ Rect source;
+ if (w > h) {
+ int off = (w - h) / 2;
+ source = new Rect(off, 0, w - off, h);
+ } else if (w < h) {
+ int off = (h - w) / 2;
+ source = new Rect(0, off, w, h - off);
+ } else
+ source = new Rect(0, 0, w, h);
+
+ Rect dest = new Rect(0, 0, source.width(), source.height());
+
+ Bitmap round = Bitmap.createBitmap(source.width(), source.height(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(round);
+
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ canvas.drawARGB(0, 0, 0, 0);
+ paint.setColor(Color.GRAY);
+ if (radius == null)
+ canvas.drawOval(new RectF(dest), paint); // round
+ else
+ canvas.drawRoundRect(new RectF(dest), radius, radius, paint); // rounded
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ canvas.drawBitmap(bitmap, source, dest, paint);
+
+ bitmap.recycle();
+ return round;
+ }
+
static Drawable decodeImage(final Context context, final long id, String source, boolean show, final TextView view) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean compact = prefs.getBoolean("compact", false);
diff --git a/app/src/main/res/layout/fragment_options_display.xml b/app/src/main/res/layout/fragment_options_display.xml
index 8d334d8df1..d267e8f5e0 100644
--- a/app/src/main/res/layout/fragment_options_display.xml
+++ b/app/src/main/res/layout/fragment_options_display.xml
@@ -187,6 +187,81 @@
app:layout_constraintTop_toBottomOf="@id/swIdenticons"
app:switchPadding="12dp" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Show generated icons
Show identicons
Show round icons
+ Saturation
+ Brightness
Show names and email addresses
Show a warning when the receiving server could not authenticate the message
Show subject above sender