Categories: geek » android

RSS - Atom - Subscribe via email

Controlling my Android phone by voice

Posted: - Modified: | speech, android, speechtotext

[2025-01-30 Thu]: Fix timestamp format in toggle recording task.

I want to be able to use voice control to do things on my phone while I'm busy washing dishes, putting things away, knitting, or just keeping my hands warm. It'll also be handy to have a way to get things out of my head when the kiddo is koala-ing me. I've been using my Google Pixel 8's voice interface to set timers, send text messages, and do quick web searches. Building on my recent thoughts on wearable computing, I decided to spend some more time investigating the Google Assistant and Voice Access features in Android and setting up other voice shortcuts.

Tasker routines

I switched back to Google Assistant from Gemini so that I could run Tasker routines. I also found out that I needed to switch the language from English/Canada to English/US in order for my Tasker scripts to run instead of Google Assistant treating them as web searches. Once that was sorted out, I could run Tasker tasks with "Hey Google, run {task-name} in Tasker" and parameterize them with "Hey Google, run {task-name} with {parameter} in Tasker."

Voice Access

Learning how to use Voice Access to navigate, click, and type on my phone was straightforward. "Scroll down" works for webpages, while "scroll right" works for the e-books I have in Libby. Tapping items by text usually works. When it doesn't, I can use "show labels", "show numbers", or "show grid." The speech-to-text of "type …" isn't as good as Whisper, so I probably won't use it for a lot of dictation, but it's fine for quick notes. I can keep recording in the background so that I have the raw audio in case I want to review it or grab the WhisperX transcripts instead.

For some reason, saying "Hey Google, voice access" to start up voice access has been leaving the Assistant dialog on the screen, which makes it difficult to interact with the screen I'm looking at. I added a Tasker routine to start voice access, wait a second, and tap on the screen to dismiss the Assistant dialog.

Start Voice.tsk.xml - Import via Taskernet

Start Voice.tsk.xml
<TaskerData sr="" dvi="1" tv="6.3.13">
  <Task sr="task24">
    <cdate>1737565479418</cdate>
    <edate>1737566416661</edate>
    <id>24</id>
    <nme>Start Voice</nme>
    <pri>1000</pri>
    <Share sr="Share">
      <b>false</b>
      <d>Start voice access and dismiss the assistant dialog</d>
      <g>Accessibility,AutoInput</g>
      <p>true</p>
      <t></t>
    </Share>
    <Action sr="act0" ve="7">
      <code>20</code>
      <App sr="arg0">
        <appClass>com.google.android.apps.accessibility.voiceaccess.LauncherActivity</appClass>
        <appPkg>com.google.android.apps.accessibility.voiceaccess</appPkg>
        <label>Voice Access</label>
      </App>
      <Str sr="arg1" ve="3"/>
      <Int sr="arg2" val="0"/>
      <Int sr="arg3" val="0"/>
    </Action>
    <Action sr="act1" ve="7">
      <code>30</code>
      <Int sr="arg0" val="0"/>
      <Int sr="arg1" val="1"/>
      <Int sr="arg2" val="0"/>
      <Int sr="arg3" val="0"/>
      <Int sr="arg4" val="0"/>
    </Action>
    <Action sr="act2" ve="7">
      <code>107361459</code>
      <Bundle sr="arg0">
        <Vals sr="val">
          <EnableDisableAccessibilityService>&lt;null&gt;</EnableDisableAccessibilityService>
          <EnableDisableAccessibilityService-type>java.lang.String</EnableDisableAccessibilityService-type>
          <Password>&lt;null&gt;</Password>
          <Password-type>java.lang.String</Password-type>
          <com.twofortyfouram.locale.intent.extra.BLURB>Actions To Perform: click(point,564\,1045)
Not In AutoInput: true
Not In Tasker: true
Separator: ,
Check Millis: 1000</com.twofortyfouram.locale.intent.extra.BLURB>
          <com.twofortyfouram.locale.intent.extra.BLURB-type>java.lang.String</com.twofortyfouram.locale.intent.extra.BLURB-type>
          <net.dinglisch.android.tasker.JSON_ENCODED_KEYS>parameters</net.dinglisch.android.tasker.JSON_ENCODED_KEYS>
          <net.dinglisch.android.tasker.JSON_ENCODED_KEYS-type>java.lang.String</net.dinglisch.android.tasker.JSON_ENCODED_KEYS-type>
          <net.dinglisch.android.tasker.RELEVANT_VARIABLES>&lt;StringArray sr=""&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;%ailastbounds
Last Bounds
Bounds (left,top,right,bottom) of the item that the action last interacted with&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;%ailastcoordinates
Last Coordinates
Center coordinates (x,y) of the item that the action last interacted with&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2&gt;%err
Error Code
Only available if you select &amp;lt;b&amp;gt;Continue Task After Error&amp;lt;/b&amp;gt; and the action ends in error&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES3&gt;%errmsg
Error Message
Only available if you select &amp;lt;b&amp;gt;Continue Task After Error&amp;lt;/b&amp;gt; and the action ends in error&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES3&gt;&lt;/StringArray&gt;</net.dinglisch.android.tasker.RELEVANT_VARIABLES>
          <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>
          <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>parameters plugininstanceid plugintypeid </net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>
          <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>java.lang.String</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>
          <net.dinglisch.android.tasker.subbundled>true</net.dinglisch.android.tasker.subbundled>
          <net.dinglisch.android.tasker.subbundled-type>java.lang.Boolean</net.dinglisch.android.tasker.subbundled-type>
          <parameters>{"_action":"click(point,564\\,1045)","_additionalOptions":{"checkMs":"1000","separator":",","withCoordinates":false},"_whenToPerformAction":{"notInAutoInput":true,"notInTasker":true},"generatedValues":{}}</parameters>
          <parameters-type>java.lang.String</parameters-type>
          <plugininstanceid>b46b8afc-c840-40ad-9283-3946c57a1018</plugininstanceid>
          <plugininstanceid-type>java.lang.String</plugininstanceid-type>
          <plugintypeid>com.joaomgcd.autoinput.intent.IntentActionv2</plugintypeid>
          <plugintypeid-type>java.lang.String</plugintypeid-type>
        </Vals>
      </Bundle>
      <Str sr="arg1" ve="3">com.joaomgcd.autoinput</Str>
      <Str sr="arg2" ve="3">com.joaomgcd.autoinput.activity.ActivityConfigActionv2</Str>
      <Int sr="arg3" val="60"/>
      <Int sr="arg4" val="1"/>
    </Action>
  </Task>
</TaskerData>

I can use "Hey Google, read aloud" to read a webpage. I can use "Hey Google, skip ahead 2 minutes" or "Hey Google, rewind 30 seconds." Not sure how I can navigate by text, though. It would be nice to get an overview of headings and then jump to the one I want, or search for text and continue from there.

Autoplay an emacs.tv video

I wanted to be able to play random emacs.tv videos without needing to touch my phone. I added autoplay support to the web interface so that you can open https://emacs.tv?autoplay=1 and have it autoplay videos when you select the next random one by clicking on the site logo, "Lucky pick", or the dice icon. The first video doesn't autoplay because YouTube requires user interaction in order to autoplay unmuted videos, but I can work around that with a Tasker script that loads the URL, waits a few seconds, and clicks on the heading with AutoInput.

Emacs TV.tsk.xml - Import via Taskernet

Emacs TV.tsk.xml
<TaskerData sr="" dvi="1" tv="6.3.13">
  <Task sr="task18">
    <cdate>1737558964554</cdate>
    <edate>1737562488128</edate>
    <id>18</id>
    <nme>Emacs TV</nme>
    <pri>1000</pri>
    <Share sr="Share">
      <b>false</b>
      <d>Play random Emacs video</d>
      <g>Watch</g>
      <p>true</p>
      <t></t>
    </Share>
    <Action sr="act0" ve="7">
      <code>104</code>
      <Str sr="arg0" ve="3">https://emacs.tv?autoplay=1</Str>
      <App sr="arg1"/>
      <Int sr="arg2" val="0"/>
      <Str sr="arg3" ve="3"/>
    </Action>
    <Action sr="act1" ve="7">
      <code>30</code>
      <Int sr="arg0" val="0"/>
      <Int sr="arg1" val="3"/>
      <Int sr="arg2" val="0"/>
      <Int sr="arg3" val="0"/>
      <Int sr="arg4" val="0"/>
    </Action>
    <Action sr="act2" ve="7">
      <code>107361459</code>
      <Bundle sr="arg0">
        <Vals sr="val">
          <EnableDisableAccessibilityService>&lt;null&gt;</EnableDisableAccessibilityService>
          <EnableDisableAccessibilityService-type>java.lang.String</EnableDisableAccessibilityService-type>
          <Password>&lt;null&gt;</Password>
          <Password-type>java.lang.String</Password-type>
          <com.twofortyfouram.locale.intent.extra.BLURB>Actions To Perform: click(point,229\,417)
