Nithitsuki's Website

posts abt-me homelab others


setup emacs (with journaling) on your phone

guide to setup emacs and org-capture shortcut on an android

Hover over a footnote to read it in-place

Install termux and emacs

You can grab termux from F-Droid or GitHub.

The first boot takes a while, Once its done, install emacs:

pkg update
pkg upgrade -y
pkg install -y emacs git ripgrep fd

We'll stick to the terminal build for this, not the separate GUI emacs android app for reasons1

Where the files lives

Emacs can write in two kinds of places on termux:

  • The termux home, for example ~/Documents/org/. It stays inside termux's directory, and other apps can't read it.
  • System storage, for example /storage/emulated/0/Documents/org/. Other apps can read it too. You need to run termux-setup-storage to get access to it

    termux-setup-storage

Its best to keep them in a shared directory if you want your files to be shared or synced2 between devices.

Porting your config over to your phone

Emacs reads its config from the termux home, either ~/.emacs.d/ or ~/.config/emacs/.3 It's easier to keep the same layout as your computer.

You can bring your own existing config, here are some ways:

  • git: git clone <url> ~/.config/emacs
  • ssh: scp -r config/ phone:~/.config/emacs, with sshd on the phone.4
  • adb: adb push config /sdcard/Download/, then cp -rv in termux.
  • USB-OTG: copy with the android file manager, then cp -rv in termux.

It's best to not copy the packages or cache folders. Let emacs install its packages on the phone.5

Android specific emacs tweaks

Know that you're in termux

(defvar my/termux-p (or (getenv "TERMUX_VERSION")
                        (file-exists-p "/data/data/com.termux")))

The environment variable alone isnt enough as termux only sets it in shells started by it.6

Wrap the GUI-only parts

Fonts, transparency, margins, and image sizes only matter in a GUI. You can wrap them:

(unless my/termux-p
  (setq doom-font (font-spec :family "SF Mono" :size 14))
  (add-to-list 'default-frame-alist '(alpha-background . 85)))

If the journal is on shared storage, you'll also want to keep emacs litter out of it.7

(when my/termux-p
  (let ((auto (expand-file-name "auto-save/" user-emacs-directory))
        (backup (expand-file-name "backups/" user-emacs-directory)))
    (make-directory auto t)
    (make-directory backup t)
    (setq create-lockfiles nil
          backup-directory-alist `(("." . ,backup))
          auto-save-file-name-transforms `((".*" ,auto t)))))

Clipboard if you like it

Emacs can use the android clipboard through Termux:API.8

