diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index 879e0c2223..11912d81bd 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -143,6 +143,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB static final String ACTION_EDIT_ANSWER = BuildConfig.APPLICATION_ID + ".EDIT_ANSWER"; static final String ACTION_EDIT_RULES = BuildConfig.APPLICATION_ID + ".EDIT_RULES"; static final String ACTION_EDIT_RULE = BuildConfig.APPLICATION_ID + ".EDIT_RULE"; + static final String ACTION_VIEW_RULE_LOG = BuildConfig.APPLICATION_ID + ".RULE_LOG"; static final String ACTION_STORE_ATTACHMENT = BuildConfig.APPLICATION_ID + ".STORE_ATTACHMENT"; static final String ACTION_STORE_ATTACHMENTS = BuildConfig.APPLICATION_ID + ".STORE_ATTACHMENTS"; static final String ACTION_COLOR = BuildConfig.APPLICATION_ID + ".COLOR"; @@ -546,6 +547,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB iff.addAction(ACTION_EDIT_ANSWER); iff.addAction(ACTION_EDIT_RULES); iff.addAction(ACTION_EDIT_RULE); + iff.addAction(ACTION_VIEW_RULE_LOG); iff.addAction(ACTION_STORE_ATTACHMENT); iff.addAction(ACTION_STORE_ATTACHMENTS); iff.addAction(ACTION_COLOR); @@ -1034,6 +1036,8 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB onEditRules(intent); else if (ACTION_EDIT_RULE.equals(action)) onEditRule(intent); + else if (ACTION_VIEW_RULE_LOG.equals(action)) + onRuleLog(intent); else if (ACTION_STORE_ATTACHMENT.equals(action)) onStoreAttachment(intent); else if (ACTION_STORE_ATTACHMENTS.equals(action)) @@ -1158,6 +1162,14 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB fragmentTransaction.commit(); } + private void onRuleLog(Intent intent) { + FragmentRuleLogs fragment = new FragmentRuleLogs(); + fragment.setArguments(intent.getExtras()); + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("rule_log"); + fragmentTransaction.commit(); + } + private void onStoreAttachment(Intent intent) { attachment = intent.getLongExtra("id", -1); Intent create = new Intent(Intent.ACTION_CREATE_DOCUMENT); diff --git a/app/src/main/java/eu/faircode/email/AdapterRule.java b/app/src/main/java/eu/faircode/email/AdapterRule.java index 18a4b6530e..f91976aa8a 100644 --- a/app/src/main/java/eu/faircode/email/AdapterRule.java +++ b/app/src/main/java/eu/faircode/email/AdapterRule.java @@ -159,6 +159,7 @@ public class AdapterRule extends RecyclerView.Adapter { popupMenu.getMenu().add(Menu.NONE, R.string.title_rule_enabled, 1, R.string.title_rule_enabled) .setCheckable(true).setChecked(rule.enabled); + popupMenu.getMenu().add(Menu.NONE, R.string.title_log, 2, R.string.title_log); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override @@ -167,6 +168,8 @@ public class AdapterRule extends RecyclerView.Adapter { case R.string.title_rule_enabled: onActionEnabled(!item.isChecked()); return true; + case R.string.title_log: + onActionLog(); default: return false; } @@ -195,6 +198,14 @@ public class AdapterRule extends RecyclerView.Adapter { } }.execute(context, owner, args, "rule:enable"); } + + private void onActionLog() { + Intent log = new Intent(ActivityView.ACTION_VIEW_RULE_LOG); + log.putExtra("rule", rule.id); + + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); + lbm.sendBroadcast(log); + } }); popupMenu.show(); diff --git a/app/src/main/java/eu/faircode/email/AdapterRuleLog.java b/app/src/main/java/eu/faircode/email/AdapterRuleLog.java new file mode 100644 index 0000000000..b7db477c57 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/AdapterRuleLog.java @@ -0,0 +1,189 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; +import android.content.Intent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.ListUpdateCallback; +import androidx.recyclerview.widget.RecyclerView; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +public class AdapterRuleLog extends RecyclerView.Adapter { + private Context context; + private LifecycleOwner owner; + private LayoutInflater inflater; + + private List items = new ArrayList<>(); + + private DateFormat DF = SimpleDateFormat.getDateTimeInstance(); + + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + private View view; + private TextView tvTime; + private TextView tvSubject; + + ViewHolder(View itemView) { + super(itemView); + + view = itemView.findViewById(R.id.clItem); + tvTime = itemView.findViewById(R.id.tvTime); + tvSubject = itemView.findViewById(R.id.tvSubject); + } + + private void wire() { + view.setOnClickListener(this); + } + + private void unwire() { + view.setOnClickListener(null); + } + + private void bindTo(TupleRuleLogEx log) { + tvTime.setText(DF.format(log.time)); + tvSubject.setText(log.subject); + } + + @Override + public void onClick(View v) { + int pos = getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) + return; + + TupleRuleLogEx rule = items.get(pos); + + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); + lbm.sendBroadcast( + new Intent(ActivityView.ACTION_VIEW_THREAD) + .putExtra("account", rule.account) + .putExtra("thread", rule.thread) + .putExtra("id", rule.message) + .putExtra("found", false)); + } + } + + AdapterRuleLog(Context context, LifecycleOwner owner) { + this.context = context; + this.owner = owner; + this.inflater = LayoutInflater.from(context); + setHasStableIds(true); + } + + public void set(@NonNull List logs) { + Log.i("Set logs=" + logs.size()); + + DiffUtil.DiffResult diff = DiffUtil.calculateDiff(new DiffCallback(items, logs), false); + + items = logs; + + diff.dispatchUpdatesTo(new ListUpdateCallback() { + @Override + public void onInserted(int position, int count) { + Log.i("Inserted @" + position + " #" + count); + } + + @Override + public void onRemoved(int position, int count) { + Log.i("Removed @" + position + " #" + count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + Log.i("Moved " + fromPosition + ">" + toPosition); + } + + @Override + public void onChanged(int position, int count, Object payload) { + Log.i("Changed @" + position + " #" + count); + } + }); + diff.dispatchUpdatesTo(this); + } + + private class DiffCallback extends DiffUtil.Callback { + private List prev = new ArrayList<>(); + private List next = new ArrayList<>(); + + DiffCallback(List prev, List next) { + this.prev.addAll(prev); + this.next.addAll(next); + } + + @Override + public int getOldListSize() { + return prev.size(); + } + + @Override + public int getNewListSize() { + return next.size(); + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + TupleRuleLogEx r1 = prev.get(oldItemPosition); + TupleRuleLogEx r2 = next.get(newItemPosition); + return r1.id.equals(r2.id); + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + TupleRuleLogEx r1 = prev.get(oldItemPosition); + TupleRuleLogEx r2 = next.get(newItemPosition); + return r1.equals(r2); + } + } + + @Override + public long getItemId(int position) { + return items.get(position).id; + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(inflater.inflate(R.layout.item_rule_log, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + holder.unwire(); + TupleRuleLogEx rule = items.get(position); + holder.bindTo(rule); + holder.wire(); + } +} diff --git a/app/src/main/java/eu/faircode/email/DaoRuleLog.java b/app/src/main/java/eu/faircode/email/DaoRuleLog.java index 81e1254bf1..2cbc1c17b5 100644 --- a/app/src/main/java/eu/faircode/email/DaoRuleLog.java +++ b/app/src/main/java/eu/faircode/email/DaoRuleLog.java @@ -19,11 +19,20 @@ package eu.faircode.email; Copyright 2018-2019 by Marcel Bokhorst (M66B) */ +import androidx.lifecycle.LiveData; import androidx.room.Dao; import androidx.room.Insert; +import androidx.room.Query; + +import java.util.List; @Dao public interface DaoRuleLog { + @Query("SELECT rule_log.*, message.account, message.thread, message.subject FROM rule_log" + + " JOIN message ON message.id = rule_log.message" + + " WHERE rule = :rule" + + " ORDER by rule_log.time") + LiveData> liveRuleLogs(long rule); @Insert long insertRuleLog(EntityRuleLog log); diff --git a/app/src/main/java/eu/faircode/email/FragmentRuleLogs.java b/app/src/main/java/eu/faircode/email/FragmentRuleLogs.java new file mode 100644 index 0000000000..382106f238 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentRuleLogs.java @@ -0,0 +1,101 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.constraintlayout.widget.Group; +import androidx.lifecycle.Observer; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +public class FragmentRuleLogs extends FragmentBase { + private RecyclerView rvRule; + private ContentLoadingProgressBar pbWait; + private Group grpReady; + + private AdapterRuleLog adapter; + + private long rule; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Get arguments + Bundle args = getArguments(); + rule = args.getLong("rule", -1); + } + + @Override + @Nullable + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + setSubtitle(R.string.title_edit_rules); + + View view = inflater.inflate(R.layout.fragment_rules, container, false); + + // Get controls + rvRule = view.findViewById(R.id.rvRule); + pbWait = view.findViewById(R.id.pbWait); + grpReady = view.findViewById(R.id.grpReady); + + // Wire controls + + rvRule.setHasFixedSize(false); + LinearLayoutManager llm = new LinearLayoutManager(getContext()); + rvRule.setLayoutManager(llm); + + adapter = new AdapterRuleLog(getContext(), getViewLifecycleOwner()); + rvRule.setAdapter(adapter); + + // Initialize + grpReady.setVisibility(View.GONE); + pbWait.setVisibility(View.VISIBLE); + + return view; + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + DB db = DB.getInstance(getContext()); + db.rulelog().liveRuleLogs(rule).observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(List rules) { + if (rules == null) + rules = new ArrayList<>(); + + adapter.set(rules); + + pbWait.setVisibility(View.GONE); + grpReady.setVisibility(View.VISIBLE); + } + }); + } +} diff --git a/app/src/main/java/eu/faircode/email/TupleRuleLogEx.java b/app/src/main/java/eu/faircode/email/TupleRuleLogEx.java new file mode 100644 index 0000000000..d3f9330524 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/TupleRuleLogEx.java @@ -0,0 +1,26 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +public class TupleRuleLogEx extends EntityRuleLog { + public long account; + public String thread; + public String subject; +} diff --git a/app/src/main/res/layout/fragment_rule_logs.xml b/app/src/main/res/layout/fragment_rule_logs.xml new file mode 100644 index 0000000000..39d9f56435 --- /dev/null +++ b/app/src/main/res/layout/fragment_rule_logs.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_rule_log.xml b/app/src/main/res/layout/item_rule_log.xml new file mode 100644 index 0000000000..65a4a7baea --- /dev/null +++ b/app/src/main/res/layout/item_rule_log.xml @@ -0,0 +1,39 @@ + + + + + + + + + + \ No newline at end of file