Not In AutoInput: true
Not In Tasker: true
Separator: ,
Check Millis: 1000</com.twofortyfouram.locale.intent.extra.BLURB>
          <com.twofortyfouram.locale.intent.extra.BLURB-type>java.lang.String</com.twofortyfouram.locale.intent.extra.BLURB-type>
          <net.dinglisch.android.tasker.JSON_ENCODED_KEYS>parameters</net.dinglisch.android.tasker.JSON_ENCODED_KEYS>
          <net.dinglisch.android.tasker.JSON_ENCODED_KEYS-type>java.lang.String</net.dinglisch.android.tasker.JSON_ENCODED_KEYS-type>
          <net.dinglisch.android.tasker.RELEVANT_VARIABLES>&lt;StringArray sr=""&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;%ailastbounds
Last Bounds
Bounds (left,top,right,bottom) of the item that the action last interacted with&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;%ailastcoordinates
Last Coordinates
Center coordinates (x,y) of the item that the action last interacted with&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2&gt;%err
Error Code
Only available if you select &amp;lt;b&amp;gt;Continue Task After Error&amp;lt;/b&amp;gt; and the action ends in error&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES3&gt;%errmsg
Error Message
Only available if you select &amp;lt;b&amp;gt;Continue Task After Error&amp;lt;/b&amp;gt; and the action ends in error&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES3&gt;&lt;/StringArray&gt;</net.dinglisch.android.tasker.RELEVANT_VARIABLES>
          <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>
          <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>parameters plugininstanceid plugintypeid </net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>
          <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>java.lang.String</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>
          <net.dinglisch.android.tasker.subbundled>true</net.dinglisch.android.tasker.subbundled>
          <net.dinglisch.android.tasker.subbundled-type>java.lang.Boolean</net.dinglisch.android.tasker.subbundled-type>
          <parameters>{"_action":"click(point,229\\,417)","_additionalOptions":{"checkMs":"1000","separator":",","withCoordinates":false},"_whenToPerformAction":{"notInAutoInput":true,"notInTasker":true},"generatedValues":{}}</parameters>
          <parameters-type>java.lang.String</parameters-type>
          <plugininstanceid>45ce7a83-47e5-48fb-8c3e-20655e668353</plugininstanceid>
          <plugininstanceid-type>java.lang.String</plugininstanceid-type>
          <plugintypeid>com.joaomgcd.autoinput.intent.IntentActionv2</plugintypeid>
          <plugintypeid-type>java.lang.String</plugintypeid-type>
        </Vals>
      </Bundle>
      <Str sr="arg1" ve="3">com.joaomgcd.autoinput</Str>
      <Str sr="arg2" ve="3">com.joaomgcd.autoinput.activity.ActivityConfigActionv2</Str>
      <Int sr="arg3" val="60"/>
      <Int sr="arg4" val="1"/>
    </Action>
  </Task>
</TaskerData>

Then I set up a Google Assistant routine with the triggers "teach me" or "Emacs TV" and the action "run Emacs TV in Tasker. Now I can say "Hey Google, teach me" and it'll play a random Emacs video for me. I can repeat "Hey Google, teach me" to get a different video, and I can pause with "Hey Google, pause video".

This was actually my second approach. The first time I tried to implement this, I thought about using Voice Access to interact with the buttons. Strangely, I couldn't get Voice Access to click on the header links or the buttons even when I had aria-label, role="button", and tabindex attributes set on them. As a hacky workaround, I made the site logo pick a new random video when clicked, so I can at least use it as a large touch target when I use "display grid" in Voice Access. ("Tap 5" will load the next video.)

There doesn't seem to be a way to add custom voice access commands to a webpage in a way that hooks into Android Voice Access and iOS Voice Control, but maybe I'm just missing something obvious when it comes to ARIA attributes.

Open my Org agenda and scroll through it

There were some words that I couldn't get Google Assistant or Voice Access to understand, like "open Orgzly Revived". Fortunately, "Open Revived" worked just fine.

I wanted to be able to see my Org Agenda. After some fiddling around (see the resources in this section), I figured out this AutoShare intent that runs an agenda search:

orgzly-revived-search.intent

orgzly-revived-search.intent
{
  "target": "Activity",
  "appname": "Orgzly Revived",
  "action": "android.intent.action.MAIN",
  "package": "com.orgzlyrevived",
  "class": "com.orgzly.android.ui.main.MainActivity",
  "extras": [
    {
      "type": "String",
      "key": "com.orgzly.intent.extra.QUERY_STRING",
      "name": "Query"
    }
  ],
  "name": "Search",
  "id": "Orgzly-search"
}

Then I defined a Tasker task called "Search Orgzly Revived":

Download Search Orgzly Revived.tsk.xml

Search Orgzly Revived.tsk.xml
<TaskerData sr="" dvi="1" tv="6.3.13">
  <Task sr="task16">
    <cdate>1676823952566</cdate>
    <edate>1737567565538</edate>
    <id>16</id>
    <nme>Search Orgzly Revived</nme>
    <pri>100</pri>
    <Share sr="Share">
      <b>false</b>
      <d>Search Orgzly Revived</d>
      <g>Work,Well-Being</g>
      <p>false</p>
      <t></t>
    </Share>
    <Action sr="act0" ve="7">
      <code>18</code>
      <App sr="arg0">
        <appClass>com.orgzly.android.ui.LauncherActivity</appClass>
        <appPkg>com.orgzlyrevived</appPkg>
        <label>Orgzly Revived</label>
      </App>
      <Int sr="arg1" val="0"/>
    </Action>
    <Action sr="act1" ve="7">
      <code>547</code>
      <Str sr="arg0" ve="3">%extra</Str>
      <Str sr="arg1" ve="3">com.orgzly.intent.extra.QUERY_STRING:%par1</Str>
      <Int sr="arg2" val="0"/>
      <Int sr="arg3" val="0"/>
      <Int sr="arg4" val="0"/>
      <Int sr="arg5" val="3"/>
      <Int sr="arg6" val="1"/>
    </Action>
    <Action sr="act2" ve="7">
      <code>877</code>
      <Str sr="arg0" ve="3">android.intent.action.MAIN</Str>
      <Int sr="arg1" val="0"/>
      <Str sr="arg2" ve="3"/>
      <Str sr="arg3" ve="3"/>
      <Str sr="arg4" ve="3">%extra</Str>
      <Str sr="arg5" ve="3"/>
      <Str sr="arg6" ve="3"/>
      <Str sr="arg7" ve="3">com.orgzlyrevived</Str>
      <Str sr="arg8" ve="3">com.orgzly.android.ui.main.MainActivity</Str>
      <Int sr="arg9" val="1"/>
    </Action>
    <Img sr="icn" ve="2">
      <nme>mw_action_today</nme>
    </Img>
  </Task>
</TaskerData>

I made a Google Assistant routine that uses "show my agenda" as the trigger and "run search orgzly revived in Tasker" as the action. After a quick "Hey Google, show my agenda; Hey Google, voice access", I can use "scroll down" to page through the list. "Back" gets me to the list of notebooks, and "inbox" opens my inbox.

Resources:

Add and open notes in Orgzly Revived

When I'm looking at an Orgzly Revived notebook with Voice Access turned on, "plus" starts a new note. Anything that isn't a label gets typed, so I can just start saying the title of my note (or use "type …"). If I want to add the content, I have to use "hide keyboard", "tap content", and then "type …"). "Tap scheduled time; Tomorrow" works if the scheduled time widget is visible, so I just need to use "scroll down" if the title is long. "Tap done; one" saves it.

Adding a note could be simpler - maybe a Tasker task that prompts me for text and adds it. I could use Tasker to prepend to my Inbox.org and then reload it in Orgzly. It would be more elegant to figure out the intent for adding a note, though. Maybe in the Orgzly Android intent receiver documentation?

When I'm looking at the Orgzly notebook and I say part of the text in a note without a link, it opens the note. If the note has a link, it seems to open the link directly. Tapping by numbers also goes to the link, but tapping by grid opens the note.

I'd love to speech-enable this someday so that I can hear Orgzly Revived step through my agenda and use my voice to mark things as cancelled/done, schedule them for today/tomorrow/next week, or add extra notes to the body.

Add items to OurGroceries

W+ and I use the OurGroceries app. As it turns out, "Hey Google, ask OurGroceries to add milk" still works. Also, Voice Access works fine with OurGroceries. I can say "Plus", dictate an item, and tap "Add." I configured the cross-off action to be swipes instead of taps to minimize accidental crossing-off at the store, so I can say "swipe right on apples" to mark that as done.

Track time

I added a Tasker task to update my personal time-tracking system, and I added some Google Assistant routines for common categories like writing or routines. I can also use "run track with {category} in Tasker" to track a less-common category. The kiddo likes to get picked up and hugged a lot, so I added a "Hey Google, koala time" routine to clock into childcare in a more fun way. I have to enunciate that one clearly or it'll get turned into "Call into …", which doesn't work.

Toggle recording

Since I was tinkering around with Tasker a lot, I decided to try moving my voice recording into it. I want to save timestamped recordings into my ~/sync/recordings directory so that they're automatically synchronized with Syncthing, and then they can feed into my WhisperX workflow. This feels a little more responsive and reliable than Fossify Voice Recorder, actually, since that one tended to become unresponsive from time to time.

Download Toggle Recording.tsk.xml - Import via Taskernet

