diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/UiUtils.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/UiUtils.kt index 01a6ecdc..04ff0f62 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/UiUtils.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/UiUtils.kt @@ -85,6 +85,7 @@ object UiUtils { * @return true on success, false if the Intent could not be resolved (for instance, because * there is no user agent installed) */ + @Deprecated("Use SafeAndroidUriHandler (Compose) instead") fun launchUri(context: Context, uri: Uri, action: String = Intent.ACTION_VIEW, toastInstallBrowser: Boolean = true): Boolean { val intent = Intent(action, uri) try { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/OpenSourceFragment.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/OpenSourceFragment.kt index 02048269..3fa8f4b3 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/OpenSourceFragment.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/OpenSourceFragment.kt @@ -9,16 +9,47 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Checkbox +import androidx.compose.material.MaterialTheme +import androidx.compose.material.OutlinedButton +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import androidx.databinding.ObservableBoolean import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModel import at.bitfire.davdroid.App import at.bitfire.davdroid.R -import at.bitfire.davdroid.databinding.IntroOpenSourceBinding import at.bitfire.davdroid.settings.SettingsManager -import at.bitfire.davdroid.ui.UiUtils import at.bitfire.davdroid.ui.intro.OpenSourceFragment.Model.Companion.SETTING_NEXT_DONATION_POPUP +import at.bitfire.davdroid.ui.widget.CardWithImage +import at.bitfire.davdroid.ui.widget.SafeAndroidUriHandler +import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @@ -30,18 +61,83 @@ class OpenSourceFragment: Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - val binding = IntroOpenSourceBinding.inflate(inflater, container, false) - binding.lifecycleOwner = viewLifecycleOwner - binding.model = model + return ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + MdcTheme { + var dontShow by remember { mutableStateOf(model.dontShow.get()) } - binding.text.text = getString(R.string.intro_open_source_text, getString(R.string.app_name)) - binding.moreInfo.setOnClickListener { - UiUtils.launchUri(requireActivity(), App.homepageUrl(requireActivity()).buildUpon() - .appendPath("donate") - .build()) + val uriHandler = SafeAndroidUriHandler(LocalContext.current) + CompositionLocalProvider(LocalUriHandler provides uriHandler) { + FragmentContent( + dontShow = dontShow, + onChangeDontShow = { + model.dontShow.set(it) + dontShow = it + } + ) + } + } + } } + } - return binding.root + + @Preview( + showBackground = true, + showSystemUi = true + ) + @Composable + fun FragmentContent( + dontShow: Boolean = false, + onChangeDontShow: (Boolean) -> Unit = {} + ) { + val uriHandler = LocalUriHandler.current + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(8.dp) + ) { + CardWithImage( + title = stringResource(R.string.intro_open_source_title), + image = painterResource(R.drawable.intro_open_source), + imageContentScale = ContentScale.Inside, + message = stringResource( + R.string.intro_open_source_text, + stringResource(R.string.app_name) + ) + ) { + OutlinedButton( + onClick = { + uriHandler.openUri( + App.homepageUrl(requireActivity()) + .buildUpon() + .appendPath("donate") + .build() + .toString() + ) + } + ) { + Text(stringResource(R.string.intro_open_source_details)) + } + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Checkbox( + checked = dontShow, + onCheckedChange = onChangeDontShow + ) + Text( + text = stringResource(R.string.intro_battery_dont_show), + style = MaterialTheme.typography.body2, + modifier = Modifier.clickable { onChangeDontShow(!dontShow) } + ) + } + } + Spacer(Modifier.height(90.dp)) + } } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/widget/CardWithImage.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/widget/CardWithImage.kt index a99335df..9dbd2371 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/widget/CardWithImage.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/widget/CardWithImage.kt @@ -24,7 +24,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview @@ -38,14 +37,13 @@ fun CardWithImage( image: Painter? = null, imageContentDescription: String? = null, imageAlignment: Alignment = Alignment.Center, + imageContentScale: ContentScale = ContentScale.Crop, message: String? = null, subtitle: String? = null, icon: ImageVector? = null, iconContentDescription: String? = null, content: @Composable ColumnScope.() -> Unit = {} ) { - val configuration = LocalConfiguration.current - Card(modifier) { Column( modifier = Modifier.fillMaxWidth() @@ -57,7 +55,7 @@ fun CardWithImage( modifier = Modifier .fillMaxWidth() .heightIn(max = dimensionResource(R.dimen.card_theme_max_height)), - contentScale = ContentScale.Crop, + contentScale = imageContentScale, alignment = imageAlignment ) } @@ -70,7 +68,7 @@ fun CardWithImage( Row( modifier = Modifier .fillMaxWidth() - .padding(top = 12.dp), + .padding(top = 8.dp, bottom = 8.dp), verticalAlignment = Alignment.CenterVertically ) { icon?.let { @@ -103,7 +101,7 @@ fun CardWithImage( text = it, modifier = Modifier .fillMaxWidth() - .padding(top = 12.dp), + .padding(vertical = 4.dp), style = MaterialTheme.typography.body1 ) } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/widget/SafeAndroidUriHandler.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/widget/SafeAndroidUriHandler.kt new file mode 100644 index 00000000..a922d706 --- /dev/null +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/widget/SafeAndroidUriHandler.kt @@ -0,0 +1,25 @@ +package at.bitfire.davdroid.ui.widget + +import android.content.Context +import android.widget.Toast +import androidx.compose.ui.platform.AndroidUriHandler +import androidx.compose.ui.platform.UriHandler +import at.bitfire.davdroid.R +import at.bitfire.davdroid.log.Logger +import java.util.logging.Level + +class SafeAndroidUriHandler( + val context: Context +): UriHandler { + + override fun openUri(uri: String) { + try { + AndroidUriHandler(context).openUri(uri) + } catch (e: Exception) { + Logger.log.log(Level.WARNING, "No browser available", e) + // no browser available + Toast.makeText(context, R.string.install_browser, Toast.LENGTH_LONG).show() + } + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/intro_open_source.xml b/app/src/main/res/layout/intro_open_source.xml deleted file mode 100644 index 6fd54788..00000000 --- a/app/src/main/res/layout/intro_open_source.xml +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - -