Added account images

pull/215/head
M66B 6 months ago
parent ea882f27f3
commit e6eb6e75a5

File diff suppressed because it is too large Load Diff

@ -29,9 +29,11 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
@ -70,6 +72,8 @@ import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListUpdateCallback; import androidx.recyclerview.widget.ListUpdateCallback;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.util.ArrayList; import java.util.ArrayList;
@ -94,6 +98,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
private boolean show_unexposed; private boolean show_unexposed;
private boolean debug; private boolean debug;
private boolean hasAvatars = false;
private List<TupleAccountFolder> all = new ArrayList<>(); private List<TupleAccountFolder> all = new ArrayList<>();
private List<TupleAccountFolder> items = new ArrayList<>(); private List<TupleAccountFolder> items = new ArrayList<>();
@ -103,6 +108,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
private View view; private View view;
private View vwColor; private View vwColor;
private ImageView ivAvatar;
private ImageView ivOAuth; private ImageView ivOAuth;
private ImageView ivPrimary; private ImageView ivPrimary;
private ImageView ivNotify; private ImageView ivNotify;
@ -135,6 +141,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
view = itemView.findViewById(R.id.clItem); view = itemView.findViewById(R.id.clItem);
vwColor = itemView.findViewById(R.id.vwColor); vwColor = itemView.findViewById(R.id.vwColor);
ivAvatar = itemView.findViewById(R.id.ivAvatar);
ivSync = itemView.findViewById(R.id.ivSync); ivSync = itemView.findViewById(R.id.ivSync);
ibInbox = itemView.findViewById(R.id.ibInbox); ibInbox = itemView.findViewById(R.id.ibInbox);
ivOAuth = itemView.findViewById(R.id.ivOAuth); ivOAuth = itemView.findViewById(R.id.ivOAuth);
@ -210,6 +217,43 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
vwColor.setBackgroundColor(account.color == null ? Color.TRANSPARENT : account.color); vwColor.setBackgroundColor(account.color == null ? Color.TRANSPARENT : account.color);
vwColor.setVisibility(ActivityBilling.isPro(context) ? View.VISIBLE : View.INVISIBLE); vwColor.setVisibility(ActivityBilling.isPro(context) ? View.VISIBLE : View.INVISIBLE);
if (account.avatar == null)
ivAvatar.setVisibility(hasAvatars ? View.INVISIBLE : View.GONE);
else {
Bundle args = new Bundle();
args.putString("avatar", account.avatar);
new SimpleTask<Bitmap>() {
@Override
protected Bitmap onExecute(Context context, Bundle args) throws Throwable {
String avatar = args.getString("avatar");
Uri uri = Uri.parse(avatar);
Bitmap bm;
int scaleToPixels = Helper.dp2pixels(context, ContactInfo.AVATAR_SIZE);
try (InputStream is = context.getContentResolver().openInputStream(uri)) {
if (is == null)
throw new FileNotFoundException(uri.toString());
bm = ImageHelper.getScaledBitmap(is, avatar, null, scaleToPixels);
if (bm == null)
throw new FileNotFoundException(avatar);
return bm;
}
}
@Override
protected void onExecuted(Bundle args, Bitmap bitmap) {
ivAvatar.setImageBitmap(bitmap);
ivAvatar.setVisibility(View.VISIBLE);
}
@Override
protected void onException(Bundle args, Throwable ex) {
Log.w(ex);
ivAvatar.setVisibility(hasAvatars ? View.INVISIBLE : View.GONE);
}
}.execute(context, owner, args, "account:avatar");
}
ivSync.setImageResource(account.synchronize ? R.drawable.twotone_sync_24 : R.drawable.twotone_sync_disabled_24); ivSync.setImageResource(account.synchronize ? R.drawable.twotone_sync_24 : R.drawable.twotone_sync_disabled_24);
ivSync.setContentDescription(context.getString(account.synchronize ? R.string.title_legend_synchronize_on : R.string.title_legend_synchronize_off)); ivSync.setContentDescription(context.getString(account.synchronize ? R.string.title_legend_synchronize_on : R.string.title_legend_synchronize_off));
ivSync.setVisibility(View.VISIBLE); ivSync.setVisibility(View.VISIBLE);
@ -340,6 +384,8 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
vwColor.setBackgroundColor(account.folderColor == null ? Color.TRANSPARENT : account.folderColor); vwColor.setBackgroundColor(account.folderColor == null ? Color.TRANSPARENT : account.folderColor);
vwColor.setVisibility(ActivityBilling.isPro(context) ? View.VISIBLE : View.INVISIBLE); vwColor.setVisibility(ActivityBilling.isPro(context) ? View.VISIBLE : View.INVISIBLE);
ivAvatar.setVisibility(View.GONE);
ivSync.setVisibility(View.GONE); ivSync.setVisibility(View.GONE);
ivOAuth.setVisibility(View.GONE); ivOAuth.setVisibility(View.GONE);
@ -959,6 +1005,13 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new DiffCallback(items, filtered), false); DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new DiffCallback(items, filtered), false);
hasAvatars = false;
for (TupleAccountFolder account : accounts)
if (account.avatar != null) {
hasAvatars = true;
break;
}
items = filtered; items = filtered;
diff.dispatchUpdatesTo(new ListUpdateCallback() { diff.dispatchUpdatesTo(new ListUpdateCallback() {

@ -111,6 +111,8 @@ public class ContactInfo {
private static final long CACHE_FAVICON_DURATION = 2 * 7 * 24 * 60 * 60 * 1000L; // milliseconds private static final long CACHE_FAVICON_DURATION = 2 * 7 * 24 * 60 * 60 * 1000L; // milliseconds
private static final float MIN_FAVICON_LUMINANCE = 0.2f; private static final float MIN_FAVICON_LUMINANCE = 0.2f;
static final int AVATAR_SIZE = FAVICON_ICON_SIZE;
// https://realfavicongenerator.net/faq // https://realfavicongenerator.net/faq
private static final String[] FIXED_FAVICONS = new String[]{ private static final String[] FIXED_FAVICONS = new String[]{
"apple-touch-icon.png", // 57x57 "apple-touch-icon.png", // 57x57

@ -70,7 +70,7 @@ import javax.mail.internet.InternetAddress;
// https://developer.android.com/topic/libraries/architecture/room.html // https://developer.android.com/topic/libraries/architecture/room.html
@Database( @Database(
version = 296, version = 297,
entities = { entities = {
EntityIdentity.class, EntityIdentity.class,
EntityAccount.class, EntityAccount.class,
@ -3019,6 +3019,12 @@ public abstract class DB extends RoomDatabase {
logMigration(startVersion, endVersion); logMigration(startVersion, endVersion);
db.execSQL("UPDATE `identity` SET `use_ip` = 0 WHERE host = 'sslout.df.eu'"); db.execSQL("UPDATE `identity` SET `use_ip` = 0 WHERE host = 'sslout.df.eu'");
} }
}).addMigrations(new Migration(296, 297) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
logMigration(startVersion, endVersion);
db.execSQL("UPDATE `account` SET `prefix` = NULL");
}
}).addMigrations(new Migration(998, 999) { }).addMigrations(new Migration(998, 999) {
@Override @Override
public void migrate(@NonNull SupportSQLiteDatabase db) { public void migrate(@NonNull SupportSQLiteDatabase db) {

@ -103,6 +103,8 @@ public class EntityAccount extends EntityOrder implements Serializable {
public String category; public String category;
public String signature; // obsolete public String signature; // obsolete
public Integer color; public Integer color;
@ColumnInfo(name = "prefix")
public String avatar;
public String calendar; public String calendar;
@NonNull @NonNull
@ -155,7 +157,6 @@ public class EntityAccount extends EntityOrder implements Serializable {
public Boolean use_date = false; // Date header public Boolean use_date = false; // Date header
@NonNull @NonNull
public Boolean use_received = false; // Received header public Boolean use_received = false; // Received header
public String prefix; // namespace, obsolete
@NonNull @NonNull
public Boolean unicode = false; public Boolean unicode = false;
@ -482,6 +483,7 @@ public class EntityAccount extends EntityOrder implements Serializable {
Objects.equals(a1.category, other.category) && Objects.equals(a1.category, other.category) &&
// signature // signature
Objects.equals(a1.color, other.color) && Objects.equals(a1.color, other.color) &&
Objects.equals(a1.avatar, other.avatar) &&
Objects.equals(a1.calendar, other.calendar) && Objects.equals(a1.calendar, other.calendar) &&
a1.synchronize.equals(other.synchronize) && a1.synchronize.equals(other.synchronize) &&
Objects.equals(a1.ondemand, other.ondemand) && Objects.equals(a1.ondemand, other.ondemand) &&
@ -509,7 +511,6 @@ public class EntityAccount extends EntityOrder implements Serializable {
a1.ignore_size == other.ignore_size && a1.ignore_size == other.ignore_size &&
a1.use_date == other.use_date && a1.use_date == other.use_date &&
a1.use_received == other.use_received && a1.use_received == other.use_received &&
// prefix
a1.unicode == other.unicode && a1.unicode == other.unicode &&
Objects.equals(a1.conditions, other.conditions) && Objects.equals(a1.conditions, other.conditions) &&
(!state || Objects.equals(a1.quota_usage, other.quota_usage)) && (!state || Objects.equals(a1.quota_usage, other.quota_usage)) &&

@ -115,6 +115,9 @@ public class FragmentAccount extends FragmentBase {
private ViewButtonColor btnColor; private ViewButtonColor btnColor;
private TextView tvColorPro; private TextView tvColorPro;
private Button btnAvatar;
private TextView tvAvatarPro;
private Button btnCalendar; private Button btnCalendar;
private TextView tvCalendarPro; private TextView tvCalendarPro;
@ -179,14 +182,16 @@ public class FragmentAccount extends FragmentBase {
private long copy = -1; private long copy = -1;
private int auth = AUTH_TYPE_PASSWORD; private int auth = AUTH_TYPE_PASSWORD;
private String provider = null; private String provider = null;
private String avatar = null;
private String calendar = null; private String calendar = null;
private String certificate = null; private String certificate = null;
private boolean saving = false; private boolean saving = false;
private static final int REQUEST_COLOR = 1; private static final int REQUEST_COLOR = 1;
private static final int REQUEST_CALENDAR = 2; private static final int REQUEST_AVATAR = 2;
private static final int REQUEST_SAVE = 3; private static final int REQUEST_CALENDAR = 3;
private static final int REQUEST_DELETE = 4; private static final int REQUEST_SAVE = 4;
private static final int REQUEST_DELETE = 5;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -237,6 +242,9 @@ public class FragmentAccount extends FragmentBase {
btnColor = view.findViewById(R.id.btnColor); btnColor = view.findViewById(R.id.btnColor);
tvColorPro = view.findViewById(R.id.tvColorPro); tvColorPro = view.findViewById(R.id.tvColorPro);
btnAvatar = view.findViewById(R.id.btnAvatar);
tvAvatarPro = view.findViewById(R.id.tvAvatarPro);
btnCalendar = view.findViewById(R.id.btnCalendar); btnCalendar = view.findViewById(R.id.btnCalendar);
tvCalendarPro = view.findViewById(R.id.tvCalendarPro); tvCalendarPro = view.findViewById(R.id.tvCalendarPro);
@ -462,6 +470,21 @@ public class FragmentAccount extends FragmentBase {
Helper.linkPro(tvColorPro); Helper.linkPro(tvColorPro);
btnAvatar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("image/*");
Helper.openAdvanced(v.getContext(), intent);
startActivityForResult(intent, REQUEST_AVATAR);
}
});
Helper.linkPro(tvAvatarPro);
btnCalendar.setEnabled(Helper.hasPermission(getContext(), Manifest.permission.WRITE_CALENDAR)); btnCalendar.setEnabled(Helper.hasPermission(getContext(), Manifest.permission.WRITE_CALENDAR));
btnCalendar.setOnClickListener(new View.OnClickListener() { btnCalendar.setOnClickListener(new View.OnClickListener() {
@Override @Override
@ -951,6 +974,7 @@ public class FragmentAccount extends FragmentBase {
args.putString("name", etName.getText().toString()); args.putString("name", etName.getText().toString());
args.putString("category", etCategory.getText().toString()); args.putString("category", etCategory.getText().toString());
args.putInt("color", btnColor.getColor()); args.putInt("color", btnColor.getColor());
args.putString("avatar", avatar);
args.putString("calendar", calendar); args.putString("calendar", calendar);
args.putBoolean("synchronize", cbSynchronize.isChecked()); args.putBoolean("synchronize", cbSynchronize.isChecked());
@ -1030,6 +1054,7 @@ public class FragmentAccount extends FragmentBase {
String name = args.getString("name"); String name = args.getString("name");
String category = args.getString("category"); String category = args.getString("category");
Integer color = args.getInt("color"); Integer color = args.getInt("color");
String avatar = args.getString("avatar");
String calendar = args.getString("calendar"); String calendar = args.getString("calendar");
boolean synchronize = args.getBoolean("synchronize"); boolean synchronize = args.getBoolean("synchronize");
@ -1138,6 +1163,8 @@ public class FragmentAccount extends FragmentBase {
return true; return true;
if (!Objects.equals(account.color, color)) if (!Objects.equals(account.color, color))
return true; return true;
if (!Objects.equals(account.avatar, avatar))
return true;
if (!Objects.equals(account.calendar, calendar)) if (!Objects.equals(account.calendar, calendar))
return true; return true;
if (!Objects.equals(account.synchronize, synchronize)) if (!Objects.equals(account.synchronize, synchronize))
@ -1293,6 +1320,7 @@ public class FragmentAccount extends FragmentBase {
account.name = name; account.name = name;
account.category = category; account.category = category;
account.color = color; account.color = color;
account.avatar = avatar;
account.calendar = calendar; account.calendar = calendar;
account.synchronize = synchronize; account.synchronize = synchronize;
@ -1577,6 +1605,7 @@ public class FragmentAccount extends FragmentBase {
outState.putInt("fair:advanced", grpAdvanced == null ? View.VISIBLE : grpAdvanced.getVisibility()); outState.putInt("fair:advanced", grpAdvanced == null ? View.VISIBLE : grpAdvanced.getVisibility());
outState.putInt("fair:auth", auth); outState.putInt("fair:auth", auth);
outState.putString("fair:authprovider", provider); outState.putString("fair:authprovider", provider);
outState.putString("fair:avatar", avatar);
outState.putString("fair:calendar", calendar); outState.putString("fair:calendar", calendar);
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
@ -1750,6 +1779,7 @@ public class FragmentAccount extends FragmentBase {
auth = (account == null ? AUTH_TYPE_PASSWORD : account.auth_type); auth = (account == null ? AUTH_TYPE_PASSWORD : account.auth_type);
provider = (account == null ? null : account.provider); provider = (account == null ? null : account.provider);
avatar = (account == null ? null : account.avatar);
calendar = (account == null ? null : account.calendar); calendar = (account == null ? null : account.calendar);
new SimpleTask<EntityAccount>() { new SimpleTask<EntityAccount>() {
@ -1781,6 +1811,7 @@ public class FragmentAccount extends FragmentBase {
grpAdvanced.setVisibility(savedInstanceState.getInt("fair:advanced")); grpAdvanced.setVisibility(savedInstanceState.getInt("fair:advanced"));
auth = savedInstanceState.getInt("fair:auth"); auth = savedInstanceState.getInt("fair:auth");
provider = savedInstanceState.getString("fair:authprovider"); provider = savedInstanceState.getString("fair:authprovider");
avatar = savedInstanceState.getString("fair:avatar");
calendar = savedInstanceState.getString("fair:calendar"); calendar = savedInstanceState.getString("fair:calendar");
} }
@ -1977,6 +2008,12 @@ public class FragmentAccount extends FragmentBase {
startActivity(new Intent(getContext(), ActivityBilling.class)); startActivity(new Intent(getContext(), ActivityBilling.class));
} }
break; break;
case REQUEST_AVATAR:
if (resultCode == RESULT_OK && data != null)
onImageSelected(data.getData());
else
avatar = null;
break;
case REQUEST_CALENDAR: case REQUEST_CALENDAR:
if (resultCode == RESULT_OK && data != null) { if (resultCode == RESULT_OK && data != null) {
if (ActivityBilling.isPro(getContext())) { if (ActivityBilling.isPro(getContext())) {
@ -2019,6 +2056,23 @@ public class FragmentAccount extends FragmentBase {
} }
} }
private void onImageSelected(Uri uri) {
try {
final Context context = getContext();
NoStreamException.check(uri, context);
context.getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (!Helper.isPersisted(context, uri, true, false))
throw new IllegalStateException("No permission granted to access selected image " + uri);
avatar = uri.toString();
} catch (NoStreamException ex) {
ex.report(getActivity());
} catch (Throwable ex) {
Log.unexpectedError(getParentFragmentManager(), ex);
}
}
private void onDelete() { private void onDelete() {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong("id", id); args.putLong("id", id);

@ -29,6 +29,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
@ -89,6 +90,9 @@ public class FragmentPop extends FragmentBase {
private ViewButtonColor btnColor; private ViewButtonColor btnColor;
private TextView tvColorPro; private TextView tvColorPro;
private Button btnAvatar;
private TextView tvAvatarPro;
private Button btnCalendar; private Button btnCalendar;
private TextView tvCalendarPro; private TextView tvCalendarPro;
@ -126,13 +130,15 @@ public class FragmentPop extends FragmentBase {
private long id = -1; private long id = -1;
private int auth = AUTH_TYPE_PASSWORD; private int auth = AUTH_TYPE_PASSWORD;
private String avatar = null;
private String calendar = null; private String calendar = null;
private boolean saving = false; private boolean saving = false;
private static final int REQUEST_COLOR = 1; private static final int REQUEST_COLOR = 1;
private static final int REQUEST_CALENDAR = 2; private static final int REQUEST_AVATAR = 2;
private static final int REQUEST_SAVE = 3; private static final int REQUEST_CALENDAR = 3;
private static final int REQUEST_DELETE = 4; private static final int REQUEST_SAVE = 4;
private static final int REQUEST_DELETE = 5;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -169,6 +175,9 @@ public class FragmentPop extends FragmentBase {
btnColor = view.findViewById(R.id.btnColor); btnColor = view.findViewById(R.id.btnColor);
tvColorPro = view.findViewById(R.id.tvColorPro); tvColorPro = view.findViewById(R.id.tvColorPro);
btnAvatar = view.findViewById(R.id.btnAvatar);
tvAvatarPro = view.findViewById(R.id.tvAvatarPro);
btnCalendar = view.findViewById(R.id.btnCalendar); btnCalendar = view.findViewById(R.id.btnCalendar);
tvCalendarPro = view.findViewById(R.id.tvCalendarPro); tvCalendarPro = view.findViewById(R.id.tvCalendarPro);
@ -281,6 +290,21 @@ public class FragmentPop extends FragmentBase {
Helper.linkPro(tvColorPro); Helper.linkPro(tvColorPro);
btnAvatar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("image/*");
Helper.openAdvanced(v.getContext(), intent);
startActivityForResult(intent, REQUEST_AVATAR);
}
});
Helper.linkPro(tvAvatarPro);
grpCalendar.setVisibility(BuildConfig.PLAY_STORE_RELEASE ? View.GONE : View.VISIBLE); grpCalendar.setVisibility(BuildConfig.PLAY_STORE_RELEASE ? View.GONE : View.VISIBLE);
btnCalendar.setEnabled(Helper.hasPermission(getContext(), Manifest.permission.WRITE_CALENDAR)); btnCalendar.setEnabled(Helper.hasPermission(getContext(), Manifest.permission.WRITE_CALENDAR));
btnCalendar.setOnClickListener(new View.OnClickListener() { btnCalendar.setOnClickListener(new View.OnClickListener() {
@ -414,6 +438,7 @@ public class FragmentPop extends FragmentBase {
args.putString("name", etName.getText().toString()); args.putString("name", etName.getText().toString());
args.putString("category", etCategory.getText().toString()); args.putString("category", etCategory.getText().toString());
args.putInt("color", btnColor.getColor()); args.putInt("color", btnColor.getColor());
args.putString("avatar", avatar);
args.putString("calendar", calendar); args.putString("calendar", calendar);
args.putBoolean("synchronize", cbSynchronize.isChecked()); args.putBoolean("synchronize", cbSynchronize.isChecked());
@ -473,6 +498,7 @@ public class FragmentPop extends FragmentBase {
String name = args.getString("name"); String name = args.getString("name");
String category = args.getString("category"); String category = args.getString("category");
Integer color = args.getInt("color"); Integer color = args.getInt("color");
String avatar = args.getString("avatar");
String calendar = args.getString("calendar"); String calendar = args.getString("calendar");
boolean synchronize = args.getBoolean("synchronize"); boolean synchronize = args.getBoolean("synchronize");
@ -566,6 +592,8 @@ public class FragmentPop extends FragmentBase {
return true; return true;
if (!Objects.equals(account.color, color)) if (!Objects.equals(account.color, color))
return true; return true;
if (!Objects.equals(account.avatar, avatar))
return true;
if (!Objects.equals(account.calendar, calendar)) if (!Objects.equals(account.calendar, calendar))
return true; return true;
if (!Objects.equals(account.synchronize, synchronize)) if (!Objects.equals(account.synchronize, synchronize))
@ -669,6 +697,7 @@ public class FragmentPop extends FragmentBase {
account.name = name; account.name = name;
account.category = category; account.category = category;
account.color = color; account.color = color;
account.avatar = avatar;
account.calendar = calendar; account.calendar = calendar;
account.synchronize = synchronize; account.synchronize = synchronize;
@ -816,6 +845,7 @@ public class FragmentPop extends FragmentBase {
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
outState.putString("fair:password", tilPassword == null ? null : tilPassword.getEditText().getText().toString()); outState.putString("fair:password", tilPassword == null ? null : tilPassword.getEditText().getText().toString());
outState.putInt("fair:auth", auth); outState.putInt("fair:auth", auth);
outState.putString("fair:avatar", avatar);
outState.putString("fair:calendar", calendar); outState.putString("fair:calendar", calendar);
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
@ -921,6 +951,7 @@ public class FragmentPop extends FragmentBase {
} }
auth = (account == null ? AUTH_TYPE_PASSWORD : account.auth_type); auth = (account == null ? AUTH_TYPE_PASSWORD : account.auth_type);
avatar = (account == null ? null : account.avatar);
calendar = (account == null ? null : account.calendar); calendar = (account == null ? null : account.calendar);
new SimpleTask<EntityAccount>() { new SimpleTask<EntityAccount>() {
@ -943,6 +974,7 @@ public class FragmentPop extends FragmentBase {
} else { } else {
tilPassword.getEditText().setText(savedInstanceState.getString("fair:password")); tilPassword.getEditText().setText(savedInstanceState.getString("fair:password"));
auth = savedInstanceState.getInt("fair:auth"); auth = savedInstanceState.getInt("fair:auth");
avatar = savedInstanceState.getString("fair:avatar");
calendar = savedInstanceState.getString("fair:calendar"); calendar = savedInstanceState.getString("fair:calendar");
} }
@ -1017,6 +1049,12 @@ public class FragmentPop extends FragmentBase {
startActivity(new Intent(getContext(), ActivityBilling.class)); startActivity(new Intent(getContext(), ActivityBilling.class));
} }
break; break;
case REQUEST_AVATAR:
if (resultCode == RESULT_OK && data != null)
onImageSelected(data.getData());
else
avatar = null;
break;
case REQUEST_CALENDAR: case REQUEST_CALENDAR:
if (resultCode == RESULT_OK && data != null) { if (resultCode == RESULT_OK && data != null) {
if (ActivityBilling.isPro(getContext())) { if (ActivityBilling.isPro(getContext())) {
@ -1055,6 +1093,23 @@ public class FragmentPop extends FragmentBase {
} }
} }
private void onImageSelected(Uri uri) {
try {
final Context context = getContext();
NoStreamException.check(uri, context);
context.getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (!Helper.isPersisted(context, uri, true, false))
throw new IllegalStateException("No permission granted to access selected image " + uri);
avatar = uri.toString();
} catch (NoStreamException ex) {
ex.report(getActivity());
} catch (Throwable ex) {
Log.unexpectedError(getParentFragmentManager(), ex);
}
}
private void onDelete() { private void onDelete() {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong("id", id); args.putLong("id", id);

@ -536,6 +536,38 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvColorHint" /> app:layout_constraintTop_toBottomOf="@id/tvColorHint" />
<TextView
android:id="@+id/tvAvatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_avatar"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvColorPro" />
<Button
android:id="@+id/btnAvatar"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/twotone_image_24"
android:drawablePadding="6dp"
android:text="@string/title_select"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvAvatar" />
<TextView
android:id="@+id/tvAvatarPro"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="@string/title_pro_feature"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?android:attr/textColorLink"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnAvatar" />
<TextView <TextView
android:id="@+id/tvCalendar" android:id="@+id/tvCalendar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -544,7 +576,7 @@
android:text="@string/title_calendar" android:text="@string/title_calendar"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvColorPro" /> app:layout_constraintTop_toBottomOf="@id/tvAvatarPro" />
<Button <Button
android:id="@+id/btnCalendar" android:id="@+id/btnCalendar"
@ -1260,7 +1292,8 @@
btnCertificate,tvCertificate, btnCertificate,tvCertificate,
tvRealm,etRealm, tvRealm,etRealm,
tvName,tvNameRemark,etName,tvCategory,etCategory, tvName,tvNameRemark,etName,tvCategory,etCategory,
tvColor,btnColor,tvColorHint,tvColorPro" /> tvColor,btnColor,tvColorHint,tvColorPro,
tvAvatar,btnAvatar,tvAvatarPro" />
<androidx.constraintlayout.widget.Group <androidx.constraintlayout.widget.Group
android:id="@+id/grpCalendar" android:id="@+id/grpCalendar"

@ -418,6 +418,38 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvColorHint" /> app:layout_constraintTop_toBottomOf="@id/tvColorHint" />
<TextView
android:id="@+id/tvAvatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_avatar"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvColorPro" />
<Button
android:id="@+id/btnAvatar"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/twotone_image_24"
android:drawablePadding="6dp"
android:text="@string/title_select"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvAvatar" />
<TextView
android:id="@+id/tvAvatarPro"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="@string/title_pro_feature"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?android:attr/textColorLink"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnAvatar" />
<TextView <TextView
android:id="@+id/tvCalendar" android:id="@+id/tvCalendar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -426,7 +458,7 @@
android:text="@string/title_calendar" android:text="@string/title_calendar"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvColorPro" /> app:layout_constraintTop_toBottomOf="@id/tvAvatarPro" />
<Button <Button
android:id="@+id/btnCalendar" android:id="@+id/btnCalendar"

@ -32,6 +32,20 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/ivAvatar"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="6dp"
android:contentDescription="@string/title_legend_avatar"
android:padding="3dp"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="@+id/tvUser"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toTopOf="@+id/tvName"
app:srcCompat="@drawable/twotone_person_24" />
<ImageView <ImageView
android:id="@+id/ivState" android:id="@+id/ivState"
android:layout_width="24dp" android:layout_width="24dp"
@ -40,7 +54,7 @@
android:layout_marginEnd="6dp" android:layout_marginEnd="6dp"
android:contentDescription="@string/title_legend_disconnected" android:contentDescription="@string/title_legend_disconnected"
app:layout_constraintBottom_toBottomOf="@+id/tvName" app:layout_constraintBottom_toBottomOf="@+id/tvName"
app:layout_constraintStart_toEndOf="@id/vwColor" app:layout_constraintStart_toEndOf="@id/ivAvatar"
app:layout_constraintTop_toTopOf="@+id/tvName" app:layout_constraintTop_toTopOf="@+id/tvName"
app:srcCompat="@drawable/twotone_cloud_off_24" /> app:srcCompat="@drawable/twotone_cloud_off_24" />
@ -205,11 +219,11 @@
android:layout_marginStart="6dp" android:layout_marginStart="6dp"
android:layout_marginEnd="6dp" android:layout_marginEnd="6dp"
android:drawablePadding="6dp" android:drawablePadding="6dp"
app:drawableTint="?attr/colorWarning"
android:ellipsize="start" android:ellipsize="start"
android:singleLine="true" android:singleLine="true"
android:text="123/456 MB" android:text="123/456 MB"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:drawableTint="?attr/colorWarning"
app:layout_constraintEnd_toStartOf="@+id/tvMaxSize" app:layout_constraintEnd_toStartOf="@+id/tvMaxSize"
app:layout_constraintStart_toEndOf="@+id/ivState" app:layout_constraintStart_toEndOf="@+id/ivState"
app:layout_constraintTop_toBottomOf="@id/tvBackoff" /> app:layout_constraintTop_toBottomOf="@id/tvBackoff" />
@ -328,10 +342,10 @@
android:backgroundTint="?attr/colorInfoBackground" android:backgroundTint="?attr/colorInfoBackground"
android:drawableEnd="@drawable/twotone_help_24" android:drawableEnd="@drawable/twotone_help_24"
android:drawablePadding="6dp" android:drawablePadding="6dp"
app:drawableTint="?attr/colorInfoForeground"
android:text="@string/title_setup_help" android:text="@string/title_setup_help"
android:textColor="?attr/colorInfoForeground" android:textColor="?attr/colorInfoForeground"
android:textStyle="bold" android:textStyle="bold"
app:drawableTint="?attr/colorInfoForeground"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvError" /> app:layout_constraintTop_toBottomOf="@id/tvError" />

@ -1224,6 +1224,7 @@
<string name="title_account_partial_fetch_hint">Disable this only in case of empty messages or corrupt attachments</string> <string name="title_account_partial_fetch_hint">Disable this only in case of empty messages or corrupt attachments</string>
<string name="title_account_raw_fetch_hint">Enabling this will increase battery and data usage</string> <string name="title_account_raw_fetch_hint">Enabling this will increase battery and data usage</string>
<string name="title_color">Color</string> <string name="title_color">Color</string>
<string name="title_avatar">Image</string>
<string name="title_calendar">Calendar</string> <string name="title_calendar">Calendar</string>
<string name="title_background">Background</string> <string name="title_background">Background</string>
<string name="title_transparent">Transparent</string> <string name="title_transparent">Transparent</string>

Loading…
Cancel
Save