Added answer stats

pull/198/head
M66B 4 years ago
parent 0563c8808b
commit 044f62bd6a

File diff suppressed because it is too large Load Diff

@ -47,6 +47,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.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -57,6 +59,9 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
private LifecycleOwner owner; private LifecycleOwner owner;
private LayoutInflater inflater; private LayoutInflater inflater;
private DateFormat DF;
private NumberFormat NF = NumberFormat.getNumberInstance();
private String search = null; private String search = null;
private List<EntityAnswer> all = new ArrayList<>(); private List<EntityAnswer> all = new ArrayList<>();
private List<EntityAnswer> selected = new ArrayList<>(); private List<EntityAnswer> selected = new ArrayList<>();
@ -70,6 +75,8 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
private ImageView ivStandard; private ImageView ivStandard;
private ImageView ivFavorite; private ImageView ivFavorite;
private ImageView ivReceipt; private ImageView ivReceipt;
private TextView tvLastApplied;
private TextView tvApplied;
private TwoStateOwner powner = new TwoStateOwner(owner, "RulePopup"); private TwoStateOwner powner = new TwoStateOwner(owner, "RulePopup");
@ -82,6 +89,8 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
ivStandard = itemView.findViewById(R.id.ivStandard); ivStandard = itemView.findViewById(R.id.ivStandard);
ivFavorite = itemView.findViewById(R.id.ivFavorite); ivFavorite = itemView.findViewById(R.id.ivFavorite);
ivReceipt = itemView.findViewById(R.id.ivReceipt); ivReceipt = itemView.findViewById(R.id.ivReceipt);
tvLastApplied = itemView.findViewById(R.id.tvLastApplied);
tvApplied = itemView.findViewById(R.id.tvApplied);
} }
private void wire() { private void wire() {
@ -102,6 +111,8 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
ivStandard.setVisibility(answer.standard ? View.VISIBLE : View.GONE); ivStandard.setVisibility(answer.standard ? View.VISIBLE : View.GONE);
ivFavorite.setVisibility(answer.favorite ? View.VISIBLE : View.GONE); ivFavorite.setVisibility(answer.favorite ? View.VISIBLE : View.GONE);
ivReceipt.setVisibility(answer.receipt ? View.VISIBLE : View.GONE); ivReceipt.setVisibility(answer.receipt ? View.VISIBLE : View.GONE);
tvLastApplied.setText(answer.last_applied == null ? null : DF.format(answer.last_applied));
tvApplied.setText(NF.format(answer.applied));
} }
@Override @Override
@ -139,7 +150,8 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
.setCheckable(true).setChecked(answer.favorite); .setCheckable(true).setChecked(answer.favorite);
popupMenu.getMenu().add(Menu.NONE, R.string.title_answer_hide, 3, R.string.title_answer_hide) popupMenu.getMenu().add(Menu.NONE, R.string.title_answer_hide, 3, R.string.title_answer_hide)
.setCheckable(true).setChecked(answer.hide); .setCheckable(true).setChecked(answer.hide);
popupMenu.getMenu().add(Menu.NONE, R.string.title_copy, 4, R.string.title_copy); popupMenu.getMenu().add(Menu.NONE, R.string.title_reset, 4, R.string.title_reset);
popupMenu.getMenu().add(Menu.NONE, R.string.title_copy, 5, R.string.title_copy);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override @Override
@ -154,6 +166,9 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
} else if (itemId == R.string.title_answer_hide) { } else if (itemId == R.string.title_answer_hide) {
onActionHide(!item.isChecked()); onActionHide(!item.isChecked());
return true; return true;
} else if (itemId == R.string.title_reset) {
onActionReset();
return true;
} else if (itemId == R.string.title_copy) { } else if (itemId == R.string.title_copy) {
onActionCopy(); onActionCopy();
return true; return true;
@ -215,6 +230,28 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
}.execute(context, owner, args, "answer:hide"); }.execute(context, owner, args, "answer:hide");
} }
private void onActionReset() {
Bundle args = new Bundle();
args.putLong("id", answer.id);
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
long id = args.getLong("id");
DB db = DB.getInstance(context);
db.answer().resetAnswer(id);
return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
Log.unexpectedError(parentFragment.getParentFragmentManager(), ex);
}
}.execute(context, owner, args, "rule:execute");
}
private void onActionCopy() { private void onActionCopy() {
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast( lbm.sendBroadcast(
@ -237,6 +274,8 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
this.owner = parentFragment.getViewLifecycleOwner(); this.owner = parentFragment.getViewLifecycleOwner();
this.inflater = LayoutInflater.from(context); this.inflater = LayoutInflater.from(context);
this.DF = Helper.getDateTimeInstance(this.context);
setHasStableIds(true); setHasStableIds(true);
owner.getLifecycle().addObserver(new LifecycleObserver() { owner.getLifecycle().addObserver(new LifecycleObserver() {

@ -65,7 +65,7 @@ import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD;
// https://developer.android.com/topic/libraries/architecture/room.html // https://developer.android.com/topic/libraries/architecture/room.html
@Database( @Database(
version = 196, version = 197,
entities = { entities = {
EntityIdentity.class, EntityIdentity.class,
EntityAccount.class, EntityAccount.class,
@ -2013,6 +2013,13 @@ public abstract class DB extends RoomDatabase {
Log.i("DB migration from version " + startVersion + " to " + endVersion); Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `identity` ADD COLUMN `internal` TEXT"); db.execSQL("ALTER TABLE `identity` ADD COLUMN `internal` TEXT");
} }
}).addMigrations(new Migration(196, 197) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `answer` ADD COLUMN `applied` INTEGER NOT NULL DEFAULT 0");
db.execSQL("ALTER TABLE `answer` ADD COLUMN `last_applied` INTEGER");
}
}); });
} }

@ -78,6 +78,16 @@ public interface DaoAnswer {
@Query("UPDATE answer SET receipt = 0 WHERE NOT (receipt IS 0)") @Query("UPDATE answer SET receipt = 0 WHERE NOT (receipt IS 0)")
void resetReceipt(); void resetReceipt();
@Query("UPDATE answer" +
" SET applied = applied + 1, last_applied = :time" +
" WHERE id = :id")
int applyAnswer(long id, long time);
@Query("UPDATE answer" +
" SET applied = 0, last_applied = NULL" +
" WHERE id = :id AND NOT (applied IS 0)")
int resetAnswer(long id);
@Query("DELETE FROM answer WHERE id = :id") @Query("DELETE FROM answer WHERE id = :id")
void deleteAnswer(long id); void deleteAnswer(long id);
} }

@ -59,6 +59,9 @@ public class EntityAnswer implements Serializable {
public Boolean hide; public Boolean hide;
@NonNull @NonNull
public String text; public String text;
@NonNull
public Integer applied = 0;
public Long last_applied;
String getText(Address[] address) { String getText(Address[] address) {
return replacePlaceholders(text, address); return replacePlaceholders(text, address);
@ -128,6 +131,8 @@ public class EntityAnswer implements Serializable {
json.put("favorite", favorite); json.put("favorite", favorite);
json.put("hide", hide); json.put("hide", hide);
json.put("text", text); json.put("text", text);
json.put("applied", applied);
json.put("last_applied", last_applied);
return json; return json;
} }
@ -141,6 +146,9 @@ public class EntityAnswer implements Serializable {
answer.favorite = json.optBoolean("favorite"); answer.favorite = json.optBoolean("favorite");
answer.hide = json.optBoolean("hide"); answer.hide = json.optBoolean("hide");
answer.text = json.getString("text"); answer.text = json.getString("text");
answer.applied = json.optInt("applied", 0);
if (json.has("last_applied") && !json.isNull("last_applied"))
answer.last_applied = json.getLong("last_applied");
return answer; return answer;
} }
@ -154,8 +162,9 @@ public class EntityAnswer implements Serializable {
this.receipt.equals(other.receipt) && this.receipt.equals(other.receipt) &&
this.favorite.equals(other.favorite) && this.favorite.equals(other.favorite) &&
this.hide.equals(other.hide) && this.hide.equals(other.hide) &&
this.text.equals(other.text) this.text.equals(other.text) &&
); this.applied.equals(other.applied) &&
Objects.equals(this.last_applied, other.last_applied));
} }
return false; return false;
} }

