Delegate deletion of accounts, identities & folders

This commit is contained in:
M66B 2018-12-06 13:43:00 +01:00
parent fe43910b93
commit 0c072fb980
19 changed files with 1553 additions and 365 deletions

File diff suppressed because it is too large Load Diff

View File

@ -67,7 +67,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
ViewHolder(View itemView) { ViewHolder(View itemView) {
super(itemView); super(itemView);
this.itemView = itemView; this.itemView = itemView.findViewById(R.id.clItem);
vwColor = itemView.findViewById(R.id.vwColor); vwColor = itemView.findViewById(R.id.vwColor);
ivPrimary = itemView.findViewById(R.id.ivPrimary); ivPrimary = itemView.findViewById(R.id.ivPrimary);
tvName = itemView.findViewById(R.id.tvName); tvName = itemView.findViewById(R.id.tvName);
@ -88,6 +88,7 @@ public class AdapterAccount extends RecyclerView.Adapter<AdapterAccount.ViewHold
} }
private void bindTo(EntityAccount account) { private void bindTo(EntityAccount account) {
itemView.setActivated(account.tbd != null);
vwColor.setBackgroundColor(account.color == null ? Color.TRANSPARENT : account.color); vwColor.setBackgroundColor(account.color == null ? Color.TRANSPARENT : account.color);
ivPrimary.setVisibility(account.primary ? View.VISIBLE : View.INVISIBLE); ivPrimary.setVisibility(account.primary ? View.VISIBLE : View.INVISIBLE);
tvName.setText(account.name); tvName.setText(account.name);

View File

@ -82,7 +82,6 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
private final static int action_delete_local = 2; private final static int action_delete_local = 2;
private final static int action_empty_trash = 3; private final static int action_empty_trash = 3;
private final static int action_edit_properties = 4; private final static int action_edit_properties = 4;
private final static int action_legend = 5;
ViewHolder(View itemView) { ViewHolder(View itemView) {
super(itemView); super(itemView);
@ -112,6 +111,7 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
} }
private void bindTo(TupleFolderEx folder) { private void bindTo(TupleFolderEx folder) {
itemView.setActivated(folder.tbd != null);
itemView.setAlpha(folder.hide ? 0.5f : 1.0f); itemView.setAlpha(folder.hide ? 0.5f : 1.0f);
vwColor.setBackgroundColor(folder.accountColor == null ? Color.TRANSPARENT : folder.accountColor); vwColor.setBackgroundColor(folder.accountColor == null ? Color.TRANSPARENT : folder.accountColor);

View File

@ -63,7 +63,7 @@ public class AdapterIdentity extends RecyclerView.Adapter<AdapterIdentity.ViewHo
ViewHolder(View itemView) { ViewHolder(View itemView) {
super(itemView); super(itemView);
this.itemView = itemView; this.itemView = itemView.findViewById(R.id.clItem);
vwColor = itemView.findViewById(R.id.vwColor); vwColor = itemView.findViewById(R.id.vwColor);
ivPrimary = itemView.findViewById(R.id.ivPrimary); ivPrimary = itemView.findViewById(R.id.ivPrimary);
tvName = itemView.findViewById(R.id.tvName); tvName = itemView.findViewById(R.id.tvName);
@ -84,6 +84,7 @@ public class AdapterIdentity extends RecyclerView.Adapter<AdapterIdentity.ViewHo
} }
private void bindTo(TupleIdentityEx identity) { private void bindTo(TupleIdentityEx identity) {
itemView.setActivated(identity.tbd != null);
vwColor.setBackgroundColor(identity.color == null ? Color.TRANSPARENT : identity.color); vwColor.setBackgroundColor(identity.color == null ? Color.TRANSPARENT : identity.color);
ivPrimary.setVisibility(identity.primary ? View.VISIBLE : View.INVISIBLE); ivPrimary.setVisibility(identity.primary ? View.VISIBLE : View.INVISIBLE);
tvName.setText(identity.name); tvName.setText(identity.name);

View File

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

View File

@ -90,7 +90,10 @@ public interface DaoAccount {
@Query("UPDATE account SET `primary` = 0") @Query("UPDATE account SET `primary` = 0")
void resetPrimary(); void resetPrimary();
@Query("DELETE FROM account WHERE id = :id") @Query("UPDATE account SET tbd = 1 WHERE id = :id")
void deleteAccount(long id); int setAccountTbd(long id);
@Query("DELETE FROM account WHERE tbd = 1")
int deleteAccountsTbd();
} }

View File

@ -152,20 +152,17 @@ public interface DaoFolder {
@Query("UPDATE folder" + @Query("UPDATE folder" +
" SET name = :name" + " SET name = :name" +
", display = :display" + ", display = :display" +
", unified = :unified" +
", hide = :hide" + ", hide = :hide" +
", synchronize = :synchronize" + ", synchronize = :synchronize" +
", poll = :poll" + ", poll = :poll" +
", unified = :unified" +
", `sync_days` = :sync_days" + ", `sync_days` = :sync_days" +
", `keep_days` = :keep_days" + ", `keep_days` = :keep_days" +
" WHERE id = :id") " WHERE id = :id")
int setFolderProperties( int setFolderProperties(
long id, long id,
String name, String display, String name, String display, boolean unified, boolean hide,
boolean hide, boolean synchronize, boolean poll,
boolean synchronize,
boolean poll,
boolean unified,
int sync_days, int keep_days); int sync_days, int keep_days);
@Query("UPDATE folder SET keywords = :keywords WHERE id = :id") @Query("UPDATE folder SET keywords = :keywords WHERE id = :id")
@ -174,6 +171,9 @@ public interface DaoFolder {
@Query("UPDATE folder SET name = :name WHERE account = :account AND name = :old") @Query("UPDATE folder SET name = :name WHERE account = :account AND name = :old")
int renameFolder(long account, String old, String name); int renameFolder(long account, String old, String name);
@Query("UPDATE folder SET tbd = 1 WHERE id = :id")
int setFolderTbd(long id);
@Query("DELETE FROM folder WHERE id = :id") @Query("DELETE FROM folder WHERE id = :id")
void deleteFolder(long id); void deleteFolder(long id);

View File

@ -73,6 +73,9 @@ public interface DaoIdentity {
@Query("UPDATE identity SET `primary` = 0") @Query("UPDATE identity SET `primary` = 0")
void resetPrimary(); void resetPrimary();
@Query("DELETE FROM identity WHERE id = :id") @Query("UPDATE identity SET tbd = 1 WHERE id = :id")
void deleteIdentity(long id); int setIdentityTbd(long id);
@Query("DELETE FROM identity WHERE tbd = 1")
int deleteIdentitiesTbd();
} }

View File

@ -62,6 +62,7 @@ public class EntityAccount {
@NonNull @NonNull
public Integer poll_interval; // keep-alive interval public Integer poll_interval; // keep-alive interval
public Long created; public Long created;
public Boolean tbd;
public String state; public String state;
public String error; public String error;
public Long last_connected; public Long last_connected;
@ -128,6 +129,7 @@ public class EntityAccount {
this.notify.equals(other.notify) && this.notify.equals(other.notify) &&
this.poll_interval.equals(other.poll_interval) && this.poll_interval.equals(other.poll_interval) &&
(this.created == null ? other.created == null : this.created.equals(other.created)) && (this.created == null ? other.created == null : this.created.equals(other.created)) &&
(this.tbd == null ? other.tbd == null : this.tbd.equals(other.tbd)) &&
(this.state == null ? other.state == null : this.state.equals(other.state)) && (this.state == null ? other.state == null : this.state.equals(other.state)) &&
(this.error == null ? other.error == null : this.error.equals(other.error))); (this.error == null ? other.error == null : this.error.equals(other.error)));
} else } else

View File

@ -80,6 +80,7 @@ public class EntityFolder implements Serializable {
@NonNull @NonNull
public Boolean unified = false; public Boolean unified = false;
public String[] keywords; public String[] keywords;
public Boolean tbd;
public String state; public String state;
public String sync_state; public String sync_state;
public String error; public String error;
@ -176,6 +177,8 @@ public class EntityFolder implements Serializable {
(this.display == null ? other.display == null : this.display.equals(other.display)) && (this.display == null ? other.display == null : this.display.equals(other.display)) &&
this.hide == other.hide && this.hide == other.hide &&
this.unified == other.unified && this.unified == other.unified &&
Helper.equal(this.keywords, other.keywords) &&
(this.tbd == null ? other.tbd == null : this.tbd.equals(other.tbd)) &&
(this.state == null ? other.state == null : this.state.equals(other.state)) && (this.state == null ? other.state == null : this.state.equals(other.state)) &&
(this.sync_state == null ? other.sync_state == null : this.sync_state.equals(other.sync_state)) && (this.sync_state == null ? other.sync_state == null : this.sync_state.equals(other.sync_state)) &&
(this.error == null ? other.error == null : this.error.equals(other.error))); (this.error == null ? other.error == null : this.error.equals(other.error)));

View File

@ -75,6 +75,7 @@ public class EntityIdentity {
@NonNull @NonNull
public Boolean store_sent; public Boolean store_sent;
public Long sent_folder; public Long sent_folder;
public Boolean tbd;
public String state; public String state;
public String error; public String error;
@ -149,6 +150,7 @@ public class EntityIdentity {
this.synchronize.equals(other.synchronize) && this.synchronize.equals(other.synchronize) &&
this.store_sent.equals(other.store_sent) && this.store_sent.equals(other.store_sent) &&
(this.sent_folder == null ? other.sent_folder == null : this.sent_folder.equals(other.sent_folder)) && (this.sent_folder == null ? other.sent_folder == null : this.sent_folder.equals(other.sent_folder)) &&
(this.tbd == null ? other.tbd == null : this.tbd.equals(other.tbd)) &&
(this.state == null ? other.state == null : this.state.equals(other.state)) && (this.state == null ? other.state == null : this.state.equals(other.state)) &&
(this.error == null ? other.error == null : this.error.equals(other.error))); (this.error == null ? other.error == null : this.error.equals(other.error)));
} else } else

View File

@ -1052,11 +1052,12 @@ public class FragmentAccount extends FragmentEx {
@Override @Override
protected Void onLoad(Context context, Bundle args) { protected Void onLoad(Context context, Bundle args) {
long id = args.getLong("id"); long id = args.getLong("id");
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
EntityAccount account = db.account().getAccount(id); db.account().setAccountTbd(id);
db.account().deleteAccount(id);
if (account.synchronize) ServiceSynchronize.reload(getContext(), "delete account");
ServiceSynchronize.reload(getContext(), "delete account");
return null; return null;
} }

View File

@ -37,21 +37,15 @@ import android.widget.EditText;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.IMAPStore;
import java.util.Calendar; import java.util.Calendar;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.Session;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
public class FragmentFolder extends FragmentEx { public class FragmentFolder extends FragmentEx {
private ViewGroup view; private ViewGroup view;
private EditText etRename; private EditText etName;
private EditText etDisplay; private EditText etDisplay;
private CheckBox cbHide; private CheckBox cbHide;
private CheckBox cbSynchronize; private CheckBox cbSynchronize;
@ -85,7 +79,7 @@ public class FragmentFolder extends FragmentEx {
view = (ViewGroup) inflater.inflate(R.layout.fragment_folder, container, false); view = (ViewGroup) inflater.inflate(R.layout.fragment_folder, container, false);
// Get controls // Get controls
etRename = view.findViewById(R.id.etRename); etName = view.findViewById(R.id.etName);
etDisplay = view.findViewById(R.id.etDisplay); etDisplay = view.findViewById(R.id.etDisplay);
cbHide = view.findViewById(R.id.cbHide); cbHide = view.findViewById(R.id.cbHide);
cbSynchronize = view.findViewById(R.id.cbSynchronize); cbSynchronize = view.findViewById(R.id.cbSynchronize);
@ -114,7 +108,7 @@ public class FragmentFolder extends FragmentEx {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong("id", id); args.putLong("id", id);
args.putLong("account", account); args.putLong("account", account);
args.putString("name", etRename.getText().toString()); args.putString("name", etName.getText().toString());
args.putString("display", etDisplay.getText().toString()); args.putString("display", etDisplay.getText().toString());
args.putBoolean("hide", cbHide.isChecked()); args.putBoolean("hide", cbHide.isChecked());
args.putBoolean("unified", cbUnified.isChecked()); args.putBoolean("unified", cbUnified.isChecked());
@ -125,7 +119,7 @@ public class FragmentFolder extends FragmentEx {
new SimpleTask<Void>() { new SimpleTask<Void>() {
@Override @Override
protected Void onLoad(Context context, Bundle args) throws Throwable { protected Void onLoad(Context context, Bundle args) {
long id = args.getLong("id"); long id = args.getLong("id");
long aid = args.getLong("account"); long aid = args.getLong("account");
String name = args.getString("name"); String name = args.getString("name");
@ -144,61 +138,32 @@ public class FragmentFolder extends FragmentEx {
if (keep_days < sync_days) if (keep_days < sync_days)
keep_days = sync_days; keep_days = sync_days;
boolean reload = false; boolean reload;
EntityFolder folder;
IMAPStore istore = null;
DB db = DB.getInstance(getContext()); DB db = DB.getInstance(getContext());
try { try {
db.beginTransaction(); db.beginTransaction();
folder = db.folder().getFolder(id); EntityFolder folder = db.folder().getFolder(id);
if (folder == null || !folder.name.equals(name)) { if (folder == null) {
EntityAccount account = db.account().getAccount(folder == null ? aid : folder.account); reload = true;
Log.i(Helper.TAG, "Creating folder=" + name);
Properties props = MessageHelper.getSessionProperties(account.auth_type, account.insecure); EntityFolder create = new EntityFolder();
Session isession = Session.getInstance(props, null); create.account = aid;
istore = (IMAPStore) isession.getStore(account.starttls ? "imap" : "imaps"); create.name = name;
Helper.connect(context, istore, account); create.level = 0;
char separator = istore.getDefaultFolder().getSeparator(); create.display = display;
create.hide = hide;
if (folder == null) { create.type = EntityFolder.USER;
Log.i(Helper.TAG, "Creating folder=" + name); create.unified = unified;
create.synchronize = synchronize;
IMAPFolder ifolder = (IMAPFolder) istore.getFolder(name); create.poll = poll;
if (ifolder.exists()) create.sync_days = sync_days;
throw new IllegalArgumentException(getString(R.string.title_folder_exists, name)); create.keep_days = keep_days;
ifolder.create(Folder.HOLDS_MESSAGES); db.folder().insertFolder(create);
} else {
EntityFolder create = new EntityFolder(); reload = (!folder.synchronize.equals(synchronize) || !folder.poll.equals(poll));
create.account = aid;
create.name = name;
create.level = EntityFolder.getLevel(separator, name);
create.display = display;
create.hide = hide;
create.type = EntityFolder.USER;
create.unified = unified;
create.synchronize = synchronize;
create.poll = poll;
create.sync_days = sync_days;
create.keep_days = keep_days;
db.folder().insertFolder(create);
} else {
Log.i(Helper.TAG, "Renaming folder=" + name);
IMAPFolder iold = (IMAPFolder) istore.getFolder(folder.name);
IMAPFolder ifolder = (IMAPFolder) istore.getFolder(name);
if (ifolder.exists())
throw new IllegalArgumentException(getString(R.string.title_folder_exists, name));
iold.renameTo(ifolder);
}
}
if (folder != null) {
reload = (!folder.name.equals(name) ||
!folder.synchronize.equals(synchronize) ||
!folder.poll.equals(poll));
Calendar cal_keep = Calendar.getInstance(); Calendar cal_keep = Calendar.getInstance();
cal_keep.add(Calendar.DAY_OF_MONTH, -keep_days); cal_keep.add(Calendar.DAY_OF_MONTH, -keep_days);
@ -213,30 +178,25 @@ public class FragmentFolder extends FragmentEx {
Log.i(Helper.TAG, "Updating folder=" + name); Log.i(Helper.TAG, "Updating folder=" + name);
db.folder().setFolderProperties(id, db.folder().setFolderProperties(id,
name, display, name, display, unified, hide,
hide,
synchronize, poll, synchronize, poll,
unified,
sync_days, keep_days); sync_days, keep_days);
db.message().deleteMessagesBefore(id, keep_time, true); db.message().deleteMessagesBefore(id, keep_time, true);
if (!synchronize) if (!synchronize)
db.folder().setFolderError(id, null); db.folder().setFolderError(id, null);
EntityOperation.sync(db, folder.id);
} }
db.setTransactionSuccessful(); db.setTransactionSuccessful();
} finally { } finally {
db.endTransaction(); db.endTransaction();
if (istore != null)
istore.close();
} }
if (folder == null || !folder.name.equals(name) || reload) if (reload)
ServiceSynchronize.reload(getContext(), "save folder"); ServiceSynchronize.reload(getContext(), "save folder");
else
EntityOperation.sync(db, folder.id);
return null; return null;
} }
@ -308,34 +268,11 @@ public class FragmentFolder extends FragmentEx {
new SimpleTask<Void>() { new SimpleTask<Void>() {
@Override @Override
protected Void onLoad(Context context, Bundle args) throws Throwable { protected Void onLoad(Context context, Bundle args) {
long id = args.getLong("id"); long id = args.getLong("id");
IMAPStore istore = null;
DB db = DB.getInstance(getContext()); DB db = DB.getInstance(getContext());
try { db.folder().setFolderTbd(id);
db.beginTransaction();
EntityFolder folder = db.folder().getFolder(id);
EntityAccount account = db.account().getAccount(folder.account);
Properties props = MessageHelper.getSessionProperties(account.auth_type, account.insecure);
Session isession = Session.getInstance(props, null);
istore = (IMAPStore) isession.getStore(account.starttls ? "imap" : "imaps");
Helper.connect(context, istore, account);
IMAPFolder ifolder = (IMAPFolder) istore.getFolder(folder.name);
ifolder.delete(false);
db.folder().deleteFolder(id);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
if (istore != null)
istore.close();
}
ServiceSynchronize.reload(getContext(), "delete folder"); ServiceSynchronize.reload(getContext(), "delete folder");
@ -374,7 +311,7 @@ public class FragmentFolder extends FragmentEx {
new SimpleTask<EntityFolder>() { new SimpleTask<EntityFolder>() {
@Override @Override
protected EntityFolder onLoad(Context context, Bundle args) throws Throwable { protected EntityFolder onLoad(Context context, Bundle args) {
long id = args.getLong("id"); long id = args.getLong("id");
return DB.getInstance(context).folder().getFolder(id); return DB.getInstance(context).folder().getFolder(id);
} }
@ -382,7 +319,7 @@ public class FragmentFolder extends FragmentEx {
@Override @Override
protected void onLoaded(Bundle args, EntityFolder folder) { protected void onLoaded(Bundle args, EntityFolder folder) {
if (savedInstanceState == null) { if (savedInstanceState == null) {
etRename.setText(folder == null ? null : folder.name); etName.setText(folder == null ? null : folder.name);
etDisplay.setText(folder == null ? null : (folder.display == null ? folder.name : folder.display)); etDisplay.setText(folder == null ? null : (folder.display == null ? folder.name : folder.display));
etDisplay.setHint(folder == null ? null : folder.name); etDisplay.setHint(folder == null ? null : folder.name);
cbHide.setChecked(folder == null ? false : folder.hide); cbHide.setChecked(folder == null ? false : folder.hide);
@ -396,7 +333,7 @@ public class FragmentFolder extends FragmentEx {
// Consider previous save as cancelled // Consider previous save as cancelled
pbWait.setVisibility(View.GONE); pbWait.setVisibility(View.GONE);
Helper.setViewsEnabled(view, true); Helper.setViewsEnabled(view, true);
etRename.setEnabled(folder == null || EntityFolder.USER.equals(folder.type)); etName.setEnabled(folder == null);
cbPoll.setEnabled(cbSynchronize.isChecked()); cbPoll.setEnabled(cbSynchronize.isChecked());
btnSave.setEnabled(true); btnSave.setEnabled(true);
} }

View File

@ -752,11 +752,12 @@ public class FragmentIdentity extends FragmentEx {
@Override @Override
protected Void onLoad(Context context, Bundle args) { protected Void onLoad(Context context, Bundle args) {
long id = args.getLong("id"); long id = args.getLong("id");
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
EntityIdentity identity = db.identity().getIdentity(id); db.identity().setIdentityTbd(id);
db.identity().deleteIdentity(id);
if (identity.synchronize) ServiceSynchronize.reload(getContext(), "delete identity");
ServiceSynchronize.reload(getContext(), "delete identity");
return null; return null;
} }

View File

@ -1796,7 +1796,13 @@ public class ServiceSynchronize extends LifecycleService {
List<String> names = new ArrayList<>(); List<String> names = new ArrayList<>();
for (EntityFolder folder : db.folder().getUserFolders(account.id)) for (EntityFolder folder : db.folder().getUserFolders(account.id))
names.add(folder.name); if (folder.tbd == null)
names.add(folder.name);
else {
IMAPFolder ifolder = (IMAPFolder) istore.getFolder(folder.name);
ifolder.delete(false);
db.folder().deleteFolder(folder.id);
}
Log.i(Helper.TAG, "Local folder count=" + names.size()); Log.i(Helper.TAG, "Local folder count=" + names.size());
Folder defaultFolder = istore.getDefaultFolder(); Folder defaultFolder = istore.getDefaultFolder();
@ -1835,10 +1841,11 @@ public class ServiceSynchronize extends LifecycleService {
} }
} }
Log.i(Helper.TAG, "Delete local folder=" + names.size()); Log.i(Helper.TAG, "Create remote count=" + names.size());
for (String name : names) { for (String name : names) {
db.folder().deleteFolder(account.id, name); Log.i(Helper.TAG, name + " create");
Log.i(Helper.TAG, name + " deleted"); IMAPFolder ifolder = (IMAPFolder) istore.getFolder(name);
ifolder.create(Folder.HOLDS_MESSAGES);
} }
db.setTransactionSuccessful(); db.setTransactionSuccessful();
@ -2318,9 +2325,6 @@ public class ServiceSynchronize extends LifecycleService {
private boolean started = false; private boolean started = false;
private int queued = 0; private int queued = 0;
private long lastLost = 0; private long lastLost = 0;
PowerManager pm = getSystemService(PowerManager.class);
PowerManager.WakeLock wl = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID + ":manage");
private ExecutorService queue = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory); private ExecutorService queue = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory);
@Override @Override
@ -2579,6 +2583,10 @@ public class ServiceSynchronize extends LifecycleService {
queued++; queued++;
queue.submit(new Runnable() { queue.submit(new Runnable() {
PowerManager pm = getSystemService(PowerManager.class);
PowerManager.WakeLock wl = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID + ":manage");
@Override @Override
public void run() { public void run() {
try { try {
@ -2590,6 +2598,12 @@ public class ServiceSynchronize extends LifecycleService {
if (doStop) if (doStop)
stop(); stop();
DB db = DB.getInstance(ServiceSynchronize.this);
int accounts = db.account().deleteAccountsTbd();
int identities = db.identity().deleteIdentitiesTbd();
if (accounts > 0 || identities > 0)
Log.i(Helper.TAG, "Deleted accounts=" + accounts + " identities=" + identities);
if (doStart) if (doStart)
start(); start();

View File

@ -24,7 +24,7 @@
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<EditText <EditText
android:id="@+id/etRename" android:id="@+id/etName"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="text" android:inputType="text"
@ -41,7 +41,7 @@
android:text="@string/title_display_name" android:text="@string/title_display_name"
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/etRename" /> app:layout_constraintTop_toBottomOf="@id/etName" />
<EditText <EditText
android:id="@+id/etDisplay" android:id="@+id/etDisplay"

View File

@ -1,135 +1,142 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<View <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/vwColor" android:id="@+id/clItem"
android:layout_width="6dp"
android:layout_height="0dp"
android:background="@color/colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/marginTop"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="3dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/ivPrimary"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:src="@drawable/baseline_star_24"
android:visibility="visible"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/marginTop" />
<TextView
android:id="@+id/tvName"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="6dp" android:background="?attr/drawableItemBackground">
android:layout_marginEnd="6dp"
android:ellipsize="end"
android:maxLines="1"
android:text="Name"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintBottom_toBottomOf="@id/ivPrimary"
app:layout_constraintEnd_toStartOf="@+id/ivSync"
app:layout_constraintStart_toEndOf="@id/ivPrimary"
app:layout_constraintTop_toTopOf="@id/ivPrimary" />
<ImageView <View
android:id="@+id/ivSync" android:id="@+id/vwColor"
android:layout_width="24dp" android:layout_width="6dp"
android:layout_height="24dp" android:layout_height="0dp"
android:layout_marginEnd="6dp" android:background="@color/colorAccent"
android:src="@drawable/baseline_sync_24" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@id/marginTop" />
<TextView <View
android:id="@+id/tvUser" android:id="@+id/marginTop"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="3dp"
android:layout_marginStart="6dp" app:layout_constraintStart_toStartOf="parent"
android:layout_marginEnd="6dp" app:layout_constraintTop_toTopOf="parent" />
android:text="user"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintEnd_toStartOf="@+id/ivSync"
app:layout_constraintStart_toEndOf="@+id/ivPrimary"
app:layout_constraintTop_toBottomOf="@id/ivSync" />
<ImageView <ImageView
android:id="@+id/ivState" android:id="@+id/ivPrimary"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginStart="6dp" android:layout_marginStart="6dp"
android:layout_marginEnd="6dp" android:layout_marginEnd="6dp"
android:src="@drawable/baseline_cloud_off_24" android:src="@drawable/baseline_star_24"
app:layout_constraintStart_toEndOf="@id/vwColor" android:visibility="visible"
app:layout_constraintTop_toBottomOf="@id/tvUser" /> app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/marginTop" />
<TextView <TextView
android:id="@+id/tvHost" android:id="@+id/tvName"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="6dp" android:layout_marginStart="6dp"
android:layout_marginEnd="6dp" android:layout_marginEnd="6dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:text="host" android:text="Name"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintBottom_toBottomOf="@id/ivState" app:layout_constraintBottom_toBottomOf="@id/ivPrimary"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@+id/ivSync"
app:layout_constraintStart_toEndOf="@id/ivState" app:layout_constraintStart_toEndOf="@id/ivPrimary"
app:layout_constraintTop_toTopOf="@id/ivState" /> app:layout_constraintTop_toTopOf="@id/ivPrimary" />
<TextView <ImageView
android:id="@+id/tvLast" android:id="@+id/ivSync"
android:layout_width="0dp" android:layout_width="24dp"
android:layout_height="wrap_content" android:layout_height="24dp"
android:layout_marginStart="6dp" android:layout_marginEnd="6dp"
android:layout_marginEnd="6dp" android:src="@drawable/baseline_sync_24"
android:ellipsize="end" app:layout_constraintEnd_toEndOf="parent"
android:maxLines="1" app:layout_constraintTop_toBottomOf="@id/marginTop" />
android:text="Last connected time"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ivState"
app:layout_constraintTop_toBottomOf="@id/tvHost" />
<TextView <TextView
android:id="@+id/tvError" android:id="@+id/tvUser"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="6dp" android:layout_marginStart="6dp"
android:layout_marginEnd="6dp" android:layout_marginEnd="6dp"
android:text="error" android:text="user"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="?attr/colorWarning" app:layout_constraintEnd_toStartOf="@+id/ivSync"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/ivPrimary"
app:layout_constraintStart_toEndOf="@id/vwColor" app:layout_constraintTop_toBottomOf="@id/ivSync" />
app:layout_constraintTop_toBottomOf="@id/tvLast" />
<View <ImageView
android:id="@+id/marginBottom" android:id="@+id/ivState"
android:layout_width="match_parent" android:layout_width="24dp"
android:layout_height="3dp" android:layout_height="24dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="6dp"
app:layout_constraintTop_toBottomOf="@id/tvError" /> android:layout_marginEnd="6dp"
android:src="@drawable/baseline_cloud_off_24"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvUser" />
<View <TextView
android:id="@+id/vSeparator" android:id="@+id/tvHost"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="1dp" android:layout_height="wrap_content"
android:background="?attr/colorSeparator" android:layout_marginStart="6dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginEnd="6dp"
app:layout_constraintTop_toBottomOf="@id/marginBottom" /> android:ellipsize="end"
</androidx.constraintlayout.widget.ConstraintLayout> android:maxLines="1"
android:text="host"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintBottom_toBottomOf="@id/ivState"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ivState"
app:layout_constraintTop_toTopOf="@id/ivState" />
<TextView
android:id="@+id/tvLast"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:ellipsize="end"
android:maxLines="1"
android:text="Last connected time"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/ivState"
app:layout_constraintTop_toBottomOf="@id/tvHost" />
<TextView
android:id="@+id/tvError"
android:layout_width="0dp"
android:layout_height="wrap_content"
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_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvLast" />
<View
android:id="@+id/marginBottom"
android:layout_width="match_parent"
android:layout_height="3dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvError" />
<View
android:id="@+id/vSeparator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/colorSeparator"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/marginBottom" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -7,7 +7,8 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/clItem" android:id="@+id/clItem"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:background="?attr/drawableItemBackground">
<View <View
android:id="@+id/vwColor" android:id="@+id/vwColor"

View File

@ -1,123 +1,130 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<View <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/vwColor" android:id="@+id/clItem"
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_marginStart="6dp"
android:layout_marginTop="3dp"
android:layout_marginEnd="6dp"
android:src="@drawable/baseline_star_24"
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_marginStart="6dp"
android:layout_marginEnd="6dp"
android:ellipsize="end"
android:maxLines="1"
android:text="Name"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintBottom_toBottomOf="@+id/ivSync"
app:layout_constraintEnd_toStartOf="@+id/ivSync"
app:layout_constraintStart_toEndOf="@+id/ivPrimary"
app:layout_constraintTop_toTopOf="@+id/ivSync" />
<ImageView
android:id="@+id/ivSync"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="3dp"
android:layout_marginEnd="6dp"
android:src="@drawable/baseline_sync_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvUser"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:text="user"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintEnd_toStartOf="@+id/ivSync"
app:layout_constraintStart_toEndOf="@+id/ivPrimary"
app:layout_constraintTop_toBottomOf="@id/ivSync" />
<ImageView
android:id="@+id/ivState"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:src="@drawable/baseline_cloud_off_24"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvUser" />
<TextView
android:id="@+id/tvHost"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:ellipsize="end"
android:maxLines="1"
android:text="host"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintBottom_toBottomOf="@id/ivState"
app:layout_constraintEnd_toStartOf="@+id/tvAccount"
app:layout_constraintStart_toEndOf="@+id/ivPrimary"
app:layout_constraintTop_toTopOf="@id/ivState" />
<TextView
android:id="@+id/tvAccount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:text="account"
android:textAlignment="textEnd"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintBottom_toBottomOf="@id/ivState"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tvHost"
app:layout_constraintTop_toTopOf="@id/ivState" />
<TextView
android:id="@+id/tvError"
android:layout_width="0dp"
android:layout_height="wrap_content"
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_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvHost" />
<View
android:id="@+id/vSeparator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="wrap_content"
android:layout_marginTop="6dp" android:background="?attr/drawableItemBackground">
android:layout_marginBottom="6dp"
android:background="?attr/colorSeparator" <View
app:layout_constraintStart_toStartOf="parent" android:id="@+id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvError" /> android:layout_width="6dp"
</androidx.constraintlayout.widget.ConstraintLayout> 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_marginStart="6dp"
android:layout_marginTop="3dp"
android:layout_marginEnd="6dp"
android:src="@drawable/baseline_star_24"
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_marginStart="6dp"
android:layout_marginEnd="6dp"
android:ellipsize="end"
android:maxLines="1"
android:text="Name"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintBottom_toBottomOf="@+id/ivSync"
app:layout_constraintEnd_toStartOf="@+id/ivSync"
app:layout_constraintStart_toEndOf="@+id/ivPrimary"
app:layout_constraintTop_toTopOf="@+id/ivSync" />
<ImageView
android:id="@+id/ivSync"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="3dp"
android:layout_marginEnd="6dp"
android:src="@drawable/baseline_sync_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvUser"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:text="user"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintEnd_toStartOf="@+id/ivSync"
app:layout_constraintStart_toEndOf="@+id/ivPrimary"
app:layout_constraintTop_toBottomOf="@id/ivSync" />
<ImageView
android:id="@+id/ivState"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:src="@drawable/baseline_cloud_off_24"
app:layout_constraintStart_toEndOf="@id/vwColor"
app:layout_constraintTop_toBottomOf="@id/tvUser" />
<TextView
android:id="@+id/tvHost"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:ellipsize="end"
android:maxLines="1"
android:text="host"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintBottom_toBottomOf="@id/ivState"
app:layout_constraintEnd_toStartOf="@+id/tvAccount"
app:layout_constraintStart_toEndOf="@+id/ivPrimary"
app:layout_constraintTop_toTopOf="@id/ivState" />
<TextView
android:id="@+id/tvAccount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:text="account"
android:textAlignment="textEnd"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintBottom_toBottomOf="@id/ivState"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tvHost"
app:layout_constraintTop_toTopOf="@id/ivState" />
<TextView
android:id="@+id/tvError"
android:layout_width="0dp"
android:layout_height="wrap_content"
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_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_marginTop="6dp"
android:layout_marginBottom="6dp"
android:background="?attr/colorSeparator"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvError" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>