Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ import com.anytypeio.anytype.di.feature.settings.DaggerSpacesStorageComponent
import com.anytypeio.anytype.di.feature.settings.LogoutWarningModule
import com.anytypeio.anytype.di.feature.settings.ProfileModule
import com.anytypeio.anytype.di.feature.sharing.DaggerAddToAnytypeComponent
import com.anytypeio.anytype.di.feature.sharing.DaggerSharingComponent
import com.anytypeio.anytype.di.feature.spaces.DaggerCreateSpaceComponent
import com.anytypeio.anytype.di.feature.spaces.DaggerSpaceListComponent
import com.anytypeio.anytype.di.feature.spaces.DaggerSpaceSettingsComponent
Expand Down Expand Up @@ -891,6 +892,12 @@ class ComponentManager(
.create(findComponentDependencies())
}

val sharingComponent = Component {
DaggerSharingComponent
.factory()
.create(findComponentDependencies())
}

val notificationsComponent = Component {
DaggerNotificationComponent
.factory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ interface AddToAnytypeComponent {
fun create(dependency: AddToAnytypeDependencies): AddToAnytypeComponent
}

fun inject(fragment: SharingFragment)
}

@Module
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.anytypeio.anytype.di.feature.sharing

import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_utils.di.scope.PerModal
import com.anytypeio.anytype.di.common.ComponentDependencies
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.chats.AddChatMessage
import com.anytypeio.anytype.domain.device.FileSharer
import com.anytypeio.anytype.domain.media.UploadFile
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
import com.anytypeio.anytype.domain.objects.CreateBookmarkObject
import com.anytypeio.anytype.domain.objects.CreateObjectFromUrl
import com.anytypeio.anytype.domain.objects.CreatePrefilledNote
import com.anytypeio.anytype.domain.primitives.FieldParser
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
import com.anytypeio.anytype.presentation.sharing.SharingViewModel
import com.anytypeio.anytype.ui.sharing.SharingFragment
import dagger.Binds
import dagger.Component
import dagger.Module
import dagger.Provides

/**
* DI Component for the redesigned sharing extension.
* Provides dependencies for SharingViewModel which handles three flows:
* - Flow 1: Chat Space (direct message sending)
* - Flow 2: Data Space without chat (object creation)
* - Flow 3: Data Space with chat (hybrid)
*/
@Component(
dependencies = [SharingDependencies::class],
modules = [
SharingModule::class,
SharingModule.Declarations::class
]
)
@PerModal
interface SharingComponent {
@Component.Factory
interface Factory {
fun create(dependency: SharingDependencies): SharingComponent
}

fun inject(fragment: SharingFragment)
}

@Module
object SharingModule {

@Provides
@PerModal
fun provideCreateBookmarkObject(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
): CreateBookmarkObject = CreateBookmarkObject(repo)

@Provides
@PerModal
fun provideCreatePrefilledNote(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
): CreatePrefilledNote = CreatePrefilledNote(repo, dispatchers)

@Provides
@PerModal
fun provideCreateObjectFromUrl(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
): CreateObjectFromUrl = CreateObjectFromUrl(repo, dispatchers)

@Provides
@PerModal
fun provideAddChatMessage(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
): AddChatMessage = AddChatMessage(repo, dispatchers)

@Provides
@PerModal
fun provideUploadFile(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
): UploadFile = UploadFile(repo, dispatchers)

@Provides
@PerModal
fun provideSearchObjects(
repo: BlockRepository
): SearchObjects = SearchObjects(repo)

@Module
interface Declarations {
@PerModal
@Binds
fun factory(factory: SharingViewModel.Factory): ViewModelProvider.Factory
}
}