Toggle Recording.tsk.xml
<TaskerData sr="" dvi="1" tv="6.3.13">
  <Task sr="task12">
    <cdate>1737504717303</cdate>
    <edate>1738272248919</edate>
    <id>12</id>
    <nme>Toggle Recording</nme>
    <pri>100</pri>
    <Share sr="Share">
      <b>false</b>
      <d>Toggle recording on and off; save timestamped file to sync/recordings</d>
      <g>Sound</g>
      <p>true</p>
      <t></t>
    </Share>
    <Action sr="act0" ve="7">
      <code>37</code>
      <ConditionList sr="if">
        <Condition sr="c0" ve="3">
          <lhs>%RECORDING</lhs>
          <op>12</op>
          <rhs></rhs>
        </Condition>
      </ConditionList>
    </Action>
    <Action sr="act1" ve="7">
      <code>549</code>
      <Str sr="arg0" ve="3">%RECORDING</Str>
      <Int sr="arg1" val="0"/>
      <Int sr="arg2" val="0"/>
      <Int sr="arg3" val="0"/>
    </Action>
    <Action sr="act10" ve="7">
      <code>166160670</code>
      <Bundle sr="arg0">
        <Vals sr="val">
          <ActionIconString1>&lt;null&gt;</ActionIconString1>
          <ActionIconString1-type>java.lang.String</ActionIconString1-type>
          <ActionIconString2>&lt;null&gt;</ActionIconString2>
          <ActionIconString2-type>java.lang.String</ActionIconString2-type>
          <ActionIconString3>&lt;null&gt;</ActionIconString3>
          <ActionIconString3-type>java.lang.String</ActionIconString3-type>
          <ActionIconString4>&lt;null&gt;</ActionIconString4>
          <ActionIconString4-type>java.lang.String</ActionIconString4-type>
          <ActionIconString5>&lt;null&gt;</ActionIconString5>
          <ActionIconString5-type>java.lang.String</ActionIconString5-type>
          <AppendTexts>false</AppendTexts>
          <AppendTexts-type>java.lang.Boolean</AppendTexts-type>
          <BackgroundColor>&lt;null&gt;</BackgroundColor>
          <BackgroundColor-type>java.lang.String</BackgroundColor-type>
          <BadgeType>&lt;null&gt;</BadgeType>
          <BadgeType-type>java.lang.String</BadgeType-type>
          <Button1UnlockScreen>false</Button1UnlockScreen>
          <Button1UnlockScreen-type>java.lang.Boolean</Button1UnlockScreen-type>
          <Button2UnlockScreen>false</Button2UnlockScreen>
          <Button2UnlockScreen-type>java.lang.Boolean</Button2UnlockScreen-type>
          <Button3UnlockScreen>false</Button3UnlockScreen>
          <Button3UnlockScreen-type>java.lang.Boolean</Button3UnlockScreen-type>
          <Button4UnlockScreen>false</Button4UnlockScreen>
          <Button4UnlockScreen-type>java.lang.Boolean</Button4UnlockScreen-type>
          <Button5UnlockScreen>false</Button5UnlockScreen>
          <Button5UnlockScreen-type>java.lang.Boolean</Button5UnlockScreen-type>
          <ChronometerCountDown>false</ChronometerCountDown>
          <ChronometerCountDown-type>java.lang.Boolean</ChronometerCountDown-type>
          <Colorize>false</Colorize>
          <Colorize-type>java.lang.Boolean</Colorize-type>
          <DismissOnTouchVariable>&lt;null&gt;</DismissOnTouchVariable>
          <DismissOnTouchVariable-type>java.lang.String</DismissOnTouchVariable-type>
          <ExtraInfo>&lt;null&gt;</ExtraInfo>
          <ExtraInfo-type>java.lang.String</ExtraInfo-type>
          <GroupAlertBehaviour>&lt;null&gt;</GroupAlertBehaviour>
          <GroupAlertBehaviour-type>java.lang.String</GroupAlertBehaviour-type>
          <GroupKey>&lt;null&gt;</GroupKey>
          <GroupKey-type>java.lang.String</GroupKey-type>
          <IconExpanded>&lt;null&gt;</IconExpanded>
          <IconExpanded-type>java.lang.String</IconExpanded-type>
          <IsGroupSummary>false</IsGroupSummary>
          <IsGroupSummary-type>java.lang.Boolean</IsGroupSummary-type>
          <IsGroupVariable>&lt;null&gt;</IsGroupVariable>
          <IsGroupVariable-type>java.lang.String</IsGroupVariable-type>
          <MediaAlbum>&lt;null&gt;</MediaAlbum>
          <MediaAlbum-type>java.lang.String</MediaAlbum-type>
          <MediaArtist>&lt;null&gt;</MediaArtist>
          <MediaArtist-type>java.lang.String</MediaArtist-type>
          <MediaDuration>&lt;null&gt;</MediaDuration>
          <MediaDuration-type>java.lang.String</MediaDuration-type>
          <MediaIcon>&lt;null&gt;</MediaIcon>
          <MediaIcon-type>java.lang.String</MediaIcon-type>
          <MediaLayout>false</MediaLayout>
          <MediaLayout-type>java.lang.Boolean</MediaLayout-type>
          <MediaNextCommand>&lt;null&gt;</MediaNextCommand>
          <MediaNextCommand-type>java.lang.String</MediaNextCommand-type>
          <MediaPauseCommand>&lt;null&gt;</MediaPauseCommand>
          <MediaPauseCommand-type>java.lang.String</MediaPauseCommand-type>
          <MediaPlayCommand>&lt;null&gt;</MediaPlayCommand>
          <MediaPlayCommand-type>java.lang.String</MediaPlayCommand-type>
          <MediaPlaybackState>&lt;null&gt;</MediaPlaybackState>
          <MediaPlaybackState-type>java.lang.String</MediaPlaybackState-type>
          <MediaPosition>&lt;null&gt;</MediaPosition>
          <MediaPosition-type>java.lang.String</MediaPosition-type>
          <MediaPreviousCommand>&lt;null&gt;</MediaPreviousCommand>
          <MediaPreviousCommand-type>java.lang.String</MediaPreviousCommand-type>
          <MediaTrack>&lt;null&gt;</MediaTrack>
          <MediaTrack-type>java.lang.String</MediaTrack-type>
          <MessagingImages>&lt;null&gt;</MessagingImages>
          <MessagingImages-type>java.lang.String</MessagingImages-type>
          <MessagingOwnIcon>&lt;null&gt;</MessagingOwnIcon>
          <MessagingOwnIcon-type>java.lang.String</MessagingOwnIcon-type>
          <MessagingOwnName>&lt;null&gt;</MessagingOwnName>
          <MessagingOwnName-type>java.lang.String</MessagingOwnName-type>
          <MessagingPersonBot>&lt;null&gt;</MessagingPersonBot>
          <MessagingPersonBot-type>java.lang.String</MessagingPersonBot-type>
          <MessagingPersonIcons>&lt;null&gt;</MessagingPersonIcons>
          <MessagingPersonIcons-type>java.lang.String</MessagingPersonIcons-type>
          <MessagingPersonImportant>&lt;null&gt;</MessagingPersonImportant>
          <MessagingPersonImportant-type>java.lang.String</MessagingPersonImportant-type>
          <MessagingPersonNames>&lt;null&gt;</MessagingPersonNames>
          <MessagingPersonNames-type>java.lang.String</MessagingPersonNames-type>
          <MessagingPersonUri>&lt;null&gt;</MessagingPersonUri>
          <MessagingPersonUri-type>java.lang.String</MessagingPersonUri-type>
          <MessagingSeparator>&lt;null&gt;</MessagingSeparator>
          <MessagingSeparator-type>java.lang.String</MessagingSeparator-type>
          <MessagingTexts>&lt;null&gt;</MessagingTexts>
          <MessagingTexts-type>java.lang.String</MessagingTexts-type>
          <NotificationChannelBypassDnd>false</NotificationChannelBypassDnd>
          <NotificationChannelBypassDnd-type>java.lang.Boolean</NotificationChannelBypassDnd-type>
          <NotificationChannelDescription>&lt;null&gt;</NotificationChannelDescription>
          <NotificationChannelDescription-type>java.lang.String</NotificationChannelDescription-type>
          <NotificationChannelId>&lt;null&gt;</NotificationChannelId>
          <NotificationChannelId-type>java.lang.String</NotificationChannelId-type>
          <NotificationChannelImportance>&lt;null&gt;</NotificationChannelImportance>
          <NotificationChannelImportance-type>java.lang.String</NotificationChannelImportance-type>
          <NotificationChannelName>&lt;null&gt;</NotificationChannelName>
          <NotificationChannelName-type>java.lang.String</NotificationChannelName-type>
          <NotificationChannelShowBadge>false</NotificationChannelShowBadge>
          <NotificationChannelShowBadge-type>java.lang.Boolean</NotificationChannelShowBadge-type>
          <PersistentVariable>&lt;null&gt;</PersistentVariable>
          <PersistentVariable-type>java.lang.String</PersistentVariable-type>
          <PhoneOnly>false</PhoneOnly>
          <PhoneOnly-type>java.lang.Boolean</PhoneOnly-type>
          <PriorityVariable>&lt;null&gt;</PriorityVariable>
          <PriorityVariable-type>java.lang.String</PriorityVariable-type>
          <PublicVersion>&lt;null&gt;</PublicVersion>
          <PublicVersion-type>java.lang.String</PublicVersion-type>
          <ReplyAction>&lt;null&gt;</ReplyAction>
          <ReplyAction-type>java.lang.String</ReplyAction-type>
          <ReplyChoices>&lt;null&gt;</ReplyChoices>
          <ReplyChoices-type>java.lang.String</ReplyChoices-type>
          <ReplyLabel>&lt;null&gt;</ReplyLabel>
          <ReplyLabel-type>java.lang.String</ReplyLabel-type>
          <ShareButtonsVariable>&lt;null&gt;</ShareButtonsVariable>
          <ShareButtonsVariable-type>java.lang.String</ShareButtonsVariable-type>
          <SkipPictureCache>false</SkipPictureCache>
          <SkipPictureCache-type>java.lang.Boolean</SkipPictureCache-type>
          <SoundPath>&lt;null&gt;</SoundPath>
          <SoundPath-type>java.lang.String</SoundPath-type>
          <StatusBarIconString>&lt;null&gt;</StatusBarIconString>
          <StatusBarIconString-type>java.lang.String</StatusBarIconString-type>
          <StatusBarTextSize>16</StatusBarTextSize>
          <StatusBarTextSize-type>java.lang.String</StatusBarTextSize-type>
          <TextExpanded>&lt;null&gt;</TextExpanded>
          <TextExpanded-type>java.lang.String</TextExpanded-type>
          <Time>&lt;null&gt;</Time>
          <Time-type>java.lang.String</Time-type>
          <TimeFormat>&lt;null&gt;</TimeFormat>
          <TimeFormat-type>java.lang.String</TimeFormat-type>
          <Timeout>&lt;null&gt;</Timeout>
          <Timeout-type>java.lang.String</Timeout-type>
          <TitleExpanded>&lt;null&gt;</TitleExpanded>
          <TitleExpanded-type>java.lang.String</TitleExpanded-type>
          <UpdateNotification>false</UpdateNotification>
          <UpdateNotification-type>java.lang.Boolean</UpdateNotification-type>
          <UseChronometer>false</UseChronometer>
          <UseChronometer-type>java.lang.Boolean</UseChronometer-type>
          <UseHTML>false</UseHTML>
          <UseHTML-type>java.lang.Boolean</UseHTML-type>
          <Visibility>&lt;null&gt;</Visibility>
          <Visibility-type>java.lang.String</Visibility-type>
          <com.twofortyfouram.locale.intent.extra.BLURB>Title: my recording
