]> git.eshelyaron.com Git - emacs.git/commitdiff
Enable opening mailto URLs under Android
authorPo Lu <luangruo@yahoo.com>
Fri, 22 Sep 2023 01:36:40 +0000 (09:36 +0800)
committerPo Lu <luangruo@yahoo.com>
Fri, 22 Sep 2023 01:36:40 +0000 (09:36 +0800)
* doc/emacs/android.texi (Android Startup): Mention how mailto
URLs are treated by the emacsclient wrapper.

* java/AndroidManifest.xml.in: Register `mailto' scheme filters
for EmacsOpenActivity.

* java/org/gnu/emacs/EmacsOpenActivity.java (startEmacsClient):
Extract code that starts Emacs when it isn't already running,
and take a list of arguments rather than a single file name.
(onCreate): If the scheme is `mailto', escape the URI and call
`message-mailto'.

doc/emacs/android.texi
java/AndroidManifest.xml.in
java/org/gnu/emacs/EmacsOpenActivity.java

index b8862b244d2de1959e59b46e9d48aa39f6babdd1..07b689ca23b5aba230de97897533c93172bd7134 100644 (file)
@@ -158,6 +158,13 @@ opened.
 @command{emacsclient} wrapper as a program capable of opening
 ``org-protocol'' links (@pxref{Protocols,,,org, The Org Manual}).
 
+@cindex ``mailto'' links, android
+  Furthermore, the wrapper is also registered as a program capable of
+sending mail to @code{mailto} URIs; when it is invoked to open such a
+URL, it calls the function @code{message-mailto} with that URI as its
+first argument.  This feature does not function when the Emacs server
+is not already running.
+
 @node Android File System
 @section What Files Emacs Can Access on Android
 @cindex /assets directory, android
index 9044725640dabf1cd884652963f99b1bf3b37c4b..d4017a055dd1b02162a41da10d7642b23e9305e6 100644 (file)
@@ -122,6 +122,21 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>. -->
         <category android:name="android.intent.category.BROWSABLE"/>
         <data android:scheme="org-protocol"/>
       </intent-filter>
+
+      <!-- And also mailto links.  -->
+
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <category android:name="android.intent.category.BROWSABLE"/>
+        <data android:scheme="mailto"/>
+      </intent-filter>
+
+      <intent-filter>
+        <action android:name="android.intent.action.SENDTO"/>
+        <data android:scheme="mailto"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+      </intent-filter>
     </activity>
 
     <activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
index 00503b6cbcc890f44bd98168b76efdb01609f4c8..d27139e98bc505f39615d79f7c32a67cf7c8d86b 100644 (file)
@@ -342,8 +342,14 @@ public final class EmacsOpenActivity extends Activity
       });
   }
 
+  /* Start `emacsclient' with the provided list of ARGUMENTS, after
+     ARGUMENTS[0] is replaced with the name of the emacsclient binary.
+
+     Create a new thread to await its completion, subsequently
+     reporting any errors that arise to the user.  */
+
   public void
-  startEmacsClient (String fileName)
+  startEmacsClient (String[] arguments)
   {
     String libDir;
     ProcessBuilder builder;
@@ -352,23 +358,10 @@ public final class EmacsOpenActivity extends Activity
     File file;
     Intent intent;
 
-    /* If the Emacs service is not running, then start Emacs and make
-       it open this file.  */
-
-    if (EmacsService.SERVICE == null)
-      {
-       fileToOpen = fileName;
-       intent = new Intent (EmacsOpenActivity.this,
-                            EmacsActivity.class);
-       finish ();
-       startActivity (intent);
-       return;
-      }
-
     libDir = EmacsService.getLibraryDirectory (this);
-    builder = new ProcessBuilder (libDir + "/libemacsclient.so",
-                                 fileName, "--reuse-frame",
-                                 "--timeout=10", "--no-wait");
+    arguments[0] = libDir + "/libemacsclient.so";
+
+    builder = new ProcessBuilder (arguments);
 
     /* Redirection is unfortunately not possible in Android 7 and
        earlier.  */
@@ -413,7 +406,7 @@ public final class EmacsOpenActivity extends Activity
     ContentResolver resolver;
     ParcelFileDescriptor fd;
     byte[] names;
-    String errorBlurb;
+    String errorBlurb, scheme;
 
     super.onCreate (savedInstanceState);
 
@@ -431,7 +424,8 @@ public final class EmacsOpenActivity extends Activity
 
     if (action.equals ("android.intent.action.VIEW")
        || action.equals ("android.intent.action.EDIT")
-       || action.equals ("android.intent.action.PICK"))
+       || action.equals ("android.intent.action.PICK")
+       || action.equals ("android.intent.action.SENDTO"))
       {
        /* Obtain the URI of the action.  */
        uri = intent.getData ();
@@ -442,15 +436,35 @@ public final class EmacsOpenActivity extends Activity
            return;
          }
 
+       scheme = uri.getScheme ();
+
+       /* If URL is a mailto URI, call `message-mailto' much the same
+          way emacsclient-mail.desktop does.  */
+
+       if (scheme.equals ("mailto"))
+         {
+           /* Escape the special characters $ and " before enclosing
+              the string within the `message-mailto' wrapper.  */
+           fileName = uri.toString ();
+           fileName.replace ("\"", "\\\"").replace ("$", "\\$");
+           fileName = "(message-mailto \"" + fileName + "\")";
+
+           /* Execute emacsclient in order to execute this code.  */
+           currentActivity = this;
+           startEmacsClient (new String[] { "--timeout=10", "--no-wait",
+                                            "--eval", fileName, });
+           return;
+         }
+
        /* Now, try to get the file name.  */
 
-       if (uri.getScheme ().equals ("file"))
+       if (scheme.equals ("file"))
          fileName = uri.getPath ();
        else
          {
            fileName = null;
 
-           if (uri.getScheme ().equals ("content"))
+           if (scheme.equals ("content"))
              {
                /* This is one of the annoying Android ``content''
                   URIs.  Most of the time, there is actually an
@@ -501,7 +515,7 @@ public final class EmacsOpenActivity extends Activity
                      }
                  }
              }
-           else if (uri.getScheme ().equals ("org-protocol"))
+           else if (scheme.equals ("org-protocol"))
              /* URL is an org-protocol:// link, which is meant to be
                 directly relayed to emacsclient.  */
              fileName = uri.toString ();
@@ -516,11 +530,25 @@ public final class EmacsOpenActivity extends Activity
              }
          }
 
+       /* If the Emacs service is not running, then start Emacs and make
+          it open this file.  */
+
+       if (EmacsService.SERVICE == null)
+         {
+           fileToOpen = fileName;
+           intent = new Intent (EmacsOpenActivity.this,
+                                EmacsActivity.class);
+           finish ();
+           startActivity (intent);
+           return;
+         }
+
        /* And start emacsclient.  Set `currentActivity' to this now.
           Presumably, it will shortly become capable of displaying
           dialogs.  */
        currentActivity = this;
-       startEmacsClient (fileName);
+       startEmacsClient (new String[] { "--timeout=10", "--no-wait",
+                                        "--reuse-frame", fileName, });
       }
     else
       finish ();