Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add option to open URLs with custom tabs #407

Merged
merged 1 commit into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ dependencies {
implementation(libs.androidx.lifecycle.viewmodel.ktx)
implementation(libs.androidx.datastore.preferences)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.androidx.browser)
implementation(libs.jakewharton.timber)

debugImplementation(libs.facebook.stetho)
Expand Down
8 changes: 7 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Léon - The URL Cleaner
~ Copyright (C) 2023 Sven Jacobs
~ Copyright (C) 2024 Sven Jacobs
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -73,4 +73,10 @@
</provider>
</application>

<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>

</manifest>
37 changes: 37 additions & 0 deletions app/src/main/kotlin/com/svenjacobs/app/leon/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,27 @@

package com.svenjacobs.app.leon

import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
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
import com.svenjacobs.app.leon.inject.AppContainer.AppDataStoreManager
import com.svenjacobs.app.leon.ui.MainRouter
import com.svenjacobs.app.leon.ui.theme.AppTheme
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {

private val sourceText = mutableStateOf<String?>(null)
private var customTabsInitialized = false

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -45,6 +54,16 @@ class MainActivity : ComponentActivity() {
)
}
}

lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
AppDataStoreManager.customTabsEnabled.collect { customTabsEnabled ->
if (customTabsEnabled) {
setupCustomTabsService()
}
}
}
}
}

override fun onNewIntent(intent: Intent) {
Expand Down Expand Up @@ -72,6 +91,24 @@ class MainActivity : ComponentActivity() {
}
}

private fun setupCustomTabsService() {
if (customTabsInitialized) return

val connection = object : CustomTabsServiceConnection() {
override fun onCustomTabsServiceConnected(name: ComponentName, client: CustomTabsClient) {
client.warmup(0)
}

override fun onServiceDisconnected(name: ComponentName?) {
}
}

val packageName = CustomTabsClient.getPackageName(this, null)
CustomTabsClient.bindCustomTabsService(this, packageName, connection)

customTabsInitialized = true
}