Action on Touch: stop recording
Status Bar Text Size: 16
Id: my-recording
Dismiss on Touch: true
Priority: -1
Separator: ,</com.twofortyfouram.locale.intent.extra.BLURB>
          <com.twofortyfouram.locale.intent.extra.BLURB-type>java.lang.String</com.twofortyfouram.locale.intent.extra.BLURB-type>
          <config_action_1_icon>&lt;null&gt;</config_action_1_icon>
          <config_action_1_icon-type>java.lang.String</config_action_1_icon-type>
          <config_action_2_icon>&lt;null&gt;</config_action_2_icon>
          <config_action_2_icon-type>java.lang.String</config_action_2_icon-type>
          <config_action_3_icon>&lt;null&gt;</config_action_3_icon>
          <config_action_3_icon-type>java.lang.String</config_action_3_icon-type>
          <config_action_4_icon>&lt;null&gt;</config_action_4_icon>
          <config_action_4_icon-type>java.lang.String</config_action_4_icon-type>
          <config_action_5_icon>&lt;null&gt;</config_action_5_icon>
          <config_action_5_icon-type>java.lang.String</config_action_5_icon-type>
          <config_notification_action>stop recording</config_notification_action>
          <config_notification_action-type>java.lang.String</config_notification_action-type>
          <config_notification_action_button1>&lt;null&gt;</config_notification_action_button1>
          <config_notification_action_button1-type>java.lang.String</config_notification_action_button1-type>
          <config_notification_action_button2>&lt;null&gt;</config_notification_action_button2>
          <config_notification_action_button2-type>java.lang.String</config_notification_action_button2-type>
          <config_notification_action_button3>&lt;null&gt;</config_notification_action_button3>
          <config_notification_action_button3-type>java.lang.String</config_notification_action_button3-type>
          <config_notification_action_button4>&lt;null&gt;</config_notification_action_button4>
          <config_notification_action_button4-type>java.lang.String</config_notification_action_button4-type>
          <config_notification_action_button5>&lt;null&gt;</config_notification_action_button5>
          <config_notification_action_button5-type>java.lang.String</config_notification_action_button5-type>
          <config_notification_action_label1>&lt;null&gt;</config_notification_action_label1>
          <config_notification_action_label1-type>java.lang.String</config_notification_action_label1-type>
          <config_notification_action_label2>&lt;null&gt;</config_notification_action_label2>
          <config_notification_action_label2-type>java.lang.String</config_notification_action_label2-type>
          <config_notification_action_label3>&lt;null&gt;</config_notification_action_label3>
          <config_notification_action_label3-type>java.lang.String</config_notification_action_label3-type>
          <config_notification_action_on_dismiss>&lt;null&gt;</config_notification_action_on_dismiss>
          <config_notification_action_on_dismiss-type>java.lang.String</config_notification_action_on_dismiss-type>
          <config_notification_action_share>false</config_notification_action_share>
          <config_notification_action_share-type>java.lang.Boolean</config_notification_action_share-type>
          <config_notification_command>&lt;null&gt;</config_notification_command>
          <config_notification_command-type>java.lang.String</config_notification_command-type>
          <config_notification_content_info>&lt;null&gt;</config_notification_content_info>
          <config_notification_content_info-type>java.lang.String</config_notification_content_info-type>
          <config_notification_dismiss_on_touch>true</config_notification_dismiss_on_touch>
          <config_notification_dismiss_on_touch-type>java.lang.Boolean</config_notification_dismiss_on_touch-type>
          <config_notification_icon>&lt;null&gt;</config_notification_icon>
          <config_notification_icon-type>java.lang.String</config_notification_icon-type>
          <config_notification_indeterminate_progress>false</config_notification_indeterminate_progress>
          <config_notification_indeterminate_progress-type>java.lang.Boolean</config_notification_indeterminate_progress-type>
          <config_notification_led_color>&lt;null&gt;</config_notification_led_color>
          <config_notification_led_color-type>java.lang.String</config_notification_led_color-type>
          <config_notification_led_off>&lt;null&gt;</config_notification_led_off>
          <config_notification_led_off-type>java.lang.String</config_notification_led_off-type>
          <config_notification_led_on>&lt;null&gt;</config_notification_led_on>
          <config_notification_led_on-type>java.lang.String</config_notification_led_on-type>
          <config_notification_max_progress>&lt;null&gt;</config_notification_max_progress>
          <config_notification_max_progress-type>java.lang.String</config_notification_max_progress-type>
          <config_notification_number>&lt;null&gt;</config_notification_number>
          <config_notification_number-type>java.lang.String</config_notification_number-type>
          <config_notification_persistent>true</config_notification_persistent>
          <config_notification_persistent-type>java.lang.Boolean</config_notification_persistent-type>
          <config_notification_picture>&lt;null&gt;</config_notification_picture>
          <config_notification_picture-type>java.lang.String</config_notification_picture-type>
          <config_notification_priority>-1</config_notification_priority>
          <config_notification_priority-type>java.lang.String</config_notification_priority-type>
          <config_notification_progress>&lt;null&gt;</config_notification_progress>
          <config_notification_progress-type>java.lang.String</config_notification_progress-type>
          <config_notification_subtext>&lt;null&gt;</config_notification_subtext>
          <config_notification_subtext-type>java.lang.String</config_notification_subtext-type>
          <config_notification_text>&lt;null&gt;</config_notification_text>
          <config_notification_text-type>java.lang.String</config_notification_text-type>
          <config_notification_ticker>&lt;null&gt;</config_notification_ticker>
          <config_notification_ticker-type>java.lang.String</config_notification_ticker-type>
          <config_notification_title>my recording</config_notification_title>
          <config_notification_title-type>java.lang.String</config_notification_title-type>
          <config_notification_url>&lt;null&gt;</config_notification_url>
          <config_notification_url-type>java.lang.String</config_notification_url-type>
          <config_notification_vibration>&lt;null&gt;</config_notification_vibration>
          <config_notification_vibration-type>java.lang.String</config_notification_vibration-type>
          <config_status_bar_icon>&lt;null&gt;</config_status_bar_icon>
          <config_status_bar_icon-type>java.lang.String</config_status_bar_icon-type>
          <net.dinglisch.android.tasker.RELEVANT_VARIABLES>&lt;StringArray sr=""&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;%err