pkg install termux-api
(when (and my/termux-p (require 'xclip nil t))
  (xclip-mode 1))

You can point org-directory at the folder you picked in section 2, and doom users can drop it in ~/.config/doom/config.el.9

Pick a capture method

Your journal can be one or more org files. org-capture appends to one file. org-journal writes multiple files based on your config.

Here's a small setup for each

org-journal: one file per day

org-journal reads the date from the filename, so the format needs %Y, %m, and %d.10

(setq org-journal-dir "~/Documents/org/journal/"
      org-journal-file-format "%Y%m%d"
      org-journal-date-format "%A, %m/%d/%Y")

Doom's (org +journal) flag turns this on and gives you SPC n j j. Other configs usually bind org-journal-new-entry somewhere.

org-capture: one file

(setq org-capture-templates
      `(("j" "Journal Entry" entry
         (file+olp+datetree ,(expand-file-name "Journal.org" org-directory))
         "* %<%H:%M>\n%?"
         :empty-lines 1
         :unnarrowed t)))

Newer org deprecates file+datetree, so this uses file+olp+datetree.11 Doom binds SPC X j for it.

Either way, C-c C-c files the entry, and C-c C-k throws it away. Closing the terminal does neither.12

One tap capture

And finally, a home screen shortcut that opens a capture in a new emacs frame.

Prep termux

Install Termux:Widget from the same source as termux. The widget runs scripts through termux, so termux must allow commands from other apps, and a warm daemon to reduce boot times.

echo "allow-external-apps=true" >> ~/.termux/termux.properties
termux-reload-settings
emacs --daemon

The shortcut script

Save this as ~/.shortcuts/Journal and make it executable. The filename becomes the label on the home screen.13

#!/data/data/com.termux/files/usr/bin/bash
export PREFIX=/data/data/com.termux/files/usr
export HOME=/data/data/com.termux/files/home
export PATH=$PREFIX/bin:/system/bin
export LD_LIBRARY_PATH=$PREFIX/lib
export TERM=xterm-256color

# start a daemon if the server is not up yet
emacsclient --eval t >/dev/null 2>&1 || emacs --daemon
for _ in $(seq 1 40); do
  emacsclient --eval t >/dev/null 2>&1 && break
  sleep 0.25
done

emacsclient -t --eval '(progn (setq my/capture-return-home t) (org-capture nil "j"))'

# a dead frame can leave the daemon stuck inside the capture
if ! timeout 5 emacsclient --eval '(condition-case nil
    (dolist (b (buffer-list))
      (when (string-prefix-p "CAPTURE-" (buffer-name b))
        (with-current-buffer b (org-capture-kill))))
    (error nil))' >/dev/null 2>&1; then
  pkill -x emacs
fi

am start -a android.intent.action.MAIN -c android.intent.category.HOME >/dev/null 2>&1

The script expects a small flag and a hook in your config, so the frame closes after the capture:14

(defvar my/capture-return-home nil)
(defun my/journal-capture-finish (&rest _)
  (when my/capture-return-home
    (setq my/capture-return-home nil)
    (delete-frame)))
(advice-add 'org-capture-finalize :after #'my/journal-capture-finish)
(advice-add 'org-capture-kill :after #'my/journal-capture-finish)

Add the widget

Long press the home screen, open the widget list, find Termux:Widget, and drag the shortcut to a free spot. Pick Journal. A tap opens today's entry.15

Footnotes

Other resources

Guides that cover similar teritory:

More References:


1

The GUI app is a different emacs. It has its own config directory, so termux config and packages don't carry over. Doom doesn't officially support it yet. It also can't use git, ripgrep, and other termux programs. The terminal one feels more at-home and is easier for things like shortcuts

2

Some options are Syncthing, git, Nextcloud, or a USB copy. If you sync, it's safer to change one copy at a time and have a versioning system in the sync tool.

3

If both ~/.emacs.d/ and ~/.config/emacs/ exist, emacs uses ~/.emacs.d/. Doom keeps its own files in ~/.config/emacs and your private config in ~/.config/doom.

4

If you'd like ssh, run pkg install openssh and then sshd in termux. It listens on port 8022, and you can connect via ssh -p 8022 <user>@<phone-ip>.

5

You only need the init files and config folders your setup uses, for example init.el, early-init.el, lisp/, and snippets/. It's best to skip .local/, elpa/, eln-cache/, and straight/, since they're machine specific. Doom users can just copy ~/.config/doom and run doom sync 😎

6

Termux only exports TERMUX_VERSION to the shells it starts. An emacs started over ssh, or by the widget may not see it. The data directory is a more reliable way

7

Syncthing treats lock files and autosaves as changes. They add noise and can cause conflicts. Keeping them inside user-emacs-directory means they wont reach the shared folder.

8

Termux:API is an app plus a package. Install both from the same source as termux. xclip.el has a termux backend, and doom's :os tty module loads it for you. For a non-doom config, install xclip from ELPA if not already

9

The line is (setq org-directory "/storage/emulated/0/Documents/org/") for shared storage, or ~/Documents/org/ for the termux home. Doom's private config is in ~/.config/doom/. In non-doom emacs, put it in init.el.

10

A daily journal needs %Y, %m, and %d in the filename. A weekly journal needs %G and %V or the date parts, a monthly one needs %Y and %m, and a yearly one only needs %Y. This is also why org-journal can't keep a single lifetime file.

11

file+olp+datetree is the current name for the old file+datetree. The path can be absolute or built from org-directory with expand-file-name. :unnarrowed keeps the whole file visible, so you can scroll back to earlier entries while you write.

12

Closing the session leaves the entry unsaved. C-c C-c writes it to the file. C-c C-k aborts, and org may put the text in the kill ring instead of the file. Neither is a save.

13

~/.shortcuts holds shortcuts that run in a terminal session. The file needs execute permission, so run chmod 700 ~/.shortcuts/Journal. The am start at the end needs "Display over other apps" for termux on android 10 and later, or the launcher can stay behind the terminal.

14

emacsclient -t opens a terminal frame. Finishing or aborting deletes that frame, so you land back on the launcher. If the frame dies first, the daemon can be stuck inside the capture and stop answering. The timeout clears the capture, or restarts emacs.

15

For org-journal, replace the org-capture call with org-journal-new-entry. Whatever you use, finish with C-c C-c. The back button and closing the session do not save.


Webmention this page

Have you written a post that links to this page? Paste the URL and I'll fetch it.

Leave a comment

Powered by zapiska - webmentions & comments for your IndieWeb needs!