Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit a77733b8 authored by Arnau Mora's avatar Arnau Mora Committed by GitHub
Browse files

Fix intent filters (#247)



* Moved showNextButton to viewmodel

Signed-off-by: default avatarArnau Mora Gras <arnyminerz@proton.me>

* Streamlined state updates

Signed-off-by: default avatarArnau Mora Gras <arnyminerz@proton.me>

* Added `content` scheme

Signed-off-by: default avatarArnau Mora Gras <arnyminerz@proton.me>

* Moved intent processor to Compose pipeline

Signed-off-by: default avatarArnau Mora Gras <arnyminerz@proton.me>

* Got rid of path-based intent filter

Signed-off-by: default avatarArnau Mora Gras <arnyminerz@proton.me>

* Added receivers for sharing

Signed-off-by: default avatarArnau Mora Gras <arnyminerz@proton.me>

* Fixed URL extraction

Signed-off-by: default avatarArnau Mora Gras <arnyminerz@proton.me>

---------

Signed-off-by: default avatarArnau Mora Gras <arnyminerz@proton.me>
parent ae9743fe
Loading
Loading
Loading
Loading
+4 −11
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:mimeType="text/calendar" />
                <data android:scheme="content" />
                <data android:scheme="file" />
                <data android:scheme="http" />
                <data android:scheme="https" />
@@ -99,18 +100,10 @@
                <data android:scheme="webcals" />
            </intent-filter>
            <intent-filter>
                <!-- intent filter for resources without MIME type, but URL ending with .ics -->
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="file" />
                <data android:scheme="http" />
                <data android:scheme="https" />
                <data android:host="*" />
                <data android:pathPattern=".*\\.ics" />
                <data android:pathPattern=".*\\..*\\.ics" />
                <data android:pathPattern=".*\\..*\\..*\\.ics" />
                <data android:pathPattern=".*\\..*\\..*\\..*\\.ics" />
                <data android:mimeType="text/plain" />
                <data android:mimeType="text/calendar" />
            </intent-filter>
        </activity>
        <activity
+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ class CreateSubscriptionModel(application: Application) : AndroidViewModel(appli
    val success = MutableLiveData(false)
    val errorMessage = MutableLiveData<String?>(null)
    val isCreating = MutableLiveData(false)
    val showNextButton = MutableLiveData(false)

    /**
     * Creates a new subscription taking the data from the given models.
+65 −32
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ package at.bitfire.icsdroid.ui.views
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
@@ -33,10 +34,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.res.stringResource
@@ -81,27 +79,13 @@ class AddCalendarActivity : AppCompatActivity() {
                    Intent.FLAG_GRANT_READ_URI_PERMISSION
                )

                subscriptionSettingsModel.url.value = uri.toString()
                subscriptionSettingsModel.url.postValue(uri.toString())
            }
        }

    override fun onCreate(inState: Bundle?) {
        super.onCreate(inState)

        if (inState == null) {
            intent?.apply {
                data?.let { uri ->
                    subscriptionSettingsModel.url.value = uri.toString()
                }
                getStringExtra(EXTRA_TITLE)?.let {
                    subscriptionSettingsModel.title.value = it
                }
                if (hasExtra(EXTRA_COLOR))
                    subscriptionSettingsModel.color.value =
                        getIntExtra(EXTRA_COLOR, LocalCalendar.DEFAULT_COLOR)
            }
        }

        subscriptionModel.success.observe(this) { success ->
            if (success) {
                // success, show notification and close activity
@@ -136,28 +120,43 @@ class AddCalendarActivity : AppCompatActivity() {
            val validationResult: ResourceInfo? by validationModel.result.observeAsState(null)

            val isCreating: Boolean by subscriptionModel.isCreating.observeAsState(false)
            val showNextButton by subscriptionModel.showNextButton.observeAsState(false)

            LaunchedEffect(intent) {
                if (inState == null) {
                    intent?.apply {
                        try {
                            (data ?: getStringExtra(Intent.EXTRA_TEXT))
                                ?.toString()
                                ?.stripUrl()
                                ?.let(subscriptionSettingsModel.url::postValue)
                                ?.also { checkUrlIntroductionPage() }
                        } catch (_: IllegalArgumentException) {
                            // Data does not have a valid url
                        }

            var showNextButton by remember { mutableStateOf(false) }
                        (intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri)
                            ?.toString()
                            ?.let(subscriptionSettingsModel.url::postValue)
                            ?.also { checkUrlIntroductionPage() }

            // Receive updates for the URL introduction page
            LaunchedEffect(url, requiresAuth, username, password, isVerifyingUrl) {
                if (isVerifyingUrl) {
                    showNextButton = true
                    return@LaunchedEffect
                        getStringExtra(EXTRA_TITLE)
                            ?.let(subscriptionSettingsModel.title::postValue)
                        takeIf { hasExtra(EXTRA_COLOR) }
                            ?.getIntExtra(EXTRA_COLOR, LocalCalendar.DEFAULT_COLOR)
                            ?.let(subscriptionSettingsModel.color::postValue)
                    }
                }
            }

                val uri = validateUri()
                val authOK =
                    if (requiresAuth)
                        !username.isNullOrEmpty() && !password.isNullOrEmpty()
                    else
                        true
                showNextButton = uri != null && authOK
            // Receive updates for the URL introduction page
            LaunchedEffect(url, requiresAuth, username, password, isVerifyingUrl) {
                checkUrlIntroductionPage()
            }

            // Receive updates for the Details page
            LaunchedEffect(title, color, ignoreAlerts, defaultAlarmMinutes, defaultAllDayAlarmMinutes) {
                showNextButton = !subscriptionSettingsModel.title.value.isNullOrBlank()
                subscriptionModel.showNextButton.postValue(!title.isNullOrBlank())
            }

            LaunchedEffect(validationResult) {
@@ -324,6 +323,21 @@ class AddCalendarActivity : AppCompatActivity() {
        }
    }

    private fun checkUrlIntroductionPage() {
        if (validationModel.isVerifyingUrl.value == true) {
            subscriptionModel.showNextButton.postValue(true)
        } else {
            val uri = validateUri()
            val authOK =
                if (credentialsModel.requiresAuth.value == true)
                    !credentialsModel.username.value.isNullOrEmpty() &&
                        !credentialsModel.password.value.isNullOrEmpty()
                else
                    true
            subscriptionModel.showNextButton.postValue(uri != null && authOK)
        }
    }


    /* dynamic changes */

@@ -398,4 +412,23 @@ class AddCalendarActivity : AppCompatActivity() {
        return uri
    }

    /**
     * Strips the URL from a string. For example, the following string:
     * ```
     * "This is a URL: https://example.com"
     * ```
     * will return:
     * ```
     * "https://example.com"
     * ```
     * _Quotes are not included_
     * @return The URL found in the string
     * @throws IllegalArgumentException if no URL is found in the string
     */
    private fun String.stripUrl(): String? {
        return "([a-zA-Z]+)://(\\w+)(.\\w+)*[/\\w*]*".toRegex()
            .find(this)
            ?.value
    }

}