@ -183,6 +183,11 @@ public class FragmentAnswer extends FragmentBase {
@Override @Override
protected void onExecuted(Bundle args, EntityAnswer answer) { protected void onExecuted(Bundle args, EntityAnswer answer) {
if (copy > 0 && answer != null) {
answer.applied = 0;
answer.last_applied = null;
}
if (savedInstanceState == null) { if (savedInstanceState == null) {
Bundle a = getArguments(); Bundle a = getArguments();
if (a == null) if (a == null)

@ -1796,8 +1796,28 @@ public class FragmentCompose extends FragmentBase {
} }
long id = intent.getLongExtra("id", -1); long id = intent.getLongExtra("id", -1);
for (EntityAnswer answer : answers)
if (answer.id.equals(id)) { Bundle args = new Bundle();
args.putLong("id", id);
new SimpleTask<EntityAnswer>() {
@Override
protected EntityAnswer onExecute(Context context, Bundle args) throws Throwable {
long id = args.getLong("id");
DB db = DB.getInstance(context);
EntityAnswer answer = db.answer().getAnswer(id);
if (answer != null)
db.answer().applyAnswer(answer.id, new Date().getTime());
return answer;
}
@Override
protected void onExecuted(Bundle args, EntityAnswer answer) {
if (answer == null)
return;
if (etSubject.getText().length() == 0) if (etSubject.getText().length() == 0)
etSubject.setText(answer.name); etSubject.setText(answer.name);
@ -1841,13 +1861,15 @@ public class FragmentCompose extends FragmentBase {
if (pos >= 0) if (pos >= 0)
etBody.setSelection(pos); etBody.setSelection(pos);
} }
return true;
} }
Log.e("Answer=" + id + " count=" + answers.size() + " not found"); @Override
protected void onException(Bundle args, Throwable ex) {
Log.unexpectedError(getParentFragmentManager(), ex);
}
}.execute(FragmentCompose.this, args, "compose:answer");
return false; return true;
} }
}); });
@ -3820,6 +3842,7 @@ public class FragmentCompose extends FragmentBase {
? db.answer().getStandardAnswer() ? db.answer().getStandardAnswer()
: db.answer().getAnswer(answer)); : db.answer().getAnswer(answer));
if (a != null) { if (a != null) {
db.answer().applyAnswer(a.id, new Date().getTime());
data.draft.subject = a.name; data.draft.subject = a.name;
Document d = JsoupEx.parse(a.getText(null)); Document d = JsoupEx.parse(a.getText(null));
document.body().append(d.body().html()); document.body().append(d.body().html());
@ -3978,6 +4001,7 @@ public class FragmentCompose extends FragmentBase {
if (receipt == null) if (receipt == null)
texts = Helper.getStrings(context, ref.language, R.string.title_receipt_text); texts = Helper.getStrings(context, ref.language, R.string.title_receipt_text);
else { else {
db.answer().applyAnswer(receipt.id, new Date().getTime());
texts = new String[0]; texts = new String[0];
Document d = JsoupEx.parse(receipt.getText(null)); Document d = JsoupEx.parse(receipt.getText(null));
document.body().append(d.body().html()); document.body().append(d.body().html());
@ -4023,6 +4047,7 @@ public class FragmentCompose extends FragmentBase {
a = db.answer().getAnswer(answer); a = db.answer().getAnswer(answer);
if (a != null) { if (a != null) {
db.answer().applyAnswer(a.id, new Date().getTime());
Document d = JsoupEx.parse(a.getText(data.draft.to)); Document d = JsoupEx.parse(a.getText(data.draft.to));
document.body().append(d.body().html()); document.body().append(d.body().html());
} }

@ -41,12 +41,31 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvName" /> app:layout_constraintTop_toBottomOf="@+id/tvName" />
<eu.faircode.email.FixedTextView
android:id="@+id/tvLastApplied"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:text="Jan 1, 12:34"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintEnd_toStartOf="@id/tvApplied"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvGroup" />
<eu.faircode.email.FixedTextView
android:id="@+id/tvApplied"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="123"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvGroup" />
<ImageView <ImageView
android:id="@+id/ivReceipt" android:id="@+id/ivReceipt"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="12dp" android:paddingStart="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/ivStandard" app:layout_constraintEnd_toStartOf="@id/ivStandard"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/twotone_playlist_add_check_24" /> app:srcCompat="@drawable/twotone_playlist_add_check_24" />
@ -56,7 +75,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="12dp" android:paddingStart="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/ivFavorite" app:layout_constraintEnd_toStartOf="@id/ivFavorite"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/twotone_quickreply_24" /> app:srcCompat="@drawable/twotone_quickreply_24" />
@ -66,7 +84,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="12dp" android:paddingStart="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/twotone_star_24" /> app:srcCompat="@drawable/twotone_star_24" />

Loading…
Cancel
Save