Error Code
Only available if you select &amp;lt;b&amp;gt;Continue Task After Error&amp;lt;/b&amp;gt; and the action ends in error&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;%errmsg
Error Message
Only available if you select &amp;lt;b&amp;gt;Continue Task After Error&amp;lt;/b&amp;gt; and the action ends in error&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;&lt;/StringArray&gt;</net.dinglisch.android.tasker.RELEVANT_VARIABLES>
          <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>
          <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>StatusBarTextSize config_notification_title config_notification_action notificaitionid config_notification_priority plugininstanceid plugintypeid </net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>
          <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>java.lang.String</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>
          <net.dinglisch.android.tasker.subbundled>true</net.dinglisch.android.tasker.subbundled>
          <net.dinglisch.android.tasker.subbundled-type>java.lang.Boolean</net.dinglisch.android.tasker.subbundled-type>
          <notificaitionid>my-recording</notificaitionid>
          <notificaitionid-type>java.lang.String</notificaitionid-type>
          <notificaitionsound>&lt;null&gt;</notificaitionsound>
          <notificaitionsound-type>java.lang.String</notificaitionsound-type>
          <plugininstanceid>9fca7d3a-cca6-4bfb-8ec4-a991054350c5</plugininstanceid>
          <plugininstanceid-type>java.lang.String</plugininstanceid-type>
          <plugintypeid>com.joaomgcd.autonotification.intent.IntentNotification</plugintypeid>
          <plugintypeid-type>java.lang.String</plugintypeid-type>
        </Vals>
      </Bundle>
      <Str sr="arg1" ve="3">com.joaomgcd.autonotification</Str>
      <Str sr="arg2" ve="3">com.joaomgcd.autonotification.activity.ActivityConfigNotify</Str>
      <Int sr="arg3" val="0"/>
      <Int sr="arg4" val="1"/>
    </Action>
    <Action sr="act11" ve="7">
      <code>559</code>
      <Str sr="arg0" ve="3">Go</Str>
      <Str sr="arg1" ve="3">default:default</Str>
      <Int sr="arg2" val="3"/>
      <Int sr="arg3" val="5"/>
      <Int sr="arg4" val="5"/>
      <Int sr="arg5" val="1"/>
      <Int sr="arg6" val="0"/>
      <Int sr="arg7" val="0"/>
    </Action>
    <Action sr="act12" ve="7">
      <code>455</code>
      <Str sr="arg0" ve="3">sync/recordings/%filename</Str>
      <Int sr="arg1" val="0"/>
      <Int sr="arg2" val="0"/>
      <Int sr="arg3" val="0"/>
      <Int sr="arg4" val="0"/>
    </Action>
    <Action sr="act13" ve="7">
      <code>38</code>
    </Action>
    <Action sr="act2" ve="7">
      <code>657</code>
    </Action>
    <Action sr="act3" ve="7">
      <code>559</code>
      <Str sr="arg0" ve="3">Done</Str>
      <Str sr="arg1" ve="3">default:default</Str>
      <Int sr="arg2" val="3"/>
      <Int sr="arg3" val="5"/>
      <Int sr="arg4" val="5"/>
      <Int sr="arg5" val="1"/>
      <Int sr="arg6" val="0"/>
      <Int sr="arg7" val="0"/>
    </Action>
    <Action sr="act4" ve="7">
      <code>2046367074</code>
      <Bundle sr="arg0">
        <Vals sr="val">
          <App>&lt;null&gt;</App>
          <App-type>java.lang.String</App-type>
          <CancelAll>false</CancelAll>
          <CancelAll-type>java.lang.Boolean</CancelAll-type>
          <CancelPersistent>false</CancelPersistent>
          <CancelPersistent-type>java.lang.Boolean</CancelPersistent-type>
          <CaseinsensitiveApp>false</CaseinsensitiveApp>
          <CaseinsensitiveApp-type>java.lang.Boolean</CaseinsensitiveApp-type>
          <CaseinsensitivePackage>false</CaseinsensitivePackage>
          <CaseinsensitivePackage-type>java.lang.Boolean</CaseinsensitivePackage-type>
          <CaseinsensitiveText>false</CaseinsensitiveText>
          <CaseinsensitiveText-type>java.lang.Boolean</CaseinsensitiveText-type>
          <CaseinsensitiveTitle>false</CaseinsensitiveTitle>
          <CaseinsensitiveTitle-type>java.lang.Boolean</CaseinsensitiveTitle-type>
          <ExactApp>false</ExactApp>
          <ExactApp-type>java.lang.Boolean</ExactApp-type>
          <ExactPackage>false</ExactPackage>
          <ExactPackage-type>java.lang.Boolean</ExactPackage-type>
          <ExactText>false</ExactText>
          <ExactText-type>java.lang.Boolean</ExactText-type>
          <ExactTitle>false</ExactTitle>
          <ExactTitle-type>java.lang.Boolean</ExactTitle-type>
          <InterceptApps>&lt;StringArray sr=""/&gt;</InterceptApps>
          <InterceptApps-type>[Ljava.lang.String;</InterceptApps-type>
          <InvertApp>false</InvertApp>
          <InvertApp-type>java.lang.Boolean</InvertApp-type>
          <InvertPackage>false</InvertPackage>
          <InvertPackage-type>java.lang.Boolean</InvertPackage-type>
          <InvertText>false</InvertText>
          <InvertText-type>java.lang.Boolean</InvertText-type>
          <InvertTitle>false</InvertTitle>
          <InvertTitle-type>java.lang.Boolean</InvertTitle-type>
          <OtherId>&lt;null&gt;</OtherId>
          <OtherId-type>java.lang.String</OtherId-type>
          <OtherPackage>&lt;null&gt;</OtherPackage>
          <OtherPackage-type>java.lang.String</OtherPackage-type>
          <OtherTag>&lt;null&gt;</OtherTag>
          <OtherTag-type>java.lang.String</OtherTag-type>
          <PackageName>&lt;null&gt;</PackageName>
          <PackageName-type>java.lang.String</PackageName-type>
          <RegexApp>false</RegexApp>
          <RegexApp-type>java.lang.Boolean</RegexApp-type>
          <RegexPackage>false</RegexPackage>
          <RegexPackage-type>java.lang.Boolean</RegexPackage-type>
          <RegexText>false</RegexText>
          <RegexText-type>java.lang.Boolean</RegexText-type>
          <RegexTitle>false</RegexTitle>
          <RegexTitle-type>java.lang.Boolean</RegexTitle-type>
          <Text>&lt;null&gt;</Text>
          <Text-type>java.lang.String</Text-type>
          <Title>&lt;null&gt;</Title>
          <Title-type>java.lang.String</Title-type>
          <com.twofortyfouram.locale.intent.extra.BLURB>Id: my-recording</com.twofortyfouram.locale.intent.extra.BLURB>
          <com.twofortyfouram.locale.intent.extra.BLURB-type>java.lang.String</com.twofortyfouram.locale.intent.extra.BLURB-type>
          <net.dinglisch.android.tasker.RELEVANT_VARIABLES>&lt;StringArray sr=""&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;%err
Error Code
Only available if you select &amp;lt;b&amp;gt;Continue Task After Error&amp;lt;/b&amp;gt; and the action ends in error&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;%errmsg
Error Message
Only available if you select &amp;lt;b&amp;gt;Continue Task After Error&amp;lt;/b&amp;gt; and the action ends in error&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;&lt;/StringArray&gt;</net.dinglisch.android.tasker.RELEVANT_VARIABLES>
          <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>
          <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>notificaitionid plugininstanceid plugintypeid </net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>
          <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>java.lang.String</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>
          <net.dinglisch.android.tasker.subbundled>true</net.dinglisch.android.tasker.subbundled>
          <net.dinglisch.android.tasker.subbundled-type>java.lang.Boolean</net.dinglisch.android.tasker.subbundled-type>
          <notificaitionid>my-recording</notificaitionid>
          <notificaitionid-type>java.lang.String</notificaitionid-type>
          <plugininstanceid>da51b00c-7f2a-483d-864c-7fee8ac384aa</plugininstanceid>
          <plugininstanceid-type>java.lang.String</plugininstanceid-type>
          <plugintypeid>com.joaomgcd.autonotification.intent.IntentCancelNotification</plugintypeid>
          <plugintypeid-type>java.lang.String</plugintypeid-type>
        </Vals>
      </Bundle>
      <Str sr="arg1" ve="3">com.joaomgcd.autonotification</Str>
      <Str sr="arg2" ve="3">com.joaomgcd.autonotification.activity.ActivityConfigCancelNotification</Str>
      <Int sr="arg3" val="0"/>
      <Int sr="arg4" val="1"/>
    </Action>
    <Action sr="act5" ve="7">
      <code>43</code>
    </Action>
    <Action sr="act6" ve="7">
      <code>394</code>
      <Bundle sr="arg0">
        <Vals sr="val">
          <net.dinglisch.android.tasker.RELEVANT_VARIABLES>&lt;StringArray sr=""&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;%current_time
00. Current time
&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;%dt_millis
1. MilliSeconds
Milliseconds Since Epoch&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2&gt;%dt_seconds
2. Seconds
Seconds Since Epoch&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES3&gt;%dt_day_of_month
3. Day Of Month
&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES3&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES4&gt;%dt_month_of_year
4. Month Of Year
&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES4&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES5&gt;%dt_year
5. Year
&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES5&gt;&lt;/StringArray&gt;</net.dinglisch.android.tasker.RELEVANT_VARIABLES>
          <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>
        </Vals>
      </Bundle>
      <Int sr="arg1" val="1"/>
      <Int sr="arg10" val="0"/>
      <Str sr="arg11" ve="3"/>
      <Str sr="arg12" ve="3"/>
      <Str sr="arg2" ve="3"/>
      <Str sr="arg3" ve="3"/>
      <Str sr="arg4" ve="3"/>
      <Str sr="arg5" ve="3">yyyy_MM_dd_HH_mm_ss</Str>
      <Str sr="arg6" ve="3"/>
      <Str sr="arg7" ve="3">current_time</Str>
      <Int sr="arg8" val="0"/>
      <Int sr="arg9" val="0"/>
    </Action>
    <Action sr="act7" ve="7">
      <code>547</code>
      <Str sr="arg0" ve="3">%filename</Str>
      <Str sr="arg1" ve="3">%current_time.mp4</Str>
      <Int sr="arg2" val="0"/>
      <Int sr="arg3" val="0"/>
      <Int sr="arg4" val="0"/>
      <Int sr="arg5" val="3"/>
      <Int sr="arg6" val="1"/>
    </Action>
    <Action sr="act8" ve="7">
      <code>547</code>
      <Str sr="arg0" ve="3">%RECORDING</Str>
      <Str sr="arg1" ve="3">1</Str>
      <Int sr="arg2" val="0"/>
      <Int sr="arg3" val="0"/>
      <Int sr="arg4" val="0"/>
      <Int sr="arg5" val="3"/>
      <Int sr="arg6" val="1"/>
    </Action>
    <Action sr="act9" ve="7">
      <code>548</code>
      <Str sr="arg0" ve="3">%filename</Str>
      <Int sr="arg1" val="0"/>
      <Str sr="arg10" ve="3"/>
      <Int sr="arg11" val="1"/>
      <Int sr="arg12" val="0"/>
      <Str sr="arg13" ve="3"/>
      <Int sr="arg14" val="0"/>
      <Str sr="arg15" ve="3"/>
      <Int sr="arg2" val="0"/>
      <Str sr="arg3" ve="3"/>
      <Str sr="arg4" ve="3"/>
      <Str sr="arg5" ve="3"/>
      <Str sr="arg6" ve="3"/>
      <Str sr="arg7" ve="3"/>
      <Str sr="arg8" ve="3"/>
      <Int sr="arg9" val="1"/>
    </Action>
  </Task>
</TaskerData>

Overall, next steps

It looks like there are plenty of things I can do by voice. If I can talk, then I can record a braindump. If I can't talk but I can listen to things, then Emacs TV might be a good choice. If I want to read, I can read webpages or e-books. If my hands are busy, I can still add items to my grocery list or my Orgzly notebook. I just need to practice.

I can experiment with ARIA labels or Web Speech API interfaces on a simpler website, since emacs.tv is a bit complicated. If that doesn't let me do the speech interfaces I'm thinking of, then I might need to look into making a simple Android app.

I'd like to learn more about Orgzly Revived intents. At some point, I should probably learn more about Android programming too. There are a bunch of tweaks I might like to make to Orgzly Revived and the Emacs port of Android.

Also somewhat tempted by the idea of adding voice control or voice input to Emacs and/or Linux. If I'm on my computer already, I can usually just type, but it might be handy for using it hands-free while I'm in the kitchen. Besides, exploring accessibility early will also probably pay off when it comes to age-related changes. There's the ffmpeg+Whisper approach, there's a more sophisticated dictation mode with a voice cursor, there are some tools for Emacs tools for working with Talon or Dragonfly… There's been a lot of work in this area, so I might be able to find something that fits.

Promising!

View org source for this post

Tweaking Emacs on Android via Termux: xclip, xdg-open, syncthing conflicts

Posted: - Modified: | android, emacs

Update: Fixed reference to termux.properties

I’m planning to leave my laptop at home during our 3-week trip, so I’ve been shifting more things to my phone in preparation. Orgzly is handy for reviewing my agenda and adding items on the go, but nothing beats Emacs for full flexibility. Runnng Emacs via Termux works pretty well. I can resize by pinch-zooming, scroll by swiping, and even enter in more text by swiping right on the row of virtual keyboard buttons.

Here’s what I’ve configured in ~/.termux/termux.properties:

extra-keys = [['ESC','/','-','HOME','UP','END','PGUP','F5'],['TAB','CTRL','ALT','LEFT','DOWN','RIGHT','PGDN','F6']]

and here’s what that looks like:

Screenshot of Emacs

I patched my Termux to allow the use of function keys in the extra keys shortcut bar. It’s been merged upstream, but the new version hasn’t been released yet, so I’m still running the one I compiled from source. It would be nice to fix accidental keypresses when swiping extra keys to get to the input field, but that can wait a bit.

I set up Syncthing to synchronize files with my server and laptop, Termux:API to make storage accessible, and symlinks in my home directory to replicate the main parts of my setup. I set up Orgzly to auto-sync with a local repository (synchronized with my server and laptop via Syncthing) and the same Org files set up in my agenda in Emacs on Termux. That way, I can hop between Orgzly and Emacs as quickly as I want. Here are a few tweaks that made Emacs even better.

First, a little bit of code for phone-specific config, taking advantage of the weird automatic username I have there.

(defun my/phone-p ()
  (and (equal (system-name) "localhost") 
       (not (equal user-login-name "sacha"))))

For Emacs News, I wanted to be able to easily open Org Mode links to webpages by tapping on them. This code makes that work (and in general, anything that involves opening webpages):

(setq browse-url-browser-function 'browse-url-xdg-open)

This piece of code cleans up my Inbox.org file so that it’s easier to skim in Orgzly. It archives all the done tasks and sorts them.

     (defun my/org-clean-up-inbox ()
       "Archive all DONE tasks and sort the remainder by TODO order."
       (interactive)
       (with-current-buffer (find-file my/org-inbox-file)
         (my/org-archive-done-tasks 'file)
         (goto-char (point-min))
         (if (org-at-heading-p) (save-excursion (insert "\n")))
         (org-sort-entries nil ?p)
         (goto-char (point-min))
         (org-sort-entries nil ?o)
         (save-buffer)))

     (defun my/org-archive-done-tasks (&optional scope)
       "Archive finished or cancelled tasks.
SCOPE can be 'file or 'tree."
       (interactive)
       (org-map-entries
        (lambda ()
          (org-archive-subtree)
          (setq org-map-continue-from (outline-previous-heading)))
        "TODO=\"DONE\"|TODO=\"CANCELLED\"" (or scope (if (org-before-first-heading-p) 'file 'tree))))

I also sometimes wanted to copy and paste between Termux and Emacs by using the keyboard, so I submitted a patch for xclip.el so that it would detect and work with termux-clipboard-get/set. That code is now in xclip 1.9 in ELPA, so if you M-x package-install xclip, you should be able to turn on xclip-mode and have it copy and paste between applications. In my config, that looks like:

(when (my/phone-p)
  (use-package xclip :config (xclip-mode 1)))

Because I use Orgzly and Termux to edit my Org files and I also edit the files on my laptop, I occasionally get synchronization errors. I came across this handy bit of code to find Syncthing conflicts and resolve them. I just had to change some of the code (in bold below) in order to make it work, and I needed to pkg install diffutils to solve the diff errors. Here’s the fixed code below, along with a convenience function that checks my Orgzly directory:

(defun my/resolve-orgzly-syncthing ()
  (interactive)
  (ibizaman/syncthing-resolve-conflicts "~/cloud/orgzly"))

(defun ibizaman/syncthing-resolve-conflicts (directory)
  "Resolve all conflicts under given DIRECTORY."
  (interactive "D")
  (let* ((all (ibizaman/syncthing--get-sync-conflicts directory))
        (chosen (ibizaman/syncthing--pick-a-conflict all)))
    (ibizaman/syncthing-resolve-conflict chosen)))

(defun ibizaman/syncthing-show-conflicts-dired (directory)
  "Open dired buffer at DIRECTORY showing all syncthing conflicts."
  (interactive "D")
  (find-name-dired directory "*.sync-conflict-*"))

(defun ibizaman/syncthing-resolve-conflict-dired (&optional arg)
  "Resolve conflict of first marked file in dired or close to point with ARG."
  (interactive "P")
  (let ((chosen (car (dired-get-marked-files nil arg))))
    (ibizaman/syncthing-resolve-conflict chosen)))

(defun ibizaman/syncthing-resolve-conflict (conflict)
  "Resolve CONFLICT file using ediff."
  (let* ((normal (ibizaman/syncthing--get-normal-filename conflict)))
    (ibizaman/ediff-files
     (list conflict normal)
     `(lambda ()
       (when (y-or-n-p "Delete conflict file? ")
         (kill-buffer (get-file-buffer ,conflict))
         (delete-file ,conflict))))))

(defun ibizaman/syncthing--get-sync-conflicts (directory)
  "Return a list of all sync conflict files in a DIRECTORY."
  (directory-files-recursively directory "\\.sync-conflict-"))

(defvar ibizaman/syncthing--conflict-history nil 
  "Completion conflict history")

(defun ibizaman/syncthing--pick-a-conflict (conflicts)
  "Let user choose the next conflict from CONFLICTS to investigate."
  (completing-read "Choose the conflict to investigate: " conflicts
                   nil t nil ibizaman/syncthing--conflict-history))


(defun ibizaman/syncthing--get-normal-filename (conflict)
  "Get non-conflict filename matching the given CONFLICT."
  (replace-regexp-in-string "\\.sync-conflict-.*\\(\\..*\\)$" "\\1" conflict))


(defun ibizaman/ediff-files (&optional files quit-hook)
  (interactive)
  (lexical-let ((files (or files (dired-get-marked-files)))
                (quit-hook quit-hook)
                (wnd (current-window-configuration)))
    (if (<= (length files) 2)
        (let ((file1 (car files))
              (file2 (if (cdr files)
                         (cadr files)
                       (read-file-name
                        "file: "
                        (dired-dwim-target-directory)))))
          (if (file-newer-than-file-p file1 file2)
              (ediff-files file2 file1)
            (ediff-files file1 file2))
          (add-hook 'ediff-after-quit-hook-internal
                    (lambda ()
                      (setq ediff-after-quit-hook-internal nil)
                      (when quit-hook (funcall quit-hook))
                      (set-window-configuration wnd))))
      (error "no more than 2 files should be marked"))))

If you use Emacs on Android via Termux, I hope some of these tweaks help you too!

Reviving my Asus Transformer TF700T with the KatKiss ROM

Posted: - Modified: | android, geek

Buying the TF700T had been a mistake. It was ahead of its time and not powerful enough for the tablet it wanted to be. I hadn’t given myself enough time to try it out during the return period, so I was stuck with it. I tried reflashing it with other ROMs like CROMBi-kk, but the lack of responsiveness still drove me crazy. I put the tablet in our old electronics bin and moved on. It survived a number of e-recycling purges through the years partly because it looked in such good condition that it would be a shame to throw away, and partly because it was too frustrating a machine to inflict on anyone else.

Now we find ourselves with a toddler who wants to type. W- fixed up his old X220 tablet PC to boot to console mode with 640×480 resolution so that the text is easy to see, but it’s heavy and has poor battery life. A- declared the Sony Vaio U1 to be too small for her, so we dusted off the TF700T and W- found the charger. It was still frustratingly slow. We want computing to be pleasant. I didn’t want to give up hope, though, especially since I’d found surprisingly recent Reddit threads about people using the TF700T.

Formatting the tablet took longer than I thought it would, but fortunately the forum posts reassured me that I didn’t mess it up. After that, I reflashed it to KatKiss Nougatella following the instructions for reflashing the TF700, it actually became somewhat usable. I installed a text editor and an SSH client, docked it into the keyboard, and let A- play.

A-‘s okay with using the TF700, although she misses using F1 to bring up the help screen in Vim. (W-‘s influence! Maybe I can sneak in some Emacs if I remap Emacs’ F1 to bring up something like view-hello-fileā€¦) We’re still leaning towards the X220 since it’s more configurable, but the TF700 can be good for guided exploration too.

We don’t care about making sure A- learns how to type so early, and she’s got plenty of time to do other non-computer things. But sometimes she sees W- working on his laptop and she wants to do it too, so she might as well do something useful. I kinda like how her interface is pretty basic. No whizbang animations enticing her to play, just the feedback of seeing text appear on the screen as she presses buttons. She can toggle Caps Lock to make uppercase and lowercase letters, and she knows how to make “?” by pressing Shift with another key. She can spell her name if we tell her which letters to look for. If she happens to type 1 and 0 in the process of banging on the keyboard, she reads it out as “ten.” We’ll let her explore when that’s what she’s curious about, and we’ll also draw her away from it with lots of other activities and by keeping it out of sight as needed.

We have another under-utilized Android tablet. The TF700T’s special because it has a docking keyboard and therefore passes A-‘s “Is this a laptop?” test, while the tablet + Bluetooth keyboard combination does not. I wonder what we’ll end up doing with it. Who knows, if the battery life isn’t dismal, I might even end up using it for writing once A- is old enough for drop-off classes.

Hooray for people working on making old tech more usable!

Decision review: Samsung Note 8

| android, decision, geek, review

It was quite a big jump going from a Moto G (2nd gen, bought in 2015) to a Samsung Note 8 this year, mostly on my dad's insistence. Here's how I've been working on making the most of my new phone's capabilities.

  • Camera: Way more pictures of A-, since the camera works decently even in low light. I like how it reminds me to clean the camera lens.
  • Better battery life, quick charge: This is great. I used to keep W-‘s old Moto G handy so that I could swap to it if my phone battery ran low while I wrote in bed. Now I don't even worry about charging overnight, since I can charge my phone while having breakfast with A-.
  • More storage: It's nice not having to decide which apps I have space for on my phone. I even used the SD card to copy thousands of archived photos into Google Photos.
  • Pen: Screen-off notes are really quick to scribble down, since all I need to do is take out the pen and start writing. I trust writing more than typing for taking fast notes. I also really like the ability to select part of the screen and extract text, because sometimes apps don't make it easy to select and copy text. I've used the pen to draw and colour illustrations for a book for A-. It's more awkward than using my tablet PC or the iPad, but it lets me use phone time to get more things done, so it's worth it.
  • Fingerprint scanner: Surprisingly handy way of reducing the friction of using my phone. I keep my phone locked when the screen is off so that A- doesn't play with it, and tapping the fingerprint sensor to unlock the phone saves me a bit of fumbling around.
  • Voice and gesture control for pictures: Great for taking pictures, since it's not easy to hold and shoot with one hand.
  • Speech recognition in general: I've been getting the hang of this now that battery life means I'm a little less worried about leaving my phone listening all the time. I have a few shortcuts for tracking activities, and I often set timers and add grocery items by voice too. It's still not quite reliable, but it's worth a shot. A- occasionally parrots “Okay Google” and “Command sent,” so I try to model saying “please” when I ask my phone to do things. I haven't used Bixby as much, since voice wake-up for Bixby interferes with voice control of pictures.
  • Larger screen: This was a little hard to get used to, since the bigger screen makes it hard to put my phone in my pocket or hold in bed. But it does make it a little more manageable to ssh into my server and do things, although working with a virtual keyboard is still annoying. I've also used the split screen feature a few times.
  • Edge apps: I've used the clipboard and the ruler a few times.
  • Live focus: I've used this a few times, but I usually don't have much time to compose a picture of A-. Maybe when she's a bit older, or if I think about it more.

I want to explore more of the camera's features, figure out a good workflow for photos, learn more about what I can do with speech recognition, and get better at workflows for notes (thoughts, images, and e-books).

I wonder if it makes sense to draw more on my phone than on paper even when A- is around. I haven't been keen on doing so because she usually wants to imitate me, and she gets more out of drawing on paper than on my phone. Maybe I'll draw on paper and take quick pictures on my phone instead.

I probably won't upgrade for a while, since it's likely to be a bit of a hassle selling this phone and upgrading to a new one. I might be tempted by an even better camera in the next phone. After all A- is only this age once. I really like the stylus. That means keeping an eye on the Samsung Note line and figuring out when it makes sense to upgrade. I hope this phone is sturdier than the Samsung Galaxy S3 I experimented with years ago. This one has survived life with a toddler so far, so that's good. I spend more time on my phone than on my laptop these days, so it'll be interesting to see how I can make the most of it.

Daily, weekly, and monthly journals: my Memento + Google Sheets + Tasks Free + Google Tasks + WordPress workflow

Posted: - Modified: | android, blogging, writing

Journaling considerations:

  • A- nurses a lot in bed. I keep my phone handy and I write when she doesn’t want to let me go.
  • I also jot quick notes throughout the day so that I don’t have to keep them in my head. These go into the nearest synchronized device.
  • It’s hard to remember the context for those notes if too much time passes. A daily verbal recap for W- and a weekly summary for my blog seem to be just the right balance. Anything older than a week gets too fuzzy, while writing detailed notes every day takes too much time away from other things I’d like to do.
  • Monthly reviews give me a better perspective on big changes. It’s hard to keep enough in my head when I’m reading or writing on my phone, so I need help summarizing a month’s worth of highlights.

Here are the technical details:

I set up Memento Database on my phone and on a backup Android phone. I picked it because it can synchronize between phones in the background, and it can also sync with Google Sheets so that I can process things further.

My journal database has the following fields:

  • Date: defaults to current date
  • Note
  • Category: single value from a list. Most of my entries go into Gross Motor, Fine Motor, Language, Self-care, Other, or Us, and I add other categories as needed.
  • Highlight: a number indicating the level of review this should be included in: 1 – weekly, 2 – monthly, 3 – yearly. I display this field as the status, so that it shows up on the right side.

I have a shortcut on my home screen so that I can quickly add a journal entry.

I normally sort the list by date, with recent entries on top.

As part of my weekly review, I look at recent entries, fill in any categories I skipped, and choose a few to highlight. For example, last week, I wrote 17 entries and I chose 13 to include in the weekly review.

I configured Memento’s default export formatting to include only the Note field and to export that without the field label.

I filtered the database to show only the entries within a given date range where the highlight value was greater than 0.5.

I grouped it by category so that similar entries were together. This was better than fiddling with the sorting, since this takes fewer taps to set back to my default view.

After filtering and grouping the entries, I used the “Send all > Send as text” command to send it to Tasks Free, which is a task manager that synchronizes with Google Tasks. I like the way I can drag-and-drop tasks to reorder them, which makes prioritizing so much easier on my phone. I edit the text in Tasks Free, turning the keywords into paragraphs and moving things around for better flow.

After drafting the body of the post (and possibly switching between phones, if my battery ran low), I select all the text, copy it into the WordPress app, set the categories and the title, and post the entry.

The monthly review process is quite similar. I start with a filtered view that shows all entries for last month (133 entries in November), and I group it by category. I skim all the entries, not just the ones included in the weekly review, because sometimes little moments turn out to be significant or part of a bigger pattern. After setting the highlight values for the things I’d like to include in my monthly review, I switch to another filter that shows me last month’s entries with a highlight value greater than 1.5 (28 entries in November). I send it all to Tasks Free, edit the post, copy it into WordPress, and publish.

If I manage to squeeze in some computer time, I use Google Tasks to copy the text into Emacs and then use my regular Org Mode review/publish processes.

I’ve been thinking about how I can improve this workflow. Sending text to the WordPress app doesn’t seem to work (the text disappears after I save or publish), and it’s kinda nice being able to move my weekly review task around on my task list in order to accommodate other priorities. I also like the way Google Tasks keeps the data from completed tasks, which has come in handy a few times. Tasks Free editing is more responsive, too. Synchronizing with Tasks Free seems to be more robust than synchronizing with Orgzly, since I only have to watch out for editing the same task on two devices instead of watching out for the whole file.

I’d like to get back to drawing the weekly and monthly reviews, but maybe that can wait until A-‘s sleep is more settled and my discretionary time is more consolidated. The visual journals are more fun to flip through, but the bulk and chronological views I hacked into my WordPress theme are reasonable workarounds.

Recreating and enhancing my tracking interface by using Tasker and Javascript

| android, geek

I got tired of setting up Tasker scripts by tapping them into my phone, so I looked into how to create Tasker interfaces using Javascript. First, I created a folder in Dropbox, and I used Dropsync to synchronize it with my phone. Then I created a simple test.html in that folder. I created a Tasker scene with a WebView that loaded the file. Then I started digging into how I can perform tasks, load applications, and send intents to Evernote so that I can create notes with pre-filled text. I really liked being able to reorder items and create additional screens using Emacs instead of Tasker’s interface.

Here’s my code at the moment. It relies on other Tasker tasks I’ve already created, so it’s not a standalone example you can use right off the bat. Still, it might be useful for ideas.

tasker-scripts/test.html:

<html>
    <head>
        <title>Sacha's personal tracking interface</title>
        <style type="text/css">
         button { padding: 20px; font-size: large; width: 45%; display: inline-block  }
        </style>
        <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
    </head>
    <body>
        <div id="feedback"></div>
        <!-- For making it easy to track things -->
        <div class="screen" id="main-screen">
        <button class="note">Do</button>
        <button class="note">Think</button>
        <button class="note">Note</button>
        <button class="note">Journal</button>
        <button class="switch-screen" data-screen="track-screen">Track</button>
        <button class="switch-screen" data-screen="play-screen">Play</button>
        <button class="switch-screen" data-screen="eat-screen">Eat</button>
        <button class="switch-screen" data-screen="energy-screen">Energy</button>
        <button id="reload">Reload</button>
        </div>
        <div class="screen" id="play-screen">
            <button class="play">Persona 3</button>
            <button class="play">Ni No Kuni</button>
            <button class="play">Hobbit</button>
            <button class="switch-screen"
                    data-screen="main-screen">Back</button>
        </div>
        <div class="screen" id="energy-screen">
            <button class="energy">5</button><br />
            <button class="energy">4</button><br />
            <button class="energy">3</button><br />
            <button class="energy">2</button><br />
            <button class="energy">1</button><br />
            <button class="switch-screen"
                    data-screen="main-screen">Back</button>
        </div>
        <div class="screen" id="eat-screen">
            <button class="eat">Breakfast</button>
            <button class="eat">Lunch</button>
            <button class="eat">Dinner</button>
            <button class="switch-screen"
                    data-screen="main-screen">Back</button>
        </div>
        <div class="screen" id="track-screen">
            <button class="update-qa">Routines</button>
            <button class="update-qa">Subway</button>
            <button class="update-qa">Coding</button>
            <button class="update-qa">E1 Gen</button>
            <button class="update-qa">Drawing</button>
            <button class="update-qa">Cook</button>
            <button class="update-qa">Kitchen</button>
            <button class="update-qa">Tidy</button>
            <button class="update-qa">Relax</button>
            <button class="update-qa">Family</button>
            <button class="update-qa">Walk Other</button>
            <button class="update-qa">Nonfiction</button>
            <button class="update-qa">Laundry</button>
            <button class="update-qa">Sleep</button>
            <button id="goToWeb">Web</button>
            <button class="switch-screen" data-screen="main-screen">Back</button>
        </div>
        <script>
         function updateQuantifiedAwesome(category) {
             performTask('Update QA', null, category);
             hideScene('Test');
         }

         function showFeedback(s) {
             $('#feedback').html(s);
         }
         function switchScreen(s) {
             $('.screen').hide();
             $('#' + s).show();
         }

         $('.switch-screen').click(function() {
             switchScreen($(this).attr('data-screen'));
         });
         function createEvernote(title, body) {
             sendIntent('com.evernote.action.CREATE_NEW_NOTE', 'activity',
                        '', '', 'none', '', '',
                        ['android.intent.extra.TITLE:' + (title || ''),
                         'android.intent.extra.TEXT:' + (body || '')]);
         }
         $('.note').click(function() {
             createEvernote($(this).text());
         });
         $('.energy').click(function() {
             createEvernote('Energy', 'Energy ' + $(this).text() + ' ');
             switchScreen('main-screen');
         });
         $('#reload').click(function() {
             performTask('Reload Test');
         });
         $('.update-qa').click(function() {
             updateQuantifiedAwesome($(this).attr('data-cat') || $(this).text());
             hideScene('Test View');
         });
         $('#goToWeb').click(function() {
             browseURL('http://quantifiedawesome.com');
         });
         $('.eat').click(function() {
             updateQuantifiedAwesome($(this).text());
             loadApp('MyFitnessPal');
         });
         $('.play').click(function() {
             performTask('Play', null, $(this).text());
         });

         switchScreen('main-screen');

         </script>
    </body>
</html>

You can find the latest version at https://github.com/sachac/tasker-scripts.

Thinking about simplifying capture on my phone

Posted: - Modified: | android, geek

I’ve been thinking about temporary information: things like where I put down something I was holding, the task I’m working on just in case I get interrupted, thoughts that I want to explore later on.

The seeds of a good system are there, if I learn how to use them more effectively. I usually have my phone handy. Evernote can record audio, pictures, or text, and the creation date is an automatic timestamp. I can export the notes and process them using Emacs. Google Now’s “Note to self” command can create a note in Evernote, or I can tap the Create Note icon.

How can I improve how I use these tools?

If I get used to starting my note lines with a special keyword, then it’ll be easier for me to extract those lines and process them. For example:

DO Actions to add to my to-do list
THINK Thoughts to explore
PLACE Putting things down

If I add custom commands to Google Now, or make Google Now the fallback for another command line, then I can use that for my tracking system as well. AutoShare and AutoVoice might be handy. I should probably learn how to get Tasker working with Javascript, too. Alternatively, I can use Evernote’s e-mail interface, although there might be a slight delay if I’m offline.

If I create a custom single-click interface that can start the note with a specified keyword, that would be even better.

I could also use a more systematic review process. For example:

  • All THINK items can be automatically added to the end of my

questions.org as their own headings.

  • All DO items can be added to my uncategorized tasks.

Okay, let’s start by figuring out Javascript and Tasker, since that will make it easier for me to write actions that take advantage of intents.

First step is to save the JS library template from Tasker. The file is stored in /storage/emulated/0/Tasker/meta/tasker.js. Okay, I’ve created a Tasker scene that has a WebView component that loads the file that I synchronized with Dropsync.

The next step is to simplify development so that I can try things quickly. I want to be able to sync and reload my WebView scene by tapping a button. The Dropsync Tasker action returns immediately, so maybe I’ll just add a wait to it.

Hmm, maybe a good path might be:

  1. Set up easy sync/reload, so I can try things out quickly.
  2. Include JQuery.
  3. Run tasks.
  4. Launch apps.
  5. Send intents.
  6. Convert my current tracking menu to this format.
  7. Add buttons for creating notes (either e-mail-based or intent-based):
    • Do
    • Think
    • Place
  8. Add a command-line. Compare using it vs. using buttons.

On a related note, what kinds of things would I like my phone to be smart enough to do?

  • If I’m at home and I’m calling my cellphone from our home phone, set the ring volume to maximum, since that means I’m trying to find it.
  • If I say that I’m at a social event, ask me who I’m spending time with. Track that. Create a note with the person’s name as the title and “social” as the tag.
  • If I’m going to sleep, track that, then start tracking in Sleep as Android.
  • If I’m going to play a game, track that, then ask me what I’m going to do afterwards and how long I want to play. Load hints for the game (if I want). After the specified time, make a sound and remind me of what I was going to do.
  • If I’m going to read a book, show me the list of my checked-out books and let me pick one of them. Track that, then create a note with the title and author so that I can take notes.
  • NFC opportunities:
    • If I scan at the door, show me a menu: walk, subway, groceries, family, bike.
    • If I scan in at the kitchen table, track it as breakfast / lunch / dinner (as appropriate), then launch MyFitnessPal.
    • If I scan in at the table in front of the TV, show me a menu: relax, nonfiction, fiction, games.
    • If I scan in at my bedside table, treat it as going to sleep.

Mmm… Little things to tweak. =)