private companion object {
private const val MIME_TYPE_TEXT_PLAIN = "text/plain"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,22 @@ class AppDataStoreManager(private val context: Context = AppContext) {
preferences[KEY_EXTRACT_URL] ?: false
}

suspend fun setCustomTabsEnabled(enabled: Boolean) {
context.dataStore.edit {
it[KEY_CUSTOM_TABS] = enabled
}
}

val customTabsEnabled: Flow<Boolean> =
context.dataStore.data.map { preferences ->
preferences[KEY_CUSTOM_TABS] ?: false
}

private companion object {
private val KEY_VERSION_CODE = intPreferencesKey("version_code")
private val KEY_ACTION_AFTER_CLEAN = stringPreferencesKey("action_after_clean")
private val KEY_URL_DECODE = booleanPreferencesKey("url_decode")
private val KEY_EXTRACT_URL = booleanPreferencesKey("extract_url")
private val KEY_CUSTOM_TABS = booleanPreferencesKey("custom_tabs")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import android.content.ContextWrapper
import android.content.Intent
import android.net.Uri
import android.view.Window
import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.foundation.Image
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
Expand Down Expand Up @@ -143,6 +144,24 @@ fun MainScreen(
)
}

fun openInCustomTabs(result: Result.Success) {
result.urls.firstOrNull()?.let { url ->
val intent = CustomTabsIntent.Builder()
.setColorScheme(CustomTabsIntent.COLOR_SCHEME_SYSTEM)
.build()

intent.launchUrl(context, Uri.parse(url))
}
}

fun openUrl(result: Result.Success) {
if (uiState.isCustomTabsEnabled) {
openInCustomTabs(result)
} else {
openInDefaultApp(result)
}
}

fun copyToClipboard(result: Result.Success) {
clipboard.setText(AnnotatedString(result.cleanedText))
coroutineScope.launch {
Expand Down Expand Up @@ -172,7 +191,7 @@ fun MainScreen(
(uiState.result as? Result.Success)?.let { result ->
when (uiState.actionAfterClean) {
ActionAfterClean.OpenShareMenu -> openShareMenu(result)
ActionAfterClean.OpenUrl -> openInDefaultApp(result)
ActionAfterClean.OpenUrl -> openUrl(result)
ActionAfterClean.CopyToClipboard -> copyToClipboard(result)
ActionAfterClean.DoNothing -> {}
}
Expand All @@ -192,7 +211,7 @@ fun MainScreen(
},
onShareClick = ::openShareMenu,
onCopyToClipboardClick = ::copyToClipboard,
onOpenClick = ::openInDefaultApp,
onOpenClick = ::openUrl,
onResetClick = viewModel::onResetClick,
onUrlDecodeCheckedChange = viewModel::onUrlDecodeCheckedChange,
onExtractUrlCheckedChange = viewModel::onExtractUrlCheckedChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class MainScreenViewModel(
val isLoading: Boolean = true,
val isUrlDecodeEnabled: Boolean = false,
val isExtractUrlEnabled: Boolean = false,
val isCustomTabsEnabled: Boolean = false,
val result: Result = Result.Empty,
val actionAfterClean: ActionAfterClean = ActionAfterClean.DoNothing,
) {
Expand All @@ -65,8 +66,9 @@ class MainScreenViewModel(
text,
appDataStoreManager.urlDecodeEnabled,
appDataStoreManager.extractUrlEnabled,
appDataStoreManager.customTabsEnabled,
appDataStoreManager.actionAfterClean,
) { text, urlDecodeEnabled, extractUrlEnabled, actionAfterClean ->
) { text, urlDecodeEnabled, extractUrlEnabled, isCustomTabsEnabled, actionAfterClean ->
val result = text?.let {
clean(
text = text,
Expand All @@ -79,6 +81,7 @@ class MainScreenViewModel(
isLoading = text == null,
isUrlDecodeEnabled = urlDecodeEnabled,
isExtractUrlEnabled = extractUrlEnabled,
isCustomTabsEnabled = isCustomTabsEnabled,
result = result,
actionAfterClean = actionAfterClean ?: ActionAfterClean.DoNothing,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@ fun SettingsScreen(
modifier = modifier,
isLoading = uiState.isLoading,
browserEnabled = uiState.browserEnabled,
customTabsEnabled = uiState.customTabsEnabled,
actionAfterClean = uiState.actionAfterClean,
onSanitizersClick = onNavigateToSettingsSanitizers,
onLicensesClick = onNavigateToSettingsLicenses,
onBrowserSwitchCheckedChange = viewModel::onBrowserSwitchCheckedChange,
onCustomTabsSwitchCheckedChange = viewModel::onCustomTabsSwitchCheckedChange,
onActionAfterCleanClick = viewModel::onActionAfterCleanClick,
)
}
Expand All @@ -77,10 +79,12 @@ fun SettingsScreen(
private fun Content(
isLoading: Boolean,
browserEnabled: Boolean,
customTabsEnabled: Boolean,
actionAfterClean: ActionAfterClean,
onSanitizersClick: () -> Unit,
onLicensesClick: () -> Unit,
onBrowserSwitchCheckedChange: (Boolean) -> Unit,
onCustomTabsSwitchCheckedChange: (Boolean) -> Unit,
onActionAfterCleanClick: (ActionAfterClean) -> Unit,
modifier: Modifier = Modifier,
) {
Expand Down Expand Up @@ -111,24 +115,19 @@ private fun Content(
Text(stringResource(R.string.licenses))
}

Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier
.padding(end = 8.dp)
.weight(1f),
text = stringResource(R.string.register_as_browser),
)

Switch(
checked = browserEnabled,
onCheckedChange = onBrowserSwitchCheckedChange,
)
}
SwitchRow(
modifier = Modifier.padding(top = 16.dp),
text = stringResource(R.string.register_as_browser),
checked = browserEnabled,
onCheckedChange = onBrowserSwitchCheckedChange,
)

SwitchRow(
modifier = Modifier.padding(top = 16.dp),
text = stringResource(R.string.open_in_custom_tabs),
checked = customTabsEnabled,
onCheckedChange = onCustomTabsSwitchCheckedChange,
)

Column(
modifier = Modifier.padding(top = 8.dp),
Expand Down Expand Up @@ -210,6 +209,31 @@ private fun Content(
}
}

@Composable
private fun SwitchRow(
text: String,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier
.padding(end = 8.dp)
.weight(1f),
text = text,
)

Switch(
checked = checked,
onCheckedChange = onCheckedChange,
)
}
}

@Composable
private fun ActionAfterClean.text(): String = when (this) {
ActionAfterClean.DoNothing -> stringResource(R.string.do_nothing)
Expand All @@ -225,10 +249,12 @@ private fun ContentPreview() {
Content(
isLoading = false,
browserEnabled = false,
customTabsEnabled = false,
actionAfterClean = ActionAfterClean.OpenShareMenu,
onSanitizersClick = {},
onLicensesClick = {},
onBrowserSwitchCheckedChange = {},
onCustomTabsSwitchCheckedChange = {},
onActionAfterCleanClick = {},
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Léon - The URL Cleaner
* Copyright (C) 2023 Sven Jacobs
* Copyright (C) 2024 Sven Jacobs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -44,6 +44,7 @@ class SettingsScreenViewModel(
data class UiState(
val isLoading: Boolean = true,
val browserEnabled: Boolean = false,
val customTabsEnabled: Boolean = false,
val actionAfterClean: ActionAfterClean = ActionAfterClean.DoNothing,
)

Expand All @@ -52,11 +53,13 @@ class SettingsScreenViewModel(
val uiState: StateFlow<UiState> =
combine(
browserEnabled,
appDataStoreManager.customTabsEnabled,
appDataStoreManager.actionAfterClean,
) { browserEnabled, actionAfterClean ->
) { browserEnabled, customTabsEnabled, actionAfterClean ->
UiState(
isLoading = false,
browserEnabled = browserEnabled,
customTabsEnabled = customTabsEnabled,
actionAfterClean = actionAfterClean ?: ActionAfterClean.DoNothing,
)
}.stateIn(
Expand All @@ -83,6 +86,12 @@ class SettingsScreenViewModel(
)
}

fun onCustomTabsSwitchCheckedChange(checked: Boolean) {
viewModelScope.launch {
appDataStoreManager.setCustomTabsEnabled(checked)
}
}

fun onActionAfterCleanClick(actionAfterClean: ActionAfterClean) {
viewModelScope.launch {
appDataStoreManager.setActionAfterClean(actionAfterClean)
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!--
~ Léon - The URL Cleaner
~ Copyright (C) 2023 Sven Jacobs
~ Copyright (C) 2024 Sven Jacobs
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -36,6 +36,7 @@
<string name="reset">Zurücksetzen</string>
<string name="extract_url">Nur URL extrahieren</string>
<string name="register_as_browser">Registriere Léon als Browser</string>
<string name="open_in_custom_tabs">URLs in Browser Custom Tabs öffnen</string>
<string name="action_after_clean">Aktion nach Reinigung</string>
<string name="do_nothing">Nichts unternehmen</string>
<string name="open_share_menu">Teilenmenü öffnen</string>
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values-pl/strings.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!--
~ Léon - The URL Cleaner
~ Copyright (C) 2023 Sven Jacobs
~ Copyright (C) 2024 Sven Jacobs
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -36,6 +36,7 @@
<string name="reset">Wyzeruj</string>
<string name="extract_url">Wyodrębnij tylko URL</string>
<string name="register_as_browser">Zarejestruj Léona jako przeglądarkę</string>
<string name="open_in_custom_tabs">Open URLs in browser custom tabs</string> <!-- TODO: translate -->
<string name="action_after_clean">Action after clean</string> <!-- TODO: translate -->
<string name="do_nothing">Do nothing</string> <!-- TODO: translate -->
<string name="open_share_menu">Open share menu</string> <!-- TODO: translate -->
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!--
~ Léon - The URL Cleaner
~ Copyright (C) 2023 Sven Jacobs
~ Copyright (C) 2024 Sven Jacobs
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -36,6 +36,7 @@
<string name="reset">Сброс</string>
<string name="extract_url">Извлечение только URL-адреса</string>
<string name="register_as_browser">Register Léon as browser</string> <!-- TODO: translate -->
<string name="open_in_custom_tabs">Open URLs in browser custom tabs</string> <!-- TODO: translate -->
<string name="action_after_clean">Action after clean</string> <!-- TODO: translate -->
<string name="do_nothing">Do nothing</string> <!-- TODO: translate -->
<string name="open_share_menu">Open share menu</string> <!-- TODO: translate -->
Expand Down
Loading