diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-06-17 18:10:59 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2019-06-17 18:10:59 -0300 |
| commit | 76520c1f7335e514eadd204e12c9b91f914a81df (patch) | |
| tree | 24a6bcfb2522191441f903a1afbe86bcbda45802 /android/src | |
| parent | 4df5577ee29a0ba4943f0a9db9293aee663bbb7d (diff) | |
| parent | c9c01b7ee6526a5ea5dab207fa9a5af41b5b6c4d (diff) | |
| download | mullvadvpn-76520c1f7335e514eadd204e12c9b91f914a81df.tar.xz mullvadvpn-76520c1f7335e514eadd204e12c9b91f914a81df.zip | |
Merge branch 'listen-for-relay-list-updates'
Diffstat (limited to 'android/src')
8 files changed, 234 insertions, 64 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt index 71416c92c4..e21c7b6682 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/MainActivity.kt @@ -20,27 +20,21 @@ import net.mullvad.mullvadvpn.model.RelaySettings import net.mullvad.mullvadvpn.model.Settings import net.mullvad.mullvadvpn.relaylist.RelayItem import net.mullvad.mullvadvpn.relaylist.RelayList +import net.mullvad.mullvadvpn.relaylist.RelayListListener class MainActivity : FragmentActivity() { var asyncDaemon = CompletableDeferred<MullvadDaemon>() val daemon get() = runBlocking { asyncDaemon.await() } - var asyncRelayList: Deferred<RelayList> = fetchRelayList() - private set - val relayList: RelayList - get() = runBlocking { asyncRelayList.await() } - var asyncSettings = fetchSettings() private set val settings get() = runBlocking { asyncSettings.await() } val accountCache = AccountCache(this) + var relayListListener = RelayListListener(this) - var selectedRelayItem: RelayItem? = null - - private val restoreSelectedRelayListItemJob = restoreSelectedRelayListItem() private var waitForDaemonJob: Job? = null private val serviceConnection = object : ServiceConnection { @@ -84,11 +78,10 @@ class MainActivity : FragmentActivity() { override fun onDestroy() { accountCache.onDestroy() + relayListListener.onDestroy() - restoreSelectedRelayListItemJob.cancel() waitForDaemonJob?.cancel() asyncSettings.cancel() - asyncRelayList.cancel() asyncDaemon.cancel() super.onDestroy() @@ -122,25 +115,7 @@ class MainActivity : FragmentActivity() { } } - private fun fetchRelayList() = GlobalScope.async(Dispatchers.Default) { - RelayList(asyncDaemon.await().getRelayLocations()) - } - private fun fetchSettings() = GlobalScope.async(Dispatchers.Default) { asyncDaemon.await().getSettings() } - - private fun restoreSelectedRelayListItem() = GlobalScope.launch(Dispatchers.Default) { - val relaySettings = asyncSettings.await().relaySettings - - when (relaySettings) { - is RelaySettings.CustomTunnelEndpoint -> selectedRelayItem = null - is RelaySettings.RelayConstraints -> { - val location = relaySettings.location - val relayList = asyncRelayList.await() - - selectedRelayItem = relayList.findItemForLocation(location, true) - } - } - } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt index d16ba06a1a..a777611c84 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/MullvadDaemon.kt @@ -14,6 +14,7 @@ class MullvadDaemon(val vpnService: MullvadVpnService) { initialize(vpnService) } + var onRelayListChange: ((RelayList) -> Unit)? = null var onTunnelStateChange: ((TunnelStateTransition) -> Unit)? = null external fun connect() @@ -30,6 +31,10 @@ class MullvadDaemon(val vpnService: MullvadVpnService) { private external fun initialize(vpnService: MullvadVpnService) + private fun notifyRelayListEvent(relayList: RelayList) { + onRelayListChange?.invoke(relayList) + } + private fun notifyTunnelStateEvent(event: TunnelStateTransition) { onTunnelStateChange?.invoke(event) } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/SelectLocationFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/SelectLocationFragment.kt index 5ca767c19e..4255dc3060 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/SelectLocationFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/SelectLocationFragment.kt @@ -3,7 +3,9 @@ package net.mullvad.mullvadvpn import kotlinx.coroutines.launch import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job +import android.content.Context import android.os.Bundle import android.support.v4.app.Fragment import android.support.v7.widget.LinearLayoutManager @@ -12,6 +14,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageButton +import android.widget.ViewSwitcher import net.mullvad.mullvadvpn.model.Constraint import net.mullvad.mullvadvpn.model.LocationConstraint @@ -20,8 +23,37 @@ import net.mullvad.mullvadvpn.relaylist.RelayItem import net.mullvad.mullvadvpn.relaylist.RelayItemDividerDecoration import net.mullvad.mullvadvpn.relaylist.RelayList import net.mullvad.mullvadvpn.relaylist.RelayListAdapter +import net.mullvad.mullvadvpn.relaylist.RelayListListener class SelectLocationFragment : Fragment() { + private lateinit var parentActivity: MainActivity + private lateinit var relayListListener: RelayListListener + + private lateinit var relayListContainer: ViewSwitcher + + private val relayListAdapter = RelayListAdapter() + + private var updateRelayListJob: Job? = null + + init { + relayListAdapter.onSelect = { relayItem -> + relayListListener.selectedRelayItem = relayItem + updateLocationConstraint() + close() + } + } + + override fun onAttach(context: Context) { + super.onAttach(context) + + parentActivity = context as MainActivity + relayListListener = parentActivity.relayListListener + + relayListListener.onRelayListChange = { relayList, selectedItem -> + updateRelayListJob = updateRelayList(relayList, selectedItem) + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -31,26 +63,25 @@ class SelectLocationFragment : Fragment() { view.findViewById<ImageButton>(R.id.close).setOnClickListener { close() } + relayListContainer = view.findViewById<ViewSwitcher>(R.id.relay_list_container) + relayListContainer.showNext() + configureRelayList(view.findViewById<RecyclerView>(R.id.relay_list)) return view } + override fun onDestroyView() { + updateRelayListJob?.cancel() + + super.onDestroyView() + } + fun close() { activity?.onBackPressed() } private fun configureRelayList(relayList: RecyclerView) { - val parentActivity = activity as MainActivity - val relayListAdapter = - RelayListAdapter(parentActivity.relayList, parentActivity.selectedRelayItem) - - relayListAdapter.onSelect = { relayItem -> - parentActivity.selectedRelayItem = relayItem - updateLocationConstraint(relayItem) - close() - } - relayList.apply { layoutManager = LinearLayoutManager(context!!) adapter = relayListAdapter @@ -59,19 +90,22 @@ class SelectLocationFragment : Fragment() { } } - private fun updateLocationConstraint(relayItem: RelayItem?) = - GlobalScope.launch(Dispatchers.Default) { - val parentActivity = activity as MainActivity - var constraint: Constraint<LocationConstraint> - - if (relayItem == null) { - constraint = Constraint.Any() - } else { - constraint = Constraint.Only(relayItem.location) - } + private fun updateLocationConstraint() = GlobalScope.launch(Dispatchers.Default) { + val constraint = relayListListener.selectedRelayLocation parentActivity.asyncDaemon.await().updateRelaySettings( RelaySettingsUpdate.RelayConstraintsUpdate(constraint) ) } + + private fun updateRelayList(relayList: RelayList, selectedItem: RelayItem?) = + GlobalScope.launch(Dispatchers.Main) { + relayListAdapter.onRelayListChange(relayList, selectedItem) + + if (relayList.countries.isEmpty()) { + relayListContainer.showPrevious() + } else if (relayListContainer.displayedChild == 0) { + relayListContainer.showNext() + } + } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListAdapter.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListAdapter.kt index de3336a4ea..60ad96171c 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListAdapter.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListAdapter.kt @@ -9,10 +9,9 @@ import android.view.ViewGroup import net.mullvad.mullvadvpn.R -class RelayListAdapter( - private val relayList: RelayList, - private var selectedItem: RelayItem? -) : Adapter<RelayItemHolder>() { +class RelayListAdapter : Adapter<RelayItemHolder>() { + private var relayList: RelayList? = null + private var selectedItem: RelayItem? = null private val activeIndices = LinkedList<WeakReference<RelayListAdapterPosition>>() private var selectedItemHolder: RelayItemHolder? = null @@ -29,23 +28,34 @@ class RelayListAdapter( } override fun onBindViewHolder(holder: RelayItemHolder, position: Int) { - var remaining = position + val relayList = this.relayList - for (country in relayList.countries) { - val itemOrCount = country.getItem(remaining) + if (relayList != null) { + var remaining = position - when (itemOrCount) { - is GetItemResult.Item -> { - bindHolderToItem(holder, itemOrCount.item, position) - return + for (country in relayList.countries) { + val itemOrCount = country.getItem(remaining) + + when (itemOrCount) { + is GetItemResult.Item -> { + bindHolderToItem(holder, itemOrCount.item, position) + return + } + is GetItemResult.Count -> remaining -= itemOrCount.count } - is GetItemResult.Count -> remaining -= itemOrCount.count } } } override fun getItemCount() = - relayList.countries.map { country -> country.visibleItemCount }.sum() + relayList?.countries?.map { country -> country.visibleItemCount }?.sum() ?: 0 + + fun onRelayListChange(relayList: RelayList, selectedItem: RelayItem?) { + this.relayList = relayList + this.selectedItem = selectedItem + + notifyDataSetChanged() + } fun selectItem(item: RelayItem?, holder: RelayItemHolder?) { selectedItemHolder?.selected = false diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListListener.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListListener.kt new file mode 100644 index 0000000000..87624b116c --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListListener.kt @@ -0,0 +1,113 @@ +package net.mullvad.mullvadvpn.relaylist + +import kotlinx.coroutines.launch +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope + +import net.mullvad.mullvadvpn.MainActivity +import net.mullvad.mullvadvpn.model.Constraint +import net.mullvad.mullvadvpn.model.LocationConstraint +import net.mullvad.mullvadvpn.model.RelaySettings +import net.mullvad.mullvadvpn.MullvadDaemon + +class RelayListListener(val parentActivity: MainActivity) { + private val daemon = CompletableDeferred<MullvadDaemon>() + private val setUpJob = setUp() + + private var relayList: RelayList? = null + private var relaySettings: RelaySettings? = null + + var selectedRelayItem: RelayItem? = null + set(value) { + field = value + updateRelaySettings() + } + + val selectedRelayLocation: Constraint<LocationConstraint> + get() { + val location = selectedRelayItem?.location + + if (location == null) { + return Constraint.Any() + } else { + return Constraint.Only(location) + } + } + + var onRelayListChange: ((RelayList, RelayItem?) -> Unit)? = null + set(value) { + field = value + + synchronized(this) { + val relayList = this.relayList + + if (relayList != null) { + value?.invoke(relayList, selectedRelayItem) + } + } + } + + fun onDestroy() { + setUpJob.cancel() + + if (daemon.isActive) { + daemon.cancel() + } else { + daemon.getCompleted().onRelayListChange = null + } + } + + private fun setUp() = GlobalScope.launch(Dispatchers.Default) { + daemon.complete(parentActivity.asyncDaemon.await()) + + setUpListener() + fetchInitialRelayList() + } + + private suspend fun setUpListener() { + daemon.await().onRelayListChange = { relayLocations -> + relayListChanged(RelayList(relayLocations)) + } + } + + private suspend fun fetchInitialRelayList() { + val relayLocations = daemon.await().getRelayLocations() + + relaySettings = parentActivity.asyncSettings.await().relaySettings + + synchronized(this) { + if (relayList == null) { + relayListChanged(RelayList(relayLocations)) + } + } + } + + private fun relayListChanged(newRelayList: RelayList) { + synchronized(this) { + relayList = newRelayList + selectedRelayItem = findSelectedRelayItem() + + onRelayListChange?.invoke(newRelayList, selectedRelayItem) + } + } + + private fun findSelectedRelayItem(): RelayItem? { + val relaySettings = this.relaySettings + + when (relaySettings) { + is RelaySettings.CustomTunnelEndpoint -> return null + is RelaySettings.RelayConstraints -> { + val location = relaySettings.location + + return relayList?.findItemForLocation(location, true) + } + } + + return null + } + + private fun updateRelaySettings() { + relaySettings = RelaySettings.RelayConstraints(selectedRelayLocation) + } +} diff --git a/android/src/main/res/anim/fade_in.xml b/android/src/main/res/anim/fade_in.xml new file mode 100644 index 0000000000..fc857c772d --- /dev/null +++ b/android/src/main/res/anim/fade_in.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <alpha + android:fromAlpha="0.0" + android:toAlpha="1.0" + android:duration="450" + /> +</set> diff --git a/android/src/main/res/anim/fade_out.xml b/android/src/main/res/anim/fade_out.xml new file mode 100644 index 0000000000..c1d91f102b --- /dev/null +++ b/android/src/main/res/anim/fade_out.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <alpha + android:fromAlpha="1.0" + android:toAlpha="0.0" + android:duration="450" + /> +</set> diff --git a/android/src/main/res/layout/select_location.xml b/android/src/main/res/layout/select_location.xml index a372b32a9f..3a07326b48 100644 --- a/android/src/main/res/layout/select_location.xml +++ b/android/src/main/res/layout/select_location.xml @@ -37,10 +37,27 @@ android:textSize="13sp" android:text="@string/select_location_description" /> - <android.support.v7.widget.RecyclerView android:id="@+id/relay_list" + <ViewSwitcher android:id="@+id/relay_list_container" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" - android:scrollbars="vertical" - /> + android:inAnimation="@anim/fade_in" + android:outAnimation="@anim/fade_out" + > + <ProgressBar + android:layout_width="60dp" + android:layout_height="60dp" + android:layout_gravity="center" + android:indeterminate="true" + android:indeterminateOnly="true" + android:indeterminateDuration="600" + android:indeterminateDrawable="@drawable/icon_spinner" + android:visibility="invisible" + /> + <android.support.v7.widget.RecyclerView android:id="@+id/relay_list" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scrollbars="vertical" + /> + </ViewSwitcher> </LinearLayout> |