/**
* Dependencies required by the SharingComponent.
* These are provided by the parent component (MainComponent).
*/
interface SharingDependencies : ComponentDependencies {
fun blockRepo(): BlockRepository
fun spaceManager(): SpaceManager
fun dispatchers(): AppCoroutineDispatchers
fun urlBuilder(): UrlBuilder
fun awaitAccountStartedManager(): AwaitAccountStartManager
fun analytics(): Analytics
fun fileSharer(): FileSharer
fun permissions(): UserPermissionProvider
fun analyticSpaceHelper(): AnalyticSpaceHelperDelegate
fun spaceViewSubscriptionContainer(): SpaceViewSubscriptionContainer
fun fieldParser() : FieldParser
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import com.anytypeio.anytype.di.feature.settings.LogoutWarningSubComponent
import com.anytypeio.anytype.di.feature.settings.ProfileSubComponent
import com.anytypeio.anytype.di.feature.settings.SpacesStorageDependencies
import com.anytypeio.anytype.di.feature.sharing.AddToAnytypeDependencies
import com.anytypeio.anytype.di.feature.sharing.SharingDependencies
import com.anytypeio.anytype.di.feature.spaces.CreateSpaceDependencies
import com.anytypeio.anytype.di.feature.spaces.SpaceListDependencies
import com.anytypeio.anytype.di.feature.spaces.SpaceSettingsDependencies
Expand Down Expand Up @@ -154,7 +155,8 @@ interface MainComponent :
PublishToWebDependencies,
MySitesDependencies,
MediaDependencies,
CreateChatObjectDependencies
CreateChatObjectDependencies,
SharingDependencies
{

fun inject(app: AndroidApplication)
Expand Down Expand Up @@ -452,4 +454,9 @@ abstract class ComponentDependenciesModule {
@IntoMap
@ComponentDependenciesKey(CreateChatObjectDependencies::class)
abstract fun createChatObjectDependencies(component: MainComponent): ComponentDependencies

@Binds
@IntoMap
@ComponentDependenciesKey(SharingDependencies::class)
abstract fun sharingDependencies(component: MainComponent): ComponentDependencies
}
102 changes: 25 additions & 77 deletions app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -162,37 +162,51 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
)
)
}
// New single entry point for all share intents
is Command.Sharing.Show -> {
SharingFragment.newInstance(command.intent).show(
supportFragmentManager,
SHARE_DIALOG_LABEL
)
}
// Legacy handlers - kept for backward compatibility
is Command.Sharing.Text -> {
@Suppress("DEPRECATION")
SharingFragment.text(command.data).show(
supportFragmentManager,
SHARE_DIALOG_LABEL
)
}
is Command.Sharing.Image -> {
@Suppress("DEPRECATION")
SharingFragment.image(command.uri).show(
supportFragmentManager,
SHARE_DIALOG_LABEL
)
}
is Command.Sharing.Images -> {
@Suppress("DEPRECATION")
SharingFragment.images(command.uris).show(
supportFragmentManager,
SHARE_DIALOG_LABEL
)
}
is Command.Sharing.Videos -> {
@Suppress("DEPRECATION")
SharingFragment.videos(command.uris).show(
supportFragmentManager,
SHARE_DIALOG_LABEL
)
}
is Command.Sharing.Files -> {
@Suppress("DEPRECATION")
SharingFragment.files(command.uris).show(
supportFragmentManager,
SHARE_DIALOG_LABEL
)
}
is Command.Sharing.File -> {
@Suppress("DEPRECATION")
SharingFragment.file(command.uri).show(
supportFragmentManager,
SHARE_DIALOG_LABEL
Expand Down Expand Up @@ -568,87 +582,24 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
}

/**
* Main activity is responsible only for checking new deep links.
* Launch deep links are handled by SplashFragment.
* Single entry point for all share intents.
* Deep links are checked here, all other content is routed to SharingFragment.
* SharingFragment handles MIME type detection internally.
*/
private fun proceedWithShareIntent(intent: Intent, checkDeepLink: Boolean = false) {
if (BuildConfig.DEBUG) Timber.d("Proceeding with share intent: $intent")
when {
intent.type == Mimetype.MIME_TEXT_PLAIN.value -> {
handleTextShare(
intent = intent,
checkDeepLink = checkDeepLink
)
}
intent.type?.startsWith(SHARE_IMAGE_INTENT_PATTERN) == true -> {
proceedWithImageShareIntent(intent)
}
intent.type?.startsWith(SHARE_VIDEO_INTENT_PATTERN) == true -> {
proceedWithVideoShareIntent(intent)
}
intent.type?.startsWith(SHARE_FILE_INTENT_PATTERN) == true -> {
proceedWithFileShareIntent(intent)
}
intent.type == Mimetype.MIME_FILE_ALL.value -> {
proceedWithFileShareIntent(intent)
}
else -> Timber.e("Unexpected scenario: ${intent.type}")
}
}

private fun handleTextShare(intent: Intent, checkDeepLink: Boolean) {
val raw = intent.getStringExtra(Intent.EXTRA_TEXT) ?: intent.dataString ?: return
if (BuildConfig.DEBUG) Timber.d("Proceeding with share intent: type=${intent.type}, action=${intent.action}")

when {
checkDeepLink && DefaultDeepLinkResolver.isDeepLink(raw) -> {
// Check for deep links in text content first
if (checkDeepLink && intent.type == Mimetype.MIME_TEXT_PLAIN.value) {
val raw = intent.getStringExtra(Intent.EXTRA_TEXT) ?: intent.dataString
if (raw != null && DefaultDeepLinkResolver.isDeepLink(raw)) {
vm.handleNewDeepLink(DefaultDeepLinkResolver.resolve(raw))
}
raw.isNotEmpty() && !DefaultDeepLinkResolver.isDeepLink(raw) -> {
vm.onIntentTextShare(raw)
}
else -> {
Timber.d("handleTextShare, skip handle intent :$raw")
return
}
}
}

private fun proceedWithFileShareIntent(intent: Intent) {
if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
vm.onIntentMultipleFilesShare(intent.parseActionSendMultipleUris())
} else {
val uri = intent.parseActionSendUri()
if (uri != null) {
vm.onIntentMultipleFilesShare(listOf(uri))
} else {
toast("Could not parse URI")
}
}
}

private fun proceedWithImageShareIntent(intent: Intent) {
if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
vm.onIntentMultipleImageShare(uris = intent.parseActionSendMultipleUris())
} else {
val uri = intent.parseActionSendUri()
if (uri != null) {
vm.onIntentMultipleImageShare(listOf(uri))
} else {
toast("Could not parse URI")
}
}
}

private fun proceedWithVideoShareIntent(intent: Intent) {
if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
vm.onIntentMultipleVideoShare(uris = intent.parseActionSendMultipleUris())
} else {
val uri = intent.parseActionSendUri()
if (uri != null) {
vm.onIntentMultipleVideoShare(listOf(uri))
} else {
toast("Could not parse URI")
}
}
// Single entry point: pass intent to SharingFragment via ViewModel
vm.onShareIntent(intent)
}

private fun proceedWithNotificationIntent(intent: Intent) {
Expand Down Expand Up @@ -832,8 +783,5 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr

companion object {
const val SHARE_DIALOG_LABEL = "anytype.dialog.share.label"
const val SHARE_IMAGE_INTENT_PATTERN = "image/"
const val SHARE_VIDEO_INTENT_PATTERN = "video/"
const val SHARE_FILE_INTENT_PATTERN = "application/"
}
}
Loading