Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IMAP Keywords #2668

Closed
wants to merge 60 commits into from
Closed

IMAP Keywords #2668

wants to merge 60 commits into from

Conversation

Trogel
Copy link
Contributor

@Trogel Trogel commented Aug 7, 2017

This is my attempt at issue #769. It started as a rebase of pull request #558. Many thanks to @igoralmeida and all other contributors! However, to avoid surprises: I assume I have turned almost everything upside down.

As of now I'd call this a working prototype. So I don't expect this go into k-9 without further changes. However, now would be a good time for me to get some feedback and guidance for further development.

Implemented features

  • Message list: background shading using color of first tag ("first" in terms of the order defined in the keyword editor)
  • Message view: list of colored tag names; tap for tag choice dialog
  • Tag choice dialog: de-/select tags for message; changes are written to the IMAP sever; tap preferences icon for keyword editor
  • Keyword editor: change visibility, display name, color, order; add/remove keyword

New keywords found on emails are automatically added to the database, but by default they are hidden. To make them visible open an email, tap the tag list, tap the preferences icon, tap the visibility icon, and then tap the back button.

Some more things I'd like to add

  • Make shading in the message list optional.
  • Make tag names in the message view optinal.
  • Add optional set of colored tag icons to message list and message view left of the star icon. A tap on any of these icons should also open the tag choice dialog.
  • Commands to add default keywords of other popular email clients.
  • Single tag mode for tag choice dialog, so that a single tap already closes the dialog.
  • Add more commands to context menu in keyword editor, such as hide/show, edit name, etc.
  • Add "Edit tags" to context menu in message view.
  • When editing a keyword name, disable the Ok button while the name is empty.

Well, the list is getting long. It probably makes more sense to focus just on the fundamental functionality for now and leave additional fancy features for further pull requests.

