diff --git a/shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/home/ui/PostOptionShareIconButton.kt b/shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/home/ui/PostOptionShareIconButton.kt deleted file mode 100644 index dfd39a859..000000000 --- a/shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/home/ui/PostOptionShareIconButton.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2023 Sasikanth Miriyampalli - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.sasikanth.rss.reader.home.ui - -import android.content.Intent -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext -import dev.sasikanth.rss.reader.resources.icons.Share -import dev.sasikanth.rss.reader.resources.icons.TwineIcons -import dev.sasikanth.rss.reader.resources.strings.LocalStrings - -@Composable -internal actual fun PostOptionShareIconButton(postLink: String) { - val context = LocalContext.current - PostOptionIconButton( - icon = TwineIcons.Share, - contentDescription = LocalStrings.current.share, - onClick = { - val sendIntent = - Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_TEXT, postLink) - type = "text/plain" - } - val shareIntent = Intent.createChooser(sendIntent, null) - context.startActivity(shareIntent) - } - ) -} diff --git a/shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/feeds/ui/ShareIconButton.kt b/shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/share/AndroidShareHandler.kt similarity index 61% rename from shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/feeds/ui/ShareIconButton.kt rename to shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/share/AndroidShareHandler.kt index 4d0a696e3..a0143291d 100644 --- a/shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/feeds/ui/ShareIconButton.kt +++ b/shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/share/AndroidShareHandler.kt @@ -13,24 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dev.sasikanth.rss.reader.feeds.ui +package dev.sasikanth.rss.reader.share + +import android.content.Context import android.content.Intent -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import dev.sasikanth.rss.reader.di.scopes.AppScope +import me.tatarka.inject.annotations.Inject -@Composable -internal actual fun ShareIconButton(content: () -> String) { - val context = LocalContext.current +@Inject +@AppScope +class AndroidShareHandler(private val context: Context) : ShareHandler { - ShareIconButtonInternal { + override fun share(content: String) { val sendIntent = Intent().apply { action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_TEXT, content()) + putExtra(Intent.EXTRA_TEXT, content) type = "text/plain" } - val shareIntent = Intent.createChooser(sendIntent, null) + val shareIntent = + Intent.createChooser(sendIntent, null).apply { addFlags(FLAG_ACTIVITY_NEW_TASK) } context.startActivity(shareIntent) } } diff --git a/shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/share/ShareComponent.kt b/shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/share/ShareComponent.kt new file mode 100644 index 000000000..d15e47acd --- /dev/null +++ b/shared/src/androidMain/kotlin/dev/sasikanth/rss/reader/share/ShareComponent.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2023 Sasikanth Miriyampalli + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.sasikanth.rss.reader.share + +import me.tatarka.inject.annotations.Provides + +actual interface ShareComponent { + + @Provides fun AndroidShareHandler.bind(): ShareHandler = this +} diff --git a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/app/App.kt b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/app/App.kt index 6b6e04c6c..1611ff8aa 100644 --- a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/app/App.kt +++ b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/app/App.kt @@ -37,6 +37,8 @@ import dev.sasikanth.rss.reader.repository.BrowserType import dev.sasikanth.rss.reader.resources.strings.ProvideStrings import dev.sasikanth.rss.reader.search.ui.SearchScreen import dev.sasikanth.rss.reader.settings.ui.SettingsScreen +import dev.sasikanth.rss.reader.share.LocalShareHandler +import dev.sasikanth.rss.reader.share.ShareHandler import dev.sasikanth.rss.reader.utils.LocalWindowSizeClass import me.tatarka.inject.annotations.Assisted import me.tatarka.inject.annotations.Inject @@ -49,6 +51,7 @@ typealias App = @Composable (openLink: (String, BrowserType) -> Unit) -> Unit fun App( appPresenter: AppPresenter, imageLoader: ImageLoader, + shareHandler: ShareHandler, @Assisted openLink: (String, BrowserType) -> Unit ) { val dynamicColorState = rememberDynamicColorState(imageLoader = imageLoader) @@ -56,7 +59,8 @@ fun App( CompositionLocalProvider( LocalImageLoader provides imageLoader, LocalWindowSizeClass provides calculateWindowSizeClass(), - LocalDynamicColorState provides dynamicColorState + LocalDynamicColorState provides dynamicColorState, + LocalShareHandler provides shareHandler ) { DynamicContentTheme(dynamicColorState) { ProvideStrings { diff --git a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/di/SharedApplicationComponent.kt b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/di/SharedApplicationComponent.kt index 0c27a70b2..da0a87c8c 100644 --- a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/di/SharedApplicationComponent.kt +++ b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/di/SharedApplicationComponent.kt @@ -22,12 +22,18 @@ import dev.sasikanth.rss.reader.logging.LoggingComponent import dev.sasikanth.rss.reader.network.NetworkComponent import dev.sasikanth.rss.reader.refresh.LastUpdatedAt import dev.sasikanth.rss.reader.sentry.SentryComponent +import dev.sasikanth.rss.reader.share.ShareComponent import dev.sasikanth.rss.reader.utils.DefaultDispatchersProvider import dev.sasikanth.rss.reader.utils.DispatchersProvider import me.tatarka.inject.annotations.Provides abstract class SharedApplicationComponent : - DataComponent, ImageLoaderComponent, SentryComponent, NetworkComponent, LoggingComponent { + DataComponent, + ImageLoaderComponent, + SentryComponent, + NetworkComponent, + LoggingComponent, + ShareComponent { abstract val imageLoader: ImageLoader diff --git a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/feeds/ui/FeedListItem.kt b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/feeds/ui/FeedListItem.kt index 20caa2c88..951423343 100644 --- a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/feeds/ui/FeedListItem.kt +++ b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/feeds/ui/FeedListItem.kt @@ -62,6 +62,7 @@ import dev.sasikanth.rss.reader.resources.icons.PinFilled import dev.sasikanth.rss.reader.resources.icons.Share import dev.sasikanth.rss.reader.resources.icons.TwineIcons import dev.sasikanth.rss.reader.resources.strings.LocalStrings +import dev.sasikanth.rss.reader.share.LocalShareHandler import dev.sasikanth.rss.reader.ui.AppTheme import kotlinx.coroutines.delay @@ -159,7 +160,8 @@ private fun ActionButtons( ) } } else { - ShareIconButton(content = { feed.link }) + val shareHandler = LocalShareHandler.current + ShareIconButton(onClick = { shareHandler.share(feed.link) }) } } } @@ -271,10 +273,8 @@ private fun FeedLabelInput( ) } -@Composable internal expect fun ShareIconButton(content: () -> String) - @Composable -internal fun ShareIconButtonInternal(modifier: Modifier = Modifier, onClick: () -> Unit) { +internal fun ShareIconButton(modifier: Modifier = Modifier, onClick: () -> Unit) { IconButton(modifier = modifier, onClick = onClick) { Icon( imageVector = TwineIcons.Share, diff --git a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/ui/PostMetadata.kt b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/ui/PostMetadata.kt index 87e336ee0..af098525a 100644 --- a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/ui/PostMetadata.kt +++ b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/home/ui/PostMetadata.kt @@ -40,8 +40,10 @@ import androidx.compose.ui.unit.dp import dev.sasikanth.rss.reader.resources.icons.Bookmark import dev.sasikanth.rss.reader.resources.icons.Bookmarked import dev.sasikanth.rss.reader.resources.icons.Comments +import dev.sasikanth.rss.reader.resources.icons.Share import dev.sasikanth.rss.reader.resources.icons.TwineIcons import dev.sasikanth.rss.reader.resources.strings.LocalStrings +import dev.sasikanth.rss.reader.share.LocalShareHandler import dev.sasikanth.rss.reader.ui.AppTheme @Composable @@ -148,12 +150,17 @@ private fun PostOptionsButtonRow( onClick = onBookmarkClick ) - PostOptionShareIconButton(postLink) + val shareHandler = LocalShareHandler.current + PostOptionIconButton( + icon = TwineIcons.Share, + contentDescription = LocalStrings.current.share, + onClick = { shareHandler.share(postLink) } + ) } } @Composable -internal fun PostOptionIconButton( +private fun PostOptionIconButton( icon: ImageVector, contentDescription: String, modifier: Modifier = Modifier, @@ -176,5 +183,3 @@ internal fun PostOptionIconButton( ) } } - -@Composable internal expect fun PostOptionShareIconButton(postLink: String) diff --git a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/share/ShareComponent.kt b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/share/ShareComponent.kt new file mode 100644 index 000000000..02625af19 --- /dev/null +++ b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/share/ShareComponent.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2023 Sasikanth Miriyampalli + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.sasikanth.rss.reader.share + +expect interface ShareComponent diff --git a/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/share/ShareHandler.kt b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/share/ShareHandler.kt new file mode 100644 index 000000000..6015ec770 --- /dev/null +++ b/shared/src/commonMain/kotlin/dev/sasikanth/rss/reader/share/ShareHandler.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Sasikanth Miriyampalli + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.sasikanth.rss.reader.share + +import androidx.compose.runtime.staticCompositionLocalOf + +interface ShareHandler { + fun share(content: String) +} + +val LocalShareHandler = + staticCompositionLocalOf { + throw NullPointerException("Please provide a share handler") + } diff --git a/shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/home/ui/PostOptionShareIconButton.kt b/shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/home/ui/PostOptionShareIconButton.kt deleted file mode 100644 index 6ce4e03e1..000000000 --- a/shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/home/ui/PostOptionShareIconButton.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2023 Sasikanth Miriyampalli - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package dev.sasikanth.rss.reader.home.ui - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.setValue -import androidx.compose.ui.interop.LocalUIViewController -import dev.sasikanth.rss.reader.resources.icons.Share -import dev.sasikanth.rss.reader.resources.icons.TwineIcons -import dev.sasikanth.rss.reader.resources.strings.LocalStrings -import kotlinx.cinterop.ExperimentalForeignApi -import platform.CoreGraphics.CGRectGetMaxY -import platform.CoreGraphics.CGRectGetMidX -import platform.CoreGraphics.CGRectMake -import platform.UIKit.UIActivityViewController -import platform.UIKit.popoverPresentationController - -@OptIn(ExperimentalForeignApi::class) -@Composable -internal actual fun PostOptionShareIconButton(postLink: String) { - val viewController = LocalUIViewController.current - - PostOptionIconButton( - icon = TwineIcons.Share, - contentDescription = LocalStrings.current.share, - onClick = { - val items = listOf(postLink) - val activityController = UIActivityViewController(items, null) - activityController.popoverPresentationController?.setSourceView(viewController.view) - activityController.popoverPresentationController?.setSourceRect( - CGRectMake( - x = CGRectGetMidX(viewController.view.bounds), - y = CGRectGetMaxY(viewController.view.bounds), - width = 0.0, - height = 0.0 - ) - ) - viewController.presentViewController(activityController, true, null) - } - ) -} diff --git a/shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/feeds/ui/ShareIconButton.kt b/shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/share/IOSShareHandler.kt similarity index 75% rename from shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/feeds/ui/ShareIconButton.kt rename to shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/share/IOSShareHandler.kt index 3a15bb453..b3cd926e8 100644 --- a/shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/feeds/ui/ShareIconButton.kt +++ b/shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/share/IOSShareHandler.kt @@ -13,26 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package dev.sasikanth.rss.reader.feeds.ui -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.setValue -import androidx.compose.ui.interop.LocalUIViewController +package dev.sasikanth.rss.reader.share + +import dev.sasikanth.rss.reader.di.scopes.AppScope import kotlinx.cinterop.ExperimentalForeignApi +import me.tatarka.inject.annotations.Inject import platform.CoreGraphics.CGRectGetMaxY import platform.CoreGraphics.CGRectGetMidX import platform.CoreGraphics.CGRectMake import platform.UIKit.UIActivityViewController +import platform.UIKit.UIViewController import platform.UIKit.popoverPresentationController -@OptIn(ExperimentalForeignApi::class) -@Composable -internal actual fun ShareIconButton(content: () -> String) { - val viewController = LocalUIViewController.current +@Inject +@AppScope +class IOSShareHandler(private val viewControllerProvider: () -> UIViewController) : ShareHandler { - ShareIconButtonInternal { - val items = listOf(content()) + @OptIn(ExperimentalForeignApi::class) + override fun share(content: String) { + val viewController = viewControllerProvider() + val items = listOf(content) val activityController = UIActivityViewController(items, null) activityController.popoverPresentationController?.setSourceView(viewController.view) activityController.popoverPresentationController?.setSourceRect( diff --git a/shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/share/ShareComponent.kt b/shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/share/ShareComponent.kt new file mode 100644 index 000000000..a0df53e27 --- /dev/null +++ b/shared/src/iosMain/kotlin/dev/sasikanth/rss/reader/share/ShareComponent.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2023 Sasikanth Miriyampalli + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.sasikanth.rss.reader.share + +import me.tatarka.inject.annotations.Provides + +actual interface ShareComponent { + + @Provides fun IOSShareHandler.bind(): ShareHandler = this +}