Imagine a world where your Gmail inbox has no unread emails!!
I managed to automatically mark a message as read when I swipe the notification of a new away on Android.
Most of the emails are short and I am done with by just reading the snippet in the notification drawer in Android. So, I wanted a way to mark them as read as soon as I am done. Unfortunately, this feature is not implemented in Gmail App. Challenge accepted!
How can you access Gmail and make changes to the messages on a normal (unrooted) Android device and add this custom functionality?
The approach I took is kind of crazy, but it does work...
Luckily Gmail App logs some events including the swipe of a message in notifications drawer and the thread ID of that message. So all I need is to figure out what log message I should check for and extract the thread ID, the do something with that. I use Tasker App to automate things on my device is has capability of triggering an event based on a matched logcat entry.
Tasker, with additional permissions, can access logcat and I can use it to get the thread ID. However, Tasker has limited access and definitely will not be able to modify Gmail messages without any API access.
Another challenge, is while I can detect Gmail notification, there is no easy way to know which message the notification has and even then, what to do with that info?
Tasker recently introduced log access feature. In this feature, you can trigger an task based on the log entry. So, I checked the logs for the swipe event of a Gmail message notification and found that the is a thread ID being logged. Then I matched that with a regex to extract the thread ID.
Gmail: NotificationHandler:Cancell ing notificationld 0 for tag gig:-1308489909 PRIORITY_INBOXALL MAIL a.x.j.Job: Starting Job(DetaileditemPr eloadingService:getltemByld#0.175) (priority=-7, jobType=RO0T) a.b.d.i.m.s. StoringThre Job(Detailedltem Preloading Service:getltemByld#0.175) Storing Thread DetailsFetcher. getF etchResultsForThread(thread-f: 16660xxxxxxxxxx9, 2) starting ... sapishim:SapiUiProvider.update: Can't mark seen for conversation=thread-f:16660xxxxxxxxxx9 ... Job(ItemChangesService:applyChangeIntents#1.372) Modifying labels for thread=thread-f:16660xxxxxxxxxx9, isBulkOp=false, affectedMessageIds=[msg-f:16660xxxxxxxxxx9], added=, removed=[^us]
However, there were 2 problems:
- Gmail API requires manual approval by Google to allow you to use "Sensitive Scopes"; anything that reads emails and/or modifies messages/threads. This is understandable practice to protect users. I didn't want to go through that and using an unverified app is limited to 100 requests, until it is verified.
- The thread ID that the API expects is not the same as the one in the log file. This was confusing. After some research, I found that the thread ID in the log files is just the decimal value of the hexadecimal thread ID Gmail API expects.
For example: thread-f:1666249521850194789 == 171fb51ca90b9b65
Decimal to Hex
This can be easily done in Tasker using a Javascriplet to convert the thread ID I get from logs to something that the API can ingest.
threadid = 1666249521850194789; hexthreadid = threadid.toString(16); //171fb51ca90b9b65
Due to the app verification issues (and OAuth complexity to manage access tokens, etc) I decided to try something else.
What if I can use good ole IMAP to read these messages?
This could only work if I can map Gmail message ID to the unique ID inside my IMAP mailbox. First, this sounded like one that will not work. Each mailbox has a unique ID inside the folder that the message lives in. Example:
Messgage ID 15 in inbox
Moved to spam now ID is 6
Moved to inbox, now ID is 16
and so on...
So, now I can get a decimal ID convert it to hex, and map a uid to thread id!
I just have to create an App Password to use for logging in using IMAP.
I created a simple python server to listen to POST requests with the thread ID, then it loops thought the last 15 messages in the inbox and fetches the message that matches via IMAP. This marks the message as read :)
Overview of the Setup
All the code is checked-in into this repo. You just have to set your values to the Gmail address/password and API secret.
SECRET = 'YOUR API SECRET TO PREVENT OTHERS FROM MAKING REQUESTS' GMAIL_ADDRESS = 'email@example.com' G_APP_PASS = 'YOUR GOOGLE APP PASSWORD'
If you are using Docker, there is also a Dockerfile to deploy the server.
Here is the Taskernet Profile, you can just click import to import the profile. Then set the secret and API endpoint in the task "Mark Read"