Major design changes compared to #558

  • I tried to align class names and such with the following terminology (see also the comment in Flag.java):
    • A "flag" is either used only internally by k-9, or it is a server flag that additionally has an external code used for representation in external storage.
    • A "server flag" is either an (IMAP) system flag (whose external code starts with a backslash "\") or an (IMAP) keyword (whose external code does not start with a backslash "\").
    • A "keyword" additionally has properties such as a name for display, a color, a visibility flag, and a position in the ordered list of keywords.
    • A "tag" is a keyword assigned to a message.
  • Keyword support has been moved from the Flag class into a derived class named Keyword.
  • The keyword catalog is now stored in the global preferences database. The old implementation also had just one catalog in memory, but stored it in the account-specific databases.

Open issues

Now for the open issues, where I am hoping for feedback and possibly some guidance:

  1. I roughly tried to follow the coding guidelines. Any feedback is welcome!
  2. Is the UI design okay? What should be changed to be bettern in line with k-9?
  3. Did I possibly break compatibility with old Android versions? Are there special things I should look for? Special checks to perform?
  4. Color shading calculation is currently implemented in the Keyword class. This way it pulls com.android.support:support-v4 into k9mail-library. Is that okay, or should that better go into k9mail (non-library)? Or would is be even preferred to leave out that support lib at all and do the calculation "manually"?
  5. The keyword editor uses @drawable/design_ic_visibility and @drawable/design_ic_visibility_off to indicate visibility of the keywords. This pulls in com.android.support:design. It that okay?
  6. The keyword editor currently uses somewhat awkward up/down buttons for ordering. A list layout with drag support would be nice! But before investing additional time, I'd like to ask if this is considered useful, and if there is a preferred way to go. I briefly looked at the following implementations:
    https://github.com/JayH5/drag-sort-listview
    https://github.com/justasm/DragLinearLayout
  7. The up/down buttons currently misuse k-9's collapse/expand icons (iconActionCollapse, iconActionExpand). Can this remain until they are replaced with a drag list layout?
  8. I wonder whether the in-memory storage and the on-disk storage of the keyword catalog are already good enough in terms of thread-safety. The former is implemented in the class Keyword, the latter in the class Preferences. The catalog is changed by the keyword editor on one hand, and by the IMAP store (store/imap/ImapFolder.java) on the other hand. So I fear that there is a potential for race conditions.

Sander Bogaert and others added 25 commits August 7, 2017 19:36
…x to allow all flags to be retrieved by valueOfByRealName() just in case.
Changes:
* Current codebase has a new "FORWARDED" flag
* Since Flag is no longer an enum, use another container (HashSet
instead of EnumSet)
* Direct (dumb) translation of ImapStore.search(). Don't quite like it
right now.
* Remove MessagingController.SYNC_FLAGS so that all flags are sync'd
(not entirely sure this works the way I think it does, though).
Also, fix wrong translation of switch statement (must not break when
encountering default flags).
Adds a label+field after "Cc:" listing all user-defined imap keywords in
the message.
per artbristol's recommendation in issue 558
This maps name->Flag, to avoid duplication of objects, and adds a "tag
name" attribute to the Flag class. The UI now uses this field instead of
the name.

The memory mapping is updated at every call to
MessagingController.checkMailForAccount(). Probably not the right place.
This creates a very simple activity (shamelessly copying from FolderList) to
present a listing of imap keywords and their corresponding tags from the memory
map. Read-only, for now.

To access the activity, click the Tags label (or any of the items) in the
message header.

Also, change Flag.REMEMBERED_KEYWORDS to LinkedHashMap to maintain
iteration order.
User can edit the tag names for imap keywords that k-9 picks up when
reading/syncing local messages.

Usability still sucks and the UI has too many quirks (lost focus, cursor
not updated, saving to memory only when leaving the activity...)
Tag names survive resets. Still barely usable: need somebody with actual android UI skills.
Features:
* Message list: background shading using color of first tag
* Message view: list of colored tag names; tap for tag choice dialog
* Tag choice dialog: de-/select tags for message; changes are written to the IMAP sever; tap preferences icon for keyword editor
* Keyword editor: change visibility, display name, color, order; add/remove keyword

keywords: Drop TagDialog.

keywords: Add new tag choice dialog.

keywords: Add new keyword editor activity.

keywords: Factor class Keyword out of Flag.

keywords: Rename 'tag' to 'keyword' where is it not attached to a mail.

keywords: Move keyword storage from account-specific LocalStore to k9-global Preferences.

keywords: Move background color shading into Keyword and make it also work for the dark theme.

keywords: Pick a random color for a new Keyword.

keywords: Save newly created Keywords to preferences.

keywords: Factor out parsing of comma-separated list of Flag codes.

keywords: Drop tags label from MessageHeader.

keywords: Separate font size of tags from cc in MessageHeader.

keywords: Clean up MessagingController.syncFlags().
@Trogel
Copy link
Contributor Author

Trogel commented Aug 7, 2017

Some screenshots:

2017-08-07 k-9 keywords

And, well, I'll soon take a deeper look at Travis' log.

@igoralmeida
Copy link

Thanks a lot for carrying the torch! I used to test this with a separate Application ID alongside my 'official' k-9 install, I'll see if I can dig up the patch I used for that and take a look at what's new.

I'd just like to comment on (6), as a user.

The keyword editor currently uses somewhat awkward up/down buttons for ordering. A list layout with drag support would be nice! But before investing additional time, I'd like to ask if this is considered useful, and if there is a preferred way to go. I briefly looked at the following implementations:
https://github.com/JayH5/drag-sort-listview
https://github.com/justasm/DragLinearLayout

I like AntennaPod's drag-and-drop reordering in the Queue activity, which is only triggered when you use the :: part on the left. However, this is something I would do so rarely that tapping arrows might be acceptable (and, actually, carry a less "temporary" feel to it).
Queue

@philipwhiuk
Copy link
Contributor

The empahsis has been on reducing k9mail-library's dependency on Android. So put the functionality in k9, possibly by implementing an interface exposed by the library.

/**
* Flags that can be applied to Messages.
*
* Terminology:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather see this go on the website's developer documentation than in the source, but I'm a clean-code-ist.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine with me! For example, I could add a basic "Flags" or "Flags and Keywords" page to the Development section on https://k9mail.github.io/ or to the Wiki. What do you prefer?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you could make a PR for the website we can merge that in at the same time we do a release :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we go: See #55 for k9mail.github.io.


// Code for use in external storage, e.g. "\\Deleted"
// For IMAP: A 'system flag' or a 'keyword' in terms of RFC 3501, 2.3.2.
private final String externalCode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should think about WebDAV and EWS/EAS here a bit. Ideally we want the same flags in multiple stores.

}


public static Keyword getKeywordByExternalCode(String externalCode)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of static methods where you're storing and loading data, they are hard to test. I'd rather these move to a FlagsManager class which is an application singleton.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have now created a FlagManager that manages lookup of flags and keywords, as well as dynamic creation, deletion, and order of keywords. I left the creation (and the HashMaps) of flags in the class Flag, since the flags are all hard-coded, and I did not want to make their constructor public. There are still lookup methods in Flag, but they no longer access Keyword.

On the downside, the constructor of Keyword and some other methods are now public, so that FlagManager can access them. I could have made the manager a nested class of Keyword, but since it manages not just keywords, but all kinds of flags, that appeared to be wrong. Also, some do-it-yourself friend construct seems to be over-the-top.

Oh, and I slightly preferred FlagManager over FlagsManager, but will happily change it, if you like. :-)

} else if (flag == Flag.FORWARDED
&& (canCreateKeywords || store.getPermanentFlagsIndex().contains(Flag.FORWARDED))) {
flagNames.add("$Forwarded");
// client's can't add the RECENT flag!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'clients'

@Override
public View getView(
final int position, View convertView, ViewGroup parent)
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is way too long. It's a method for a class in a class that and contains multiple anonymous classes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Now I refactored it and added a holder class to avoid repetition of one-time tasks. Still, the nested class KeywordEditorListAdapter has ~200 lines, which is almost half of the whole file. If desired, I can move the nested class into its own file.


Keyword tag = getItem(position);
TextView tv = (TextView) convertView.findViewById(
android.R.id.text1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

android.R.id.text1 Change this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

android.R.id.text1 comes from the built-in layout android.R.layout.simple_list_item_multiple_choice. So, to change the former (for example to something like keyword_name), I would need to add a local layout for the list item. I will happily change it! Could you please confirm that this approach is preferred over using the built-in layout?

}

Keyword tag = getItem(position);
TextView tv = (TextView) convertView.findViewById(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tv Call this something more informative.

tv.setTextColor(tag.getTextColor());
}

ListView lv = (ListView) parent;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call this something more informative.

Keyword.getVisibleKeywords(mMessage.getFlags()));
fragment.show(getFragmentManager(), "tag_choice");
} catch (Exception e) {
e.printStackTrace();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use logging instead of this.

@@ -340,6 +356,32 @@ public void populate(final Message message, final Account account) {
mForwardedIcon.setVisibility(message.isSet(Flag.FORWARDED) ? View.VISIBLE : View.GONE);
mFlagged.setChecked(message.isSet(Flag.FLAGGED));

SpannableStringBuilder sb = new SpannableStringBuilder();
List<Keyword> tags = Keyword.getVisibleKeywords(msgFlags);
if (tags.size() != 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a helper method for this code.

@Trogel
Copy link
Contributor Author

Trogel commented Aug 26, 2017

The empahsis has been on reducing k9mail-library's dependency on Android. So put the functionality in k9, possibly by implementing an interface exposed by the library.

Done with 5f89a65.

@Trogel
Copy link
Contributor Author

Trogel commented Aug 26, 2017

Oh, I hope I did not make things unnecessarily complicated by merging the 'master' into the 'keywords'. I am still new to git and GitHub, and the big "Resolve Conflicts" button was quite tempting... If desired, I'll try to revert. :-/

@philipwhiuk Except for that, I think I have worked on almost all issues you raised. Thinking about keywords in WebDAV and EWS/EAS remains open. I hope I find some time in the next days. Any additional input on this issue is welcome. Please let me know, if I have missed anything else from your feedback!

In addition, I still want to add some options to the global settings to enable/disable tag-based shading in the message list and tag names in the message view. But probably not today.

@Valodim
Copy link
Contributor

Valodim commented Aug 26, 2017

I rebased this on current master, see https://github.com/k9mail/k-9/tree/Trogel-keywords. I noticed halfway through that you might have wanted to do this yourself, sorry if I've been hasty here :)

Unfortunately 'invalid' currently includes keywords with a comma that are valid according to RFC 3501, 2.3.2, but that k-9 cannot handle.
@Trogel
Copy link
Contributor Author

Trogel commented Aug 27, 2017

@Valodim Fine with me! I hope it is okay that I still add a few changes here. You might want to merge them into Trogel-keywords.

Except for my new commit eb25b31, the diff between Trogel-keywords and my branch is currently rather small. I just found two lines that you could drop from Trogel-keywords:

  • k9mail/src/main/java/com/fsck/k9/view/MessageHeader.java line 148
  • k9mail/src/main/res/values/strings.xml line 286

@Valodim
Copy link
Contributor

Valodim commented Aug 27, 2017

It probably makes sense to keep focus on this PR. I'd suggest you just git reset --hard origin/Trogel-keywords and work on top of that (you can also git cherry-pick eb25b3139e13e7f6e4d137b912c61be1c7fce70e that last commit of yours then) :)

@Trogel
Copy link
Contributor Author

Trogel commented Aug 27, 2017

@Valodim Did I get it right: Your idea is to, let's say, replace my merge with your rebase? So, the result would be as if I had rebased my branch and the PR. I'll happily do that, if desired, but I remember all these warnings like "Do not rebase commits that exist outside your repository" (from the Git Book) and "BadThingsWillHappen" (from k-9's GitGuide).

Could you please confirm that rebasing the PR is the right thing do now?

I already tried it in a local branch, and it seems I'd better git reset to upstream/Trogel-keywords rather than origin/Trogel-keywords, right? At least, my origin has no Trogel-keywords at the moment, but upstream has. ;-)

@Trogel
Copy link
Contributor Author

Trogel commented Aug 27, 2017

Options for tags added to global settings. Enjoy! :-)

@cketti
Copy link
Member

cketti commented Aug 27, 2017

@Trogel, it's nice to see that someone is working on this. Thank you.

I'm sorry for not chiming in earlier. After looking at the issues and previous attempts I feel like this is a feature that needs more consideration before attempting an implementation. The functionality spans a lot of areas and it's not obvious what the user experience should look like. After writing down where we want to go it should be easier to come up with the right abstractions for the code. Otherwise this will be just another instance where a feature is tightly coupled to code that shouldn't have to care about what the app does with the data.

I've added my thought on this feature to issue #769. I'm tempted to close this PR because I believe it'll be easier to start over than to refactor this.

@Trogel
Copy link
Contributor Author

Trogel commented Apr 2, 2018

Closing the pull request as suggested by @cketti.

@Trogel Trogel closed this Apr 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants