diff --git a/app/src/main/java/org/mozilla/fenix/share/ShareController.kt b/app/src/main/java/org/mozilla/fenix/share/ShareController.kt index f56740289..9e293bbbe 100644 --- a/app/src/main/java/org/mozilla/fenix/share/ShareController.kt +++ b/app/src/main/java/org/mozilla/fenix/share/ShareController.kt @@ -28,6 +28,7 @@ import org.mozilla.fenix.share.listadapters.AppShareOption * Delegated by View Interactors, handles container business logic and operates changes on it. */ interface ShareController { + fun handleReauth() fun handleShareClosed() fun handleShareToApp(app: AppShareOption) fun handleAddNewDevice() @@ -53,6 +54,12 @@ class DefaultShareController( private val navController: NavController, private val dismiss: () -> Unit ) : ShareController { + override fun handleReauth() { + val directions = ShareFragmentDirections.actionShareFragmentToAccountProblemFragment() + navController.nav(R.id.shareFragment, directions) + dismiss() + } + override fun handleShareClosed() { dismiss() } 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 293c98773..49854754f 100644 --- a/app/src/main/java/org/mozilla/fenix/share/ShareFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/share/ShareFragment.kt @@ -9,6 +9,9 @@ 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.net.ConnectivityManager +import android.net.Network +import android.net.NetworkRequest import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater @@ -45,10 +48,16 @@ class ShareFragment : AppCompatDialogFragment() { private lateinit var shareToAppsView: ShareToAppsView private lateinit var appsListDeferred: Deferred> private lateinit var devicesListDeferred: Deferred> + private var connectivityManager: ConnectivityManager? = null override fun onAttach(context: Context) { super.onAttach(context) + connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager + val networkRequest = NetworkRequest.Builder().build() + connectivityManager?.registerNetworkCallback(networkRequest, networkCallback) + // Start preparing the data as soon as we have a valid Context appsListDeferred = lifecycleScope.async(Dispatchers.IO) { val shareIntent = Intent(ACTION_SEND).apply { @@ -70,6 +79,35 @@ class ShareFragment : AppCompatDialogFragment() { setStyle(STYLE_NO_TITLE, R.style.ShareDialogStyle) } + private val networkCallback = object : ConnectivityManager.NetworkCallback() { + override fun onLost(network: Network?) { + reloadDevices() + } + + override fun onAvailable(network: Network?) { + reloadDevices() + } + } + + private fun reloadDevices() { + context?.let { + val fxaAccountManager = it.components.backgroundServices.accountManager + lifecycleScope.launch { + val refreshDevicesAsync = + fxaAccountManager.authenticatedAccount()?.deviceConstellation() + ?.refreshDevicesAsync() + refreshDevicesAsync?.await() + val devicesShareOptions = buildDeviceList(fxaAccountManager) + shareToAccountDevicesView.setSharetargets(devicesShareOptions) + } + } + } + + override fun onDetach() { + connectivityManager?.unregisterNetworkCallback(networkCallback) + super.onDetach() + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -95,11 +133,8 @@ class ShareFragment : AppCompatDialogFragment() { ) ) - if (isSharingToDevicesAvailable(requireContext().applicationContext)) { - shareToAccountDevicesView = ShareToAccountDevicesView(view.devicesShareLayout, shareInteractor) - } else { - view.devicesShareGroup.visibility = View.GONE - } + shareToAccountDevicesView = + ShareToAccountDevicesView(view.devicesShareLayout, shareInteractor) shareCloseView = ShareCloseView(view.closeSharingLayout, shareInteractor) shareToAppsView = ShareToAppsView(view.appsShareLayout, shareInteractor) @@ -117,14 +152,14 @@ class ShareFragment : AppCompatDialogFragment() { } } - 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 { + private fun buildAppsList( + intentActivities: List?, + context: Context + ): List { return intentActivities?.map { resolveInfo -> AppShareOption( resolveInfo.loadLabel(context.packageManager).toString(), @@ -135,16 +170,30 @@ class ShareFragment : AppCompatDialogFragment() { } ?: emptyList() } + @Suppress("ReturnCount") private fun buildDeviceList(accountManager: FxaAccountManager): List { val list = mutableListOf() + val activeNetwork = connectivityManager?.activeNetworkInfo + if (activeNetwork?.isConnected != true) { + list.add(SyncShareOption.Offline) + return list + } + 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 (accountManager.accountNeedsReauth()) { + list.add(SyncShareOption.Reconnect) + 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) 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 902508bb6..64a45831c 100644 --- a/app/src/main/java/org/mozilla/fenix/share/ShareInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/share/ShareInteractor.kt @@ -13,6 +13,10 @@ import org.mozilla.fenix.share.listadapters.AppShareOption class ShareInteractor( private val controller: ShareController ) : ShareCloseInteractor, ShareToAccountDevicesInteractor, ShareToAppsInteractor { + override fun onReauth() { + controller.handleReauth() + } + override fun onShareClosed() { controller.handleShareClosed() } 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 9be59e389..151e095e4 100644 --- a/app/src/main/java/org/mozilla/fenix/share/ShareToAccountDevicesView.kt +++ b/app/src/main/java/org/mozilla/fenix/share/ShareToAccountDevicesView.kt @@ -18,6 +18,7 @@ import org.mozilla.fenix.share.listadapters.SyncShareOption */ interface ShareToAccountDevicesInteractor { fun onSignIn() + fun onReauth() fun onAddNewDevice() fun onShareToDevice(device: Device) fun onShareToAllDevices(devices: List) diff --git a/app/src/main/java/org/mozilla/fenix/share/listadapters/AccountDevicesAdapter.kt b/app/src/main/java/org/mozilla/fenix/share/listadapters/AccountDevicesAdapter.kt index 215f51f07..d779369a4 100644 --- a/app/src/main/java/org/mozilla/fenix/share/listadapters/AccountDevicesAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/share/listadapters/AccountDevicesAdapter.kt @@ -37,6 +37,8 @@ class AccountDevicesShareAdapter( } sealed class SyncShareOption { + object Reconnect : SyncShareOption() + object Offline : SyncShareOption() object SignIn : SyncShareOption() object AddNewDevice : SyncShareOption() data class SendAll(val devices: List) : SyncShareOption() diff --git a/app/src/main/java/org/mozilla/fenix/share/viewholders/AccountDeviceViewHolder.kt b/app/src/main/java/org/mozilla/fenix/share/viewholders/AccountDeviceViewHolder.kt index 9d2447818..2427d8337 100644 --- a/app/src/main/java/org/mozilla/fenix/share/viewholders/AccountDeviceViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/share/viewholders/AccountDeviceViewHolder.kt @@ -37,6 +37,10 @@ class AccountDeviceViewHolder( is SyncShareOption.SendAll -> interactor.onShareToAllDevices(option.devices) is SyncShareOption.Mobile -> interactor.onShareToDevice(option.device) is SyncShareOption.Desktop -> interactor.onShareToDevice(option.device) + SyncShareOption.Reconnect -> interactor.onReauth() + SyncShareOption.Offline -> { + // nothing we are offline + } } } } @@ -48,6 +52,16 @@ class AccountDeviceViewHolder( R.drawable.mozac_ic_sync, R.color.default_share_background ) + SyncShareOption.Reconnect -> Triple( + context.getText(R.string.sync_reconnect), + R.drawable.mozac_ic_warning, + R.color.default_share_background + ) + SyncShareOption.Offline -> Triple( + context.getText(R.string.sync_offline), + R.drawable.mozac_ic_warning, + R.color.default_share_background + ) SyncShareOption.AddNewDevice -> Triple( context.getText(R.string.sync_connect_device), R.drawable.mozac_ic_new, @@ -75,6 +89,7 @@ class AccountDeviceViewHolder( background.setColorFilter(ContextCompat.getColor(context, colorRes), PorterDuff.Mode.SRC_IN) drawable.setTint(ContextCompat.getColor(context, R.color.device_foreground)) } + itemView.isClickable = option != SyncShareOption.Offline itemView.deviceName.text = name } diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index e2b64706f..35eec3d24 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -490,6 +490,9 @@ app:destination="@+id/turnOnSyncFragment" app:popUpTo="@id/shareFragment" app:popUpToInclusive="true" /> +