diff --git a/app/src/main/java/org/mozilla/fenix/share/ShareFragment.kt b/app/src/main/java/org/mozilla/fenix/share/ShareFragment.kt index 3c51798a5b..c679bbba65 100644 --- a/app/src/main/java/org/mozilla/fenix/share/ShareFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/share/ShareFragment.kt @@ -4,16 +4,33 @@ package org.mozilla.fenix.share +import android.content.Context +import android.content.Intent +import android.content.Intent.ACTION_SEND +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.content.pm.ResolveInfo import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatDialogFragment +import androidx.lifecycle.lifecycleScope import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_share.view.* +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.launch +import mozilla.components.concept.sync.DeviceCapability +import mozilla.components.concept.sync.DeviceType +import mozilla.components.service.fxa.manager.FxaAccountManager import org.mozilla.fenix.R +import org.mozilla.fenix.ext.components +import org.mozilla.fenix.share.listadapters.AppShareOption +import org.mozilla.fenix.share.listadapters.SyncShareOption +@Suppress("TooManyFunctions") class ShareFragment : AppCompatDialogFragment() { interface TabsSharedCallback { fun onTabsShared(tabsSize: Int) @@ -23,7 +40,27 @@ class ShareFragment : AppCompatDialogFragment() { private lateinit var shareCloseView: ShareCloseView private lateinit var shareToAccountDevicesView: ShareToAccountDevicesView private lateinit var shareToAppsView: ShareToAppsView - private var tabs: Array = emptyArray() + private lateinit var appsListDeferred: Deferred> + private lateinit var devicesListDeferred: Deferred> + + override fun onAttach(context: Context) { + super.onAttach(context) + + // Start preparing the data as soon as we have a valid Context + appsListDeferred = lifecycleScope.async(Dispatchers.IO) { + val shareIntent = Intent(ACTION_SEND).apply { + type = "text/plain" + flags = FLAG_ACTIVITY_NEW_TASK + } + val shareAppsActivities = getIntentActivities(shareIntent, context) + buildAppsList(shareAppsActivities, context) + } + + devicesListDeferred = lifecycleScope.async(Dispatchers.IO) { + val fxaAccountManager = context.components.backgroundServices.accountManager + buildDeviceList(fxaAccountManager) + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -41,16 +78,79 @@ class ShareFragment : AppCompatDialogFragment() { throw IllegalStateException("URL and tabs cannot both be null.") } - tabs = args.tabs ?: arrayOf(ShareTab(args.url!!, args.title ?: "")) + val tabs = args.tabs?.toList() ?: listOf(ShareTab(args.url!!, args.title ?: "")) shareInteractor = ShareInteractor() + if (isSharingToDevicesAvailable(requireContext().applicationContext)) { + shareToAccountDevicesView = ShareToAccountDevicesView(view.devicesShareLayout, shareInteractor) + } else { + view.devicesShareGroup.visibility = View.GONE + } shareCloseView = ShareCloseView(view.closeSharingLayout, shareInteractor) - shareToAccountDevicesView = ShareToAccountDevicesView(view.devicesShareLayout, shareInteractor) shareToAppsView = ShareToAppsView(view.appsShareLayout, shareInteractor) return view } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + lifecycleScope.launch { + val devicesShareOptions = devicesListDeferred.await() + shareToAccountDevicesView.setSharetargets(devicesShareOptions) + val appsToShareTo = appsListDeferred.await() + shareToAppsView.setSharetargets(appsToShareTo) + } + } + + private fun isSharingToDevicesAvailable(context: Context) = + !context.components.backgroundServices.accountManager.accountNeedsReauth() + + private fun getIntentActivities(shareIntent: Intent, context: Context): List? { + return context.packageManager.queryIntentActivities(shareIntent, 0) + } + + private fun buildAppsList(intentActivities: List?, context: Context): List { + return intentActivities?.map { resolveInfo -> + AppShareOption( + resolveInfo.loadLabel(context.packageManager).toString(), + resolveInfo.loadIcon(context.packageManager), + resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name + ) + } ?: emptyList() + } + + private fun buildDeviceList(accountManager: FxaAccountManager): List { + val list = mutableListOf() + + if (accountManager.authenticatedAccount() == null) { + list.add(SyncShareOption.SignIn) + return list + } + + accountManager.authenticatedAccount()?.deviceConstellation()?.state()?.otherDevices?.let { devices -> + val shareableDevices = devices.filter { it.capabilities.contains(DeviceCapability.SEND_TAB) } + + if (shareableDevices.isEmpty()) { + list.add(SyncShareOption.AddNewDevice) + } + + val shareOptions = shareableDevices.map { + when (it.deviceType) { + DeviceType.MOBILE -> SyncShareOption.Mobile(it.displayName, it) + else -> SyncShareOption.Desktop(it.displayName, it) + } + } + list.addAll(shareOptions) + + if (shareableDevices.size > 1) { + list.add(SyncShareOption.SendAll(shareableDevices)) + } + } + return list + } } @Parcelize diff --git a/app/src/main/java/org/mozilla/fenix/share/ShareInteractor.kt b/app/src/main/java/org/mozilla/fenix/share/ShareInteractor.kt index 8eebc74b25..d80e3a3b6f 100644 --- a/app/src/main/java/org/mozilla/fenix/share/ShareInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/share/ShareInteractor.kt @@ -5,7 +5,7 @@ package org.mozilla.fenix.share import mozilla.components.concept.sync.Device -import org.mozilla.fenix.share.listadapters.Application +import org.mozilla.fenix.share.listadapters.AppShareOption /** * Interactor for the share screen. @@ -31,7 +31,7 @@ class ShareInteractor : ShareCloseInteractor, ShareToAccountDevicesInteractor, S TODO("not yet!? implemented") } - override fun onShareToApp(appToShareTo: Application) { + override fun onShareToApp(appToShareTo: AppShareOption) { TODO("not yet!? implemented") } } diff --git a/app/src/main/java/org/mozilla/fenix/share/ShareToAccountDevicesView.kt b/app/src/main/java/org/mozilla/fenix/share/ShareToAccountDevicesView.kt index bdd5109583..9be59e389e 100644 --- a/app/src/main/java/org/mozilla/fenix/share/ShareToAccountDevicesView.kt +++ b/app/src/main/java/org/mozilla/fenix/share/ShareToAccountDevicesView.kt @@ -7,8 +7,11 @@ package org.mozilla.fenix.share import android.view.LayoutInflater import android.view.ViewGroup import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.share_to_account_devices.* import mozilla.components.concept.sync.Device import org.mozilla.fenix.R +import org.mozilla.fenix.share.listadapters.AccountDevicesShareAdapter +import org.mozilla.fenix.share.listadapters.SyncShareOption /** * Callbacks for possible user interactions on the [ShareToAccountDevicesView] @@ -27,5 +30,13 @@ class ShareToAccountDevicesView( init { LayoutInflater.from(containerView.context) .inflate(R.layout.share_to_account_devices, containerView, true) + + devicesList.adapter = AccountDevicesShareAdapter(interactor) + } + + fun setSharetargets(targets: List) { + with(devicesList.adapter as AccountDevicesShareAdapter) { + updateData(targets) + } } } diff --git a/app/src/main/java/org/mozilla/fenix/share/ShareToAppsView.kt b/app/src/main/java/org/mozilla/fenix/share/ShareToAppsView.kt index 39aeb1f587..84bf95d3ce 100644 --- a/app/src/main/java/org/mozilla/fenix/share/ShareToAppsView.kt +++ b/app/src/main/java/org/mozilla/fenix/share/ShareToAppsView.kt @@ -5,24 +5,38 @@ package org.mozilla.fenix.share import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import kotlinx.android.extensions.LayoutContainer import org.mozilla.fenix.R -import org.mozilla.fenix.share.listadapters.Application +import org.mozilla.fenix.share.listadapters.AppShareOption +import kotlinx.android.synthetic.main.share_to_apps.* +import org.mozilla.fenix.share.listadapters.AppShareAdapter /** * Callbacks for possible user interactions on the [ShareCloseView] */ interface ShareToAppsInteractor { - fun onShareToApp(appToShareTo: Application) + fun onShareToApp(appToShareTo: AppShareOption) } class ShareToAppsView( override val containerView: ViewGroup, - private val interactor: ShareToAppsInteractor + interactor: ShareToAppsInteractor ) : LayoutContainer { init { LayoutInflater.from(containerView.context) .inflate(R.layout.share_to_apps, containerView, true) + + appsList.adapter = AppShareAdapter(interactor) + } + + fun setSharetargets(targets: List) { + progressBar.visibility = View.GONE + appsList.visibility = View.VISIBLE + + with(appsList.adapter as AppShareAdapter) { + updateData(targets) + } } }