Browse Source

Merge pull request #11 from elementc/dev/receive-text-shares

Add Note UI will now receive SEND text/plain intents.
zadam 3 years ago
parent
commit
51289263d2

+ 2 - 2
app/build.gradle

@@ -46,8 +46,8 @@ dependencies {
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
     implementation 'androidx.appcompat:appcompat:1.2.0'
     implementation 'com.google.android.material:material:1.2.1'
-    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
-    testImplementation 'junit:junit:4.12'
+    implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
+    testImplementation 'junit:junit:4.13.1'
     androidTestImplementation 'androidx.test:runner:1.3.0'
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
     implementation("com.squareup.okhttp3:okhttp:4.9.0")

+ 23 - 3
app/src/main/AndroidManifest.xml

@@ -1,4 +1,7 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?><!--suppress AndroidElementNotAllowed -->
+<!-- The above suppression is necessary due to the <queries> tag, which is meaningless in older SDKs
+     but is critical to looking up package names on Android devices running SDK 30.-->
+
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="io.github.zadam.triliumsender">
 
@@ -16,8 +19,8 @@
             android:label="Trilium connection setup" />
         <activity
             android:name=".MainActivity"
-            android:taskAffinity=".MainActivity"
             android:label="@string/app_name"
+            android:taskAffinity=".MainActivity"
             android:theme="@style/AppTheme.NoActionBar">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -38,15 +41,32 @@
         </activity>
         <activity
             android:name=".SendNoteActivity"
-            android:taskAffinity=".SendNoteActivity"
             android:label="Add note"
+            android:taskAffinity=".SendNoteActivity"
             android:windowSoftInputMode="adjustResize">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+
+                <data android:mimeType="text/plain" />
+            </intent-filter>
         </activity>
     </application>
 
+    <queries>
+        <intent>
+            <action android:name="android.intent.action.SEND" />
+            <data android:mimeType="text/plain" />
+        </intent>
+        <intent>
+            <action android:name="android.intent.action.SEND" />
+            <data android:mimeType="image/*" />
+        </intent>
+    </queries>
 </manifest>

+ 67 - 1
app/src/main/java/io/github/zadam/triliumsender/SendNoteActivity.kt

@@ -1,7 +1,10 @@
 package io.github.zadam.triliumsender
 
+import android.content.Intent
+import android.content.pm.PackageManager
 import android.os.Bundle
 import android.util.Log
+import android.widget.TextView
 import android.widget.Toast
 import androidx.appcompat.app.AppCompatActivity
 import io.github.zadam.triliumsender.services.HtmlConverter
@@ -20,6 +23,7 @@ import org.json.JSONObject
 class SendNoteActivity : AppCompatActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
+        val tag = "SendNoteOnCreateHandler"
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_send_note)
 
@@ -32,6 +36,18 @@ class SendNoteActivity : AppCompatActivity() {
             return
         }
 
+        // If we're a share-intent, pre-populate the note.
+        when (intent?.action) {
+            Intent.ACTION_SEND -> {
+                if ("text/plain" == intent.type) {
+                    handleSendText(intent)
+                } else {
+                    // We don't yet have text/html support.
+                    Log.e(tag, "Don't have a handler for share type ${intent.type}.")
+                }
+            }
+        }
+
         sendNoteButton.setOnClickListener {
             // Kick off a coroutine to handle the actual send attempt without blocking the UI.
             // Since we want to be able to fire Toasts, we should use the Main (UI) scope.
@@ -51,6 +67,50 @@ class SendNoteActivity : AppCompatActivity() {
         }
     }
 
+    /**
+     * If we're the target of a SEND intent, pre-fill the note's contents.
+     *
+     * @param intent, the Intent object, already verified to be of type text/plain.
+     */
+    private fun handleSendText(intent: Intent) {
+        val tag = "SendNoteHandleSendText"
+
+        // Many apps will suggest a "Text Subject", as in an email.
+        val suggestedSubject = intent.getStringExtra((Intent.EXTRA_SUBJECT))
+        if (suggestedSubject != null && suggestedSubject.isNotEmpty()) {
+            // Use suggested subject.
+            noteTitleEditText.setText(suggestedSubject, TextView.BufferType.EDITABLE)
+        } else {
+            // Try to craft a sane default title.
+            var referrerName = "Android"
+
+            // SDKs greater than 23 can access the referrer's package URI...
+            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
+                val referrer = this.referrer
+                if (referrer != null && referrer.host != null) {
+                    // Which we can use to get an AppInfo handle...
+                    try {
+                        // Which we can use to get an app label!
+                        val referrerInfo = packageManager.getApplicationInfo(referrer.host.toString(), 0)
+                        val potentialReferrerName = referrerInfo.loadLabel(packageManager).toString()
+                        // Sanity check: is it an empty string?
+                        if (potentialReferrerName.isNotEmpty()) {
+                            referrerName = potentialReferrerName
+                        }
+                    } catch (e: PackageManager.NameNotFoundException) {
+                        // Oh well, we have the default name to fall back on.
+                        Log.w(tag, "Could not find package label for package name ${referrer.host}")
+                    }
+                }
+            }
+            // Ultimately, set the note title.
+            noteTitleEditText.setText(getString(R.string.share_note_title, referrerName), TextView.BufferType.EDITABLE)
+        }
+        // And populate the note body!
+        noteContentEditText.setText(intent.getStringExtra(Intent.EXTRA_TEXT), TextView.BufferType.EDITABLE)
+    }
+
+
     /**
      * Attempts to send a note to the Trilium server.
      *
@@ -70,7 +130,13 @@ class SendNoteActivity : AppCompatActivity() {
             val client = OkHttpClient()
 
             val json = JSONObject()
-            json.put("title", noteTitle)
+
+            if (noteTitle.isEmpty()) {
+                // API does not allow empty note titles, so use a default value if the user doesn't set one.
+                json.put("title", "Note from Android")
+            } else {
+                json.put("title", noteTitle)
+            }
             json.put("content", HtmlConverter().convertPlainTextToHtml(noteText))
 
             val body = json.toString().toRequestBody(Utils.JSON)

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -23,4 +23,5 @@
     <string name="sender_not_configured_note">Trilium Sender is not configured. Can\'t compose note.</string>
     <string name="sending_note_complete">Note sent to Trilium.</string>
     <string name="sending_note_failed">Sending note to Trilium failed.</string>
+    <string name="share_note_title">Shared from %s</string>
 </resources>

+ 1 - 1
build.gradle

@@ -7,7 +7,7 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:4.0.1'
+        classpath 'com.android.tools.build:gradle:4.0.2'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 
         // NOTE: Do not place your application dependencies here; they belong