Identity colors

pull/146/head
M66B 6 years ago
parent bb7c730936
commit b6c651126d

File diff suppressed because it is too large Load Diff

@ -21,6 +21,7 @@ package eu.faircode.email;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@ -49,6 +50,7 @@ public class AdapterIdentity extends RecyclerView.Adapter<AdapterIdentity.ViewHo
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
View itemView;
View vwColor;
ImageView ivPrimary;
TextView tvName;
ImageView ivSync;
@ -62,6 +64,7 @@ public class AdapterIdentity extends RecyclerView.Adapter<AdapterIdentity.ViewHo
super(itemView);
this.itemView = itemView;
vwColor = itemView.findViewById(R.id.vwColor);
ivPrimary = itemView.findViewById(R.id.ivPrimary);
tvName = itemView.findViewById(R.id.tvName);
ivSync = itemView.findViewById(R.id.ivSync);
@ -81,6 +84,7 @@ public class AdapterIdentity extends RecyclerView.Adapter<AdapterIdentity.ViewHo
}
private void bindTo(TupleIdentityEx identity) {
vwColor.setBackgroundColor(identity.color == null ? Color.TRANSPARENT : identity.color);
ivPrimary.setVisibility(identity.primary ? View.VISIBLE : View.INVISIBLE);
tvName.setText(identity.name);
ivSync.setImageResource(identity.synchronize ? R.drawable.baseline_sync_24 : R.drawable.baseline_sync_disabled_24);

@ -46,7 +46,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
version = 26,
version = 27,
entities = {
EntityIdentity.class,
EntityAccount.class,
@ -307,6 +307,14 @@ public abstract class DB extends RoomDatabase {
db.execSQL("ALTER TABLE `message` ADD COLUMN `extra` TEXT");
}
})
.addMigrations(new Migration(26, 27) {
@Override
public void migrate(SupportSQLiteDatabase db) {
Log.i(Helper.TAG, "DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `identity` ADD COLUMN `color` INTEGER");
db.execSQL("CREATE INDEX `index_identity_account_email` ON `identity` (`account`, `email`)");
}
})
.build();
}

