Sharing file with my Eink device that runs Android

by mafty — Fri 30 December 2022

The problem

I have a Boox Max Lumi 2 tablet that I use daily for reading and note taking. A nice feature of Boox devices is that they are running Android systems, with gives certain promise of automation, and integration with Linux desktop environments.

One minor issue that has been disrupting the smoothness of my workflow is file sharing. Previously I've been using Dropbox and instant messaging softwares to transfer files from my Linux laptop to the tablet. But running android applications designed for common high-refresh-rate devices on an Eink device is a true pain in the ass.

And there is a solution

The first solution that came to my mind is to maintain a copy of my library on the tablet <sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>. However, on second though this isn't practical. In fact, most of my books are kept to comply with the way citar finds file from bibtex entries: most of my books have name in the format citekey.{pdf,djvu} where the citekeys may not have any meaning at all. Most of the time, I'm relying on the completing-read interface provided by citar to fuzzy find books by their authors and titles. However, they's not obvious way I could replicate this workflow on a eink device which is extremely sluggish in response to keyboard input. Therefore, even if I have a full copy of my library on the device, there's no way I can quickly find the file I want.

With that said, the only option left is to find files on my laptop first, and then quickly transfer them to the tablet. For the second part, keconnect quickly came to my mind. Luckily, the program provided a cli interface that can help me automate things with comfort.

After successively pairing with the device, I can see an id for my device:

% kdeconnect-cli -l
- MaxLumi2: xxxxxxxxxxxxxxx (paired and reachable)
- Pixel 7: xxxxxxxxxxxxxxx (reachable)
2 devices found

I also found that I can transfer files with the shell command kdeconnect-cli -d <id> --share <file>. At this point we have all the ingredients for sending file to the tablet inside Emacs.

tinkering with elisp

Put device id in a variable:

(defvar kdeconnect-boox-id "xxxxxxxxxxxxxxx")

Check if the device is available:

(defun boox-ok? ()
  "Check if the boox device is available for file transfer."
  (let ((str (shell-command-to-string "kdeconnect-cli -l")))
    (string-match-p (format "%s (paired and reachable)" kdeconnect-boox-id) str)))

Send currently file to the device:

(defun send-file-to-boox-if-connected (arg)
  "With prefix, read file to send from minibuffer; otherwise
- in dired-mode, send file under cursor;
- if current buffer is associated with a file, send the file."
  (interactive "P")
  (if (boox-ok?)
      (let ((*fn* (if arg
                      (read-file-name "Select file to send: ")
                    (if (eq major-mode 'dired-mode)
                        (dired-get-filename)
                      (buffer-file-name)))))
        (if *fn*
            (async-shell-command (concat (format "kdeconnect-cli -d %s --share " kdeconnect-boox-id)
                                         (shell-quote-argument *fn*)))
          (message "Not a file.")))
    (message "Device not ready.")))

Integration with embark (so that I can send a file with the following routine: citar-open-library-file → fuzzy find → select file → embark-act → "X"). It's enough to write a function that takes a filename and does the thing I want, and then add it to the embark keymap that acts on files (the keymap can be found in embark-keymap-alist).

    ;; embark integration
    (defun boox-send-file (fn)
      (if (boox-ok?)
          (async-shell-command (concat (format "kdeconnect-cli -d %s --share " kdeconnect-boox-id)
                                       (shell-quote-argument fn)))
        (message "Device not ready.")))
    
    (with-eval-after-load "embark" (define-key embark-file-map (kbd "X") #'boox-send-file))

Footnotes

<sup><a id="fn.1" href="#fnr.1">1</a></sup> Theoretically, this is totally possible because I can run services inside Termux.