Skip to content

Commit

Permalink
feat: add API for programmatic usage
Browse files Browse the repository at this point in the history
  • Loading branch information
svenjacobs committed Jun 4, 2024
1 parent af2f6e1 commit 6766b98
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 16 deletions.
29 changes: 29 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Léon can be called programmatically from other applications to clean URLs.
Just like the user-facing application it is based on the standard mechanism of intents.

To send text to Léon for cleaning, create an `Intent` with action `com.svenjacobs.app.leon.CLEAN`
and put the text to clean as an extra with key `Intent.EXTRA_TEXT`.

For Compose, use
[rememberLauncherForActivityResult](https://developer.android.com/reference/kotlin/androidx/activity/compose/package-summary#rememberLauncherForActivityResult(androidx.activity.result.contract.ActivityResultContract,kotlin.Function1))
from `androidx.activity:activity-compose`.

```kotlin
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val cleanedText = result.data?.getStringExtra(Intent.EXTRA_TEXT)
// Do something with cleanedText
}
}

val intent = Intent("com.svenjacobs.app.leon.CLEAN").apply {
putExtra(Intent.EXTRA_TEXT, "text to clean")
}
launcher.launch(intent)
```

For legacy Android development you would use
[startActivityForResult](https://developer.android.com/reference/android/app/Activity#startActivityForResult(android.content.Intent,%20int))
and the related API for Fragments.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ Did you find a bug or have an idea of how the app could be improved? Please repo
[bugs](https://github.com/svenjacobs/leon/issues) or give
[feedback](https://github.com/svenjacobs/leon/discussions).

## API

It is possible to call Léon programmatically from other applications.
See [API.md](API) for details.

## Technical implementation

This app is also meant as a blueprint for modern Android development, presenting and evaluating
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>

<!-- For programmatic use from other applications -->
<intent-filter>
<action android:name="com.svenjacobs.app.leon.CLEAN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

<!-- Handle browser intents. -->
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/kotlin/com/svenjacobs/app/leon/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.browser.customtabs.CustomTabsClient
import androidx.browser.customtabs.CustomTabsServiceConnection
import androidx.compose.runtime.mutableStateOf
import androidx.core.view.WindowCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
Expand All @@ -43,7 +43,7 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

WindowCompat.setDecorFitsSystemWindows(window, false)
enableEdgeToEdge()

onIntent(intent)

Expand Down
59 changes: 47 additions & 12 deletions app/src/main/kotlin/com/svenjacobs/app/leon/ProcessTextActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.annotation.RequiresApi
import com.svenjacobs.app.leon.core.domain.CleanerService
import com.svenjacobs.app.leon.inject.AppContainer.AppDataStoreManager
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.runBlocking

@RequiresApi(Build.VERSION_CODES.M)
Expand All @@ -32,10 +34,31 @@ class ProcessTextActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

when (intent.action) {
ACTION_CLEAN -> actionClean()
Intent.ACTION_PROCESS_TEXT -> actionProcessText()
else -> cancel()
}

finish()
}

private fun actionClean() {
val text = intent.getStringExtra(Intent.EXTRA_TEXT)

when {
text.isNullOrBlank() -> cancel()
else -> result(text, Intent.EXTRA_TEXT)
}
}

private fun actionProcessText() {
val text = intent.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT)?.toString()
val readonly = intent.getBooleanExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, false)

when {
text.isNullOrBlank() -> cancel()

// If readonly, delegate to MainActivity
readonly -> startActivity(
Intent(this, MainActivity::class.java).apply {
Expand All @@ -45,21 +68,33 @@ class ProcessTextActivity : ComponentActivity() {
},
)

text.isNullOrBlank() -> setResult(RESULT_CANCELED)
else -> result(text, Intent.EXTRA_PROCESS_TEXT)
}
}

// Needs to run with runBlocking or else setResult() won't work
else -> runBlocking {
val result = CleanerService().clean(text)
private fun result(text: String, key: String) {
// Needs to run with runBlocking or else setResult() won't work
runBlocking {
val decodeUrl = AppDataStoreManager.urlDecodeEnabled.firstOrNull() ?: false
val result = CleanerService().clean(
text = text,
decodeUrl = decodeUrl,
)

setResult(
RESULT_OK,
Intent().apply {
putExtra(Intent.EXTRA_PROCESS_TEXT, result.cleanedText)
},
)
}
setResult(
RESULT_OK,
Intent().apply {
putExtra(key, result.cleanedText)
},
)
}
}

finish()
private fun cancel() {
setResult(RESULT_CANCELED)
}

private companion object {
private const val ACTION_CLEAN = "com.svenjacobs.app.leon.CLEAN"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

package com.svenjacobs.app.leon.ui.screens.main.views

import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
Expand All @@ -42,7 +41,7 @@ internal fun BottomBar(navController: NavHostController, modifier: Modifier = Mo
)

NavigationBar(
modifier = modifier.navigationBarsPadding(),
modifier = modifier,
) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
Expand Down

0 comments on commit 6766b98

Please sign in to comment.