@ -49,6 +49,9 @@ public interface DaoIdentity {
@Query("SELECT * FROM identity WHERE id = :id")
EntityIdentity getIdentity(long id);
@Query("SELECT * FROM identity WHERE account = :account AND email = :email")
EntityIdentity getIdentity(long account, String email);
@Query("SELECT * FROM identity WHERE id = :id")
LiveData<EntityIdentity> liveIdentity(long id);

@ -36,7 +36,7 @@ public interface DaoMessage {
// https://www.sqlite.org/lang_select.html
@Query("SELECT message.*" +
", account.name AS accountName, account.color AS accountColor" +
", account.name AS accountName, IFNULL(identity.color,account.color) AS accountColor" +
", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" +
", COUNT(message.id) AS count" +
", SUM(CASE WHEN message.ui_seen" +
@ -51,6 +51,7 @@ public interface DaoMessage {
", MAX(CASE WHEN folder.unified THEN message.id ELSE 0 END) AS dummy" +
" FROM message" +
" JOIN account ON account.id = message.account" +
" LEFT JOIN identity ON identity.id = message.identity" +
" JOIN folder ON folder.id = message.folder" +
" WHERE account.`synchronize`" +
" AND (NOT message.ui_hide OR :debug)" +
@ -65,7 +66,7 @@ public interface DaoMessage {
DataSource.Factory<Integer, TupleMessageEx> pagedUnifiedInbox(String sort, boolean debug);
@Query("SELECT message.*" +
", account.name AS accountName, account.color AS accountColor" +
", account.name AS accountName, IFNULL(identity.color,account.color) AS accountColor" +
", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" +
", COUNT(message.id) AS count" +
", SUM(CASE WHEN message.ui_seen" +
@ -80,6 +81,7 @@ public interface DaoMessage {
", MAX(CASE WHEN folder.id = :folder THEN message.id ELSE 0 END) AS dummy" +
" FROM message" +
" JOIN account ON account.id = message.account" +
" LEFT JOIN identity ON identity.id = message.identity" +
" JOIN folder ON folder.id = message.folder" +
" JOIN folder f ON f.id = :folder" +
" WHERE (message.account = f.account OR folder.type = '" + EntityFolder.OUTBOX + "')" +
@ -95,7 +97,7 @@ public interface DaoMessage {
DataSource.Factory<Integer, TupleMessageEx> pagedFolder(long folder, String sort, boolean found, boolean debug);
@Query("SELECT message.*" +
", account.name AS accountName, account.color AS accountColor" +
", account.name AS accountName, IFNULL(identity.color,account.color) AS accountColor" +
", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" +
", (SELECT COUNT(m1.id) FROM message m1 WHERE m1.account = message.account AND m1.thread = message.thread AND NOT m1.ui_hide) AS count" +
", CASE WHEN message.ui_seen THEN 0 ELSE 1 END AS unseen" +
@ -103,6 +105,7 @@ public interface DaoMessage {
", (SELECT COUNT(a.id) FROM attachment a WHERE a.message = message.id) AS attachments" +
" FROM message" +
" JOIN account ON account.id = message.account" +
" LEFT JOIN identity ON identity.id = message.identity" +
" JOIN folder ON folder.id = message.folder" +
" WHERE message.account = :account" +
" AND message.thread = :thread" +
@ -165,7 +168,7 @@ public interface DaoMessage {
List<Long> getMessageWithoutPreview();
@Query("SELECT message.*" +
", account.name AS accountName, account.color AS accountColor" +
", account.name AS accountName, IFNULL(identity.color,account.color) AS accountColor" +
", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" +
", (SELECT COUNT(m1.id) FROM message m1 WHERE m1.account = message.account AND m1.thread = message.thread AND NOT m1.ui_hide) AS count" +
", CASE WHEN message.ui_seen THEN 0 ELSE 1 END AS unseen" +
@ -173,12 +176,13 @@ public interface DaoMessage {
", (SELECT COUNT(a.id) FROM attachment a WHERE a.message = message.id) AS attachments" +
" FROM message" +
" JOIN account ON account.id = message.account" +
" LEFT JOIN identity ON identity.id = message.identity" +
" JOIN folder ON folder.id = message.folder" +
" WHERE message.id = :id")
LiveData<TupleMessageEx> liveMessage(long id);
@Query("SELECT message.*" +
", account.name AS accountName, account.color AS accountColor" +
", account.name AS accountName, IFNULL(identity.color,account.color) AS accountColor" +
", folder.name AS folderName, folder.display AS folderDisplay, folder.type AS folderType" +
", 1 AS count" +
", 1 AS unseen" +
@ -186,6 +190,7 @@ public interface DaoMessage {
", 0 AS attachments" +
" FROM message" +
" JOIN account ON account.id = message.account" +
" LEFT JOIN identity ON identity.id = message.identity" +
" JOIN folder ON folder.id = message.folder" +
" WHERE account.`synchronize`" +
" AND folder.unified" +

@ -36,7 +36,8 @@ import static androidx.room.ForeignKey.CASCADE;
@ForeignKey(childColumns = "account", entity = EntityAccount.class, parentColumns = "id", onDelete = CASCADE)
},
indices = {
@Index(value = {"account"})
@Index(value = {"account"}),
@Index(value = {"account", "email"})
}
)
public class EntityIdentity {
@ -67,6 +68,7 @@ public class EntityIdentity {
public Integer auth_type;
@NonNull
public Boolean primary;
public Integer color;
@NonNull
public Boolean synchronize;
@NonNull
@ -88,6 +90,8 @@ public class EntityIdentity {
json.put("password", "");
json.put("auth_type", auth_type);
json.put("primary", primary);
if (color != null)
json.put("color", color);
json.put("synchronize", false);
json.put("store_sent", store_sent);
// not state
@ -109,6 +113,8 @@ public class EntityIdentity {
identity.password = json.getString("password");
identity.auth_type = json.getInt("auth_type");
identity.primary = json.getBoolean("primary");
if (json.has("color"))
identity.color = json.getInt("color");
identity.synchronize = json.getBoolean("synchronize");
identity.store_sent = json.getBoolean("store_sent");
return identity;
@ -129,6 +135,7 @@ public class EntityIdentity {
this.user.equals(other.user) &&
this.password.equals(other.password) &&
this.primary.equals(other.primary) &&
(this.color == null ? other.color == null : this.color.equals(other.color)) &&
this.synchronize.equals(other.synchronize) &&
this.store_sent.equals(other.store_sent) &&
(this.state == null ? other.state == null : this.state.equals(other.state)) &&

@ -22,6 +22,8 @@ package eu.faircode.email;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
@ -37,11 +39,14 @@ import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import com.android.colorpicker.ColorPickerDialog;
import com.android.colorpicker.ColorPickerSwatch;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.TextInputLayout;
@ -61,6 +66,7 @@ import javax.mail.Transport;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Group;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Observer;
public class FragmentIdentity extends FragmentEx {
@ -80,6 +86,9 @@ public class FragmentIdentity extends FragmentEx {
private EditText etPort;
private EditText etUser;
private TextInputLayout tilPassword;
private Button btnColor;
private View vwColor;
private ImageView ibColorDefault;
private CheckBox cbSynchronize;
private CheckBox cbPrimary;
private CheckBox cbStoreSent;
@ -90,6 +99,7 @@ public class FragmentIdentity extends FragmentEx {
private Group grpAdvanced;
private long id = -1;
private int color = Color.TRANSPARENT;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -131,6 +141,10 @@ public class FragmentIdentity extends FragmentEx {
etUser = view.findViewById(R.id.etUser);
tilPassword = view.findViewById(R.id.tilPassword);
btnColor = view.findViewById(R.id.btnColor);
vwColor = view.findViewById(R.id.vwColor);
ibColorDefault = view.findViewById(R.id.ibColorDefault);
cbSynchronize = view.findViewById(R.id.cbSynchronize);
cbPrimary = view.findViewById(R.id.cbPrimary);
cbStoreSent = view.findViewById(R.id.cbStoreSent);
@ -289,6 +303,37 @@ public class FragmentIdentity extends FragmentEx {
}
});
vwColor.setBackgroundColor(color);
btnColor.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (Helper.isPro(getContext())) {
int[] colors = getContext().getResources().getIntArray(R.array.colorPicker);
ColorPickerDialog colorPickerDialog = new ColorPickerDialog();
colorPickerDialog.initialize(R.string.title_account_color, colors, color, 4, colors.length);
colorPickerDialog.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener() {
@Override
public void onColorSelected(int color) {
setColor(color);
}
});
colorPickerDialog.show(getFragmentManager(), "colorpicker");
} else {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.hide(FragmentIdentity.this);
fragmentTransaction.add(R.id.content_frame, new FragmentPro()).addToBackStack("pro");
fragmentTransaction.commit();
}
}
});
ibColorDefault.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setColor(Color.TRANSPARENT);
}
});
cbSynchronize.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@ -318,6 +363,7 @@ public class FragmentIdentity extends FragmentEx {
args.putString("port", etPort.getText().toString());
args.putString("user", etUser.getText().toString());
args.putString("password", tilPassword.getEditText().getText().toString());
args.putInt("color", color);
args.putBoolean("synchronize", cbSynchronize.isChecked());
args.putBoolean("primary", cbPrimary.isChecked());
args.putBoolean("store_sent", cbStoreSent.isChecked());
@ -336,6 +382,7 @@ public class FragmentIdentity extends FragmentEx {
String port = args.getString("port");
String user = args.getString("user");
String password = args.getString("password");
Integer color = args.getInt("color");
int auth_type = args.getInt("auth_type");
boolean synchronize = args.getBoolean("synchronize");
boolean primary = args.getBoolean("primary");
@ -356,6 +403,8 @@ public class FragmentIdentity extends FragmentEx {
if (TextUtils.isEmpty(replyto))
replyto = null;
if (Color.TRANSPARENT == color)
color = null;
// Check SMTP server
if (synchronize) {
@ -396,6 +445,7 @@ public class FragmentIdentity extends FragmentEx {
identity.port = Integer.parseInt(port);
identity.user = user;
identity.password = password;
identity.color = color;
identity.auth_type = auth_type;
identity.synchronize = synchronize;
identity.primary = (identity.synchronize && primary);
@ -504,6 +554,7 @@ public class FragmentIdentity extends FragmentEx {
outState.putInt("provider", spProvider.getSelectedItemPosition());
outState.putString("password", tilPassword.getEditText().getText().toString());
outState.putInt("advanced", grpAdvanced.getVisibility());
outState.putInt("color", color);
}
@Override
@ -535,6 +586,8 @@ public class FragmentIdentity extends FragmentEx {
cbPrimary.setChecked(identity == null ? true : identity.primary);
cbStoreSent.setChecked(identity == null ? false : identity.store_sent);
color = (identity == null || identity.color == null ? Color.TRANSPARENT : identity.color);
etName.requestFocus();
if (identity == null)
@ -552,10 +605,12 @@ public class FragmentIdentity extends FragmentEx {
} else {
tilPassword.getEditText().setText(savedInstanceState.getString("password"));
grpAdvanced.setVisibility(savedInstanceState.getInt("advanced"));
color = savedInstanceState.getInt("color");
}
Helper.setViewsEnabled(view, true);
setColor(color);
cbPrimary.setEnabled(cbSynchronize.isChecked());
// Consider previous save/delete as cancelled
@ -629,4 +684,13 @@ public class FragmentIdentity extends FragmentEx {
}
});
}
private void setColor(int color) {
FragmentIdentity.this.color = color;
GradientDrawable border = new GradientDrawable();
border.setColor(color);
border.setStroke(1, Helper.resolveColor(getContext(), R.attr.colorSeparator));
vwColor.setBackground(border);
}
}

@ -1870,9 +1870,15 @@ public class ServiceSynchronize extends LifecycleService {
}
if (message == null) {
Address[] to = helper.getTo();
EntityIdentity identity = null;
if (to != null && to.length > 0)
identity = db.identity().getIdentity(folder.account, ((InternetAddress) to[0]).getAddress());
message = new EntityMessage();
message.account = folder.account;
message.folder = folder.id;
message.identity = (identity == null ? null : identity.id);
message.uid = uid;
if (!EntityFolder.ARCHIVE.equals(folder.type)) {

@ -300,6 +300,39 @@
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/btnColor"
style="@style/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:minWidth="0dp"
android:minHeight="0dp"
android:text="@string/title_account_color"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" />
<View
android:id="@+id/vwColor"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="12dp"
android:background="@color/colorAccent"
app:layout_constraintBottom_toBottomOf="@id/btnColor"
app:layout_constraintStart_toEndOf="@id/btnColor"
app:layout_constraintTop_toTopOf="@id/btnColor" />
<ImageView
android:id="@+id/ibColorDefault"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:src="@drawable/baseline_delete_24"
app:layout_constraintBottom_toBottomOf="@id/btnColor"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tilPassword" />
<CheckBox
android:id="@+id/cbSynchronize"
android:layout_width="wrap_content"
@ -307,7 +340,7 @@
android:layout_marginTop="12dp"
android:text="@string/title_synchronize_identity"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tilPassword" />
app:layout_constraintTop_toBottomOf="@id/btnColor" />
<CheckBox
android:id="@+id/cbPrimary"
@ -371,6 +404,6 @@
android:id="@+id/grpAdvanced"
android:layout_width="0dp"
android:layout_height="0dp"
app:constraint_referenced_ids="tvEmail,etEmail,tvReplyTo,etReplyTo,tvProvider,spProvider,tvDomain,etDomain,btnAutoConfig,tvSmtp,tvInsecure,tvHost,etHost,cbStartTls,tvPort,etPort,tvUser,etUser,tvPassword,tilPassword,cbSynchronize,cbPrimary,cbStoreSent" />
app:constraint_referenced_ids="tvEmail,etEmail,tvReplyTo,etReplyTo,tvProvider,spProvider,tvDomain,etDomain,btnAutoConfig,tvSmtp,tvInsecure,tvHost,etHost,cbStartTls,tvPort,etPort,tvUser,etUser,tvPassword,tilPassword,btnColor,vwColor,ibColorDefault,cbSynchronize,cbPrimary,cbStoreSent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

@ -4,23 +4,31 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="@+id/vwColor"
android:layout_width="6dp"
android:layout_height="0dp"
android:background="@color/colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/ivPrimary"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="6dp"
android:layout_marginStart="6dp"
android:layout_marginTop="3dp"
android:layout_marginEnd="6dp"
android:src="@drawable/baseline_star_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:ellipsize="end"
android:maxLines="1"
android:text="Name"
@ -34,8 +42,8 @@
android:id="@+id/ivSync"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="6dp"
android:layout_marginTop="3dp"
android:layout_marginEnd="6dp"
android:src="@drawable/baseline_sync_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@ -44,8 +52,8 @@
android:id="@+id/tvUser"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:text="user"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintEnd_toStartOf="@+id/ivSync"
@ -56,10 +64,10 @@
android:id="@+id/ivState"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="6dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:src="@drawable/baseline_cloud_off_24"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvUser" />
<TextView
@ -80,8 +88,8 @@
android:id="@+id/tvAccount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:text="account"
android:textAlignment="textEnd"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
@ -94,21 +102,21 @@
android:id="@+id/tvError"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:text="error"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvHost" />
<View
android:id="@+id/vSeparator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:background="?attr/colorSeparator"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvError" />

Loading…
Cancel
Save