Added answer stats

This commit is contained in:
M66B 2021-05-13 17:25:34 +02:00
parent 0563c8808b
commit 044f62bd6a
8 changed files with 2574 additions and 14 deletions

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,8 @@ import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListUpdateCallback;
import androidx.recyclerview.widget.RecyclerView;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
@ -57,6 +59,9 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
private LifecycleOwner owner;
private LayoutInflater inflater;
private DateFormat DF;
private NumberFormat NF = NumberFormat.getNumberInstance();
private String search = null;
private List<EntityAnswer> all = 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 ivFavorite;
private ImageView ivReceipt;
private TextView tvLastApplied;
private TextView tvApplied;
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);
ivFavorite = itemView.findViewById(R.id.ivFavorite);
ivReceipt = itemView.findViewById(R.id.ivReceipt);
tvLastApplied = itemView.findViewById(R.id.tvLastApplied);
tvApplied = itemView.findViewById(R.id.tvApplied);
}
private void wire() {
@ -102,6 +111,8 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
ivStandard.setVisibility(answer.standard ? View.VISIBLE : View.GONE);
ivFavorite.setVisibility(answer.favorite ? 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
@ -139,7 +150,8 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
.setCheckable(true).setChecked(answer.favorite);
popupMenu.getMenu().add(Menu.NONE, R.string.title_answer_hide, 3, R.string.title_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() {
@Override
@ -154,6 +166,9 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
} else if (itemId == R.string.title_answer_hide) {
onActionHide(!item.isChecked());
return true;
} else if (itemId == R.string.title_reset) {
onActionReset();
return true;
} else if (itemId == R.string.title_copy) {
onActionCopy();
return true;
@ -215,6 +230,28 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
}.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() {
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(
@ -237,6 +274,8 @@ public class AdapterAnswer extends RecyclerView.Adapter<AdapterAnswer.ViewHolder
this.owner = parentFragment.getViewLifecycleOwner();
this.inflater = LayoutInflater.from(context);
this.DF = Helper.getDateTimeInstance(this.context);
setHasStableIds(true);
owner.getLifecycle().addObserver(new LifecycleObserver() {

View File

@ -65,7 +65,7 @@ import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD;
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
version = 196,
version = 197,
entities = {
EntityIdentity.class,
EntityAccount.class,
@ -2013,6 +2013,13 @@ public abstract class DB extends RoomDatabase {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
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");
}
});
}

View File

@ -78,6 +78,16 @@ public interface DaoAnswer {
@Query("UPDATE answer SET receipt = 0 WHERE NOT (receipt IS 0)")
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")
void deleteAnswer(long id);
}

View File

@ -59,6 +59,9 @@ public class EntityAnswer implements Serializable {
public Boolean hide;
@NonNull
public String text;
@NonNull
public Integer applied = 0;
public Long last_applied;
String getText(Address[] address) {
return replacePlaceholders(text, address);
@ -128,6 +131,8 @@ public class EntityAnswer implements Serializable {
json.put("favorite", favorite);
json.put("hide", hide);
json.put("text", text);
json.put("applied", applied);
json.put("last_applied", last_applied);
return json;
}
@ -141,6 +146,9 @@ public class EntityAnswer implements Serializable {
answer.favorite = json.optBoolean("favorite");
answer.hide = json.optBoolean("hide");
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;
}
@ -154,8 +162,9 @@ public class EntityAnswer implements Serializable {
this.receipt.equals(other.receipt) &&
this.favorite.equals(other.favorite) &&
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;
}

View File

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

View File

@ -1796,8 +1796,28 @@ public class FragmentCompose extends FragmentBase {
}
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)
etSubject.setText(answer.name);
@ -1841,13 +1861,15 @@ public class FragmentCompose extends FragmentBase {
if (pos >= 0)
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().getAnswer(answer));
if (a != null) {
db.answer().applyAnswer(a.id, new Date().getTime());
data.draft.subject = a.name;
Document d = JsoupEx.parse(a.getText(null));
document.body().append(d.body().html());
@ -3978,6 +4001,7 @@ public class FragmentCompose extends FragmentBase {
if (receipt == null)
texts = Helper.getStrings(context, ref.language, R.string.title_receipt_text);
else {
db.answer().applyAnswer(receipt.id, new Date().getTime());
texts = new String[0];
Document d = JsoupEx.parse(receipt.getText(null));
document.body().append(d.body().html());
@ -4023,6 +4047,7 @@ public class FragmentCompose extends FragmentBase {
a = db.answer().getAnswer(answer);
if (a != null) {
db.answer().applyAnswer(a.id, new Date().getTime());
Document d = JsoupEx.parse(a.getText(data.draft.to));
document.body().append(d.body().html());
}

View File

@ -41,12 +41,31 @@
app:layout_constraintStart_toStartOf="parent"
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
android:id="@+id/ivReceipt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/ivStandard"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/twotone_playlist_add_check_24" />
@ -56,7 +75,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/ivFavorite"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/twotone_quickreply_24" />
@ -66,10 +84,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/twotone_star_24" />
</androidx.constraintlayout.widget.ConstraintLayout>
</eu.faircode.email.ViewCardOptional>
</FrameLayout>
</FrameLayout>