Mimic Android Wi-Fi setting design in tethering
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
package be.mygod.vpnhotspot
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -16,9 +14,7 @@ import android.support.v7.util.SortedList
|
||||
import android.support.v7.widget.DefaultItemAnimator
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import be.mygod.vpnhotspot.databinding.FragmentTetheringBinding
|
||||
@@ -26,7 +22,12 @@ import be.mygod.vpnhotspot.databinding.ListitemInterfaceBinding
|
||||
import be.mygod.vpnhotspot.net.ConnectivityManagerHelper
|
||||
import be.mygod.vpnhotspot.net.TetherType
|
||||
|
||||
class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener {
|
||||
class TetheringFragment : Fragment(), ServiceConnection {
|
||||
companion object {
|
||||
private const val VIEW_TYPE_INTERFACE = 0
|
||||
private const val VIEW_TYPE_MANAGE = 1
|
||||
}
|
||||
|
||||
private abstract class BaseSorter<T> : SortedList.Callback<T>() {
|
||||
override fun onInserted(position: Int, count: Int) { }
|
||||
override fun areContentsTheSame(oldItem: T?, newItem: T?): Boolean = oldItem == newItem
|
||||
@@ -48,7 +49,7 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
||||
var active = binder?.active?.contains(iface) == true
|
||||
}
|
||||
|
||||
class InterfaceViewHolder(val binding: ListitemInterfaceBinding) : RecyclerView.ViewHolder(binding.root),
|
||||
private class InterfaceViewHolder(val binding: ListitemInterfaceBinding) : RecyclerView.ViewHolder(binding.root),
|
||||
View.OnClickListener {
|
||||
init {
|
||||
itemView.setOnClickListener(this)
|
||||
@@ -64,38 +65,50 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
||||
data.active = !data.active
|
||||
}
|
||||
}
|
||||
inner class InterfaceAdapter : RecyclerView.Adapter<InterfaceViewHolder>() {
|
||||
private class ManageViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
|
||||
init {
|
||||
view.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) = itemView.context.startActivity(Intent()
|
||||
.setClassName("com.android.settings", "com.android.settings.Settings\$TetherSettingsActivity"))
|
||||
}
|
||||
inner class TetheringAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
private val tethered = SortedList(String::class.java, StringSorter)
|
||||
|
||||
fun update(data: Set<String>) {
|
||||
val oldEmpty = tethered.size() == 0
|
||||
tethered.clear()
|
||||
tethered.addAll(data)
|
||||
notifyDataSetChanged()
|
||||
if (oldEmpty != data.isEmpty())
|
||||
if (oldEmpty) crossFade(binding.empty, binding.interfaces)
|
||||
else crossFade(binding.interfaces, binding.empty)
|
||||
}
|
||||
|
||||
override fun getItemCount() = tethered.size()
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = InterfaceViewHolder(
|
||||
ListitemInterfaceBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||
override fun onBindViewHolder(holder: InterfaceViewHolder, position: Int) {
|
||||
holder.binding.data = Data(tethered[position])
|
||||
override fun getItemCount() = tethered.size() + 1
|
||||
override fun getItemViewType(position: Int) =
|
||||
if (position == tethered.size()) VIEW_TYPE_MANAGE else VIEW_TYPE_INTERFACE
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return when (viewType) {
|
||||
VIEW_TYPE_INTERFACE -> InterfaceViewHolder(ListitemInterfaceBinding.inflate(inflater, parent, false))
|
||||
VIEW_TYPE_MANAGE -> ManageViewHolder(inflater.inflate(R.layout.listitem_manage, parent, false))
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is InterfaceViewHolder -> holder.binding.data = Data(tethered[position])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var binding: FragmentTetheringBinding
|
||||
private var binder: TetheringService.TetheringBinder? = null
|
||||
val adapter = InterfaceAdapter()
|
||||
val adapter = TetheringAdapter()
|
||||
private val receiver = broadcastReceiver { _, intent ->
|
||||
adapter.update(ConnectivityManagerHelper.getTetheredIfaces(intent.extras).toSet())
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_tethering, container, false)
|
||||
binding.toolbar.inflateMenu(R.menu.tethering)
|
||||
binding.toolbar.setOnMenuItemClickListener(this)
|
||||
binding.interfaces.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
val animator = DefaultItemAnimator()
|
||||
animator.supportsChangeAnimations = false // prevent fading-in/out when rebinding
|
||||
@@ -128,25 +141,4 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
||||
binder?.fragment = null
|
||||
binder = null
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem) = when (item.itemId) {
|
||||
R.id.systemTethering -> {
|
||||
startActivity(Intent().setClassName("com.android.settings",
|
||||
"com.android.settings.Settings\$TetherSettingsActivity"))
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun crossFade(old: View, new: View) {
|
||||
val shortAnimTime = resources.getInteger(android.R.integer.config_shortAnimTime).toLong()
|
||||
old.animate().alpha(0F).setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
old.visibility = View.GONE
|
||||
}
|
||||
}).duration = shortAnimTime
|
||||
new.alpha = 0F
|
||||
new.visibility = View.VISIBLE
|
||||
new.animate().alpha(1F).setListener(null).duration = shortAnimTime
|
||||
}
|
||||
}
|
||||
|
||||
9
mobile/src/main/res/drawable/ic_content_add.xml
Normal file
9
mobile/src/main/res/drawable/ic_content_add.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</vector>
|
||||
@@ -12,28 +12,15 @@
|
||||
android:layout_width="match_parent"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:elevation="4dp"
|
||||
android:touchscreenBlocksFocus="false"
|
||||
app:title="@string/app_name"
|
||||
android:id="@+id/toolbar"/>
|
||||
<FrameLayout
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/interfaces"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
<TextView
|
||||
android:id="@+id/empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp"
|
||||
android:text="@string/tethering_no_interfaces"/>
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/interfaces"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:scrollbars="vertical"
|
||||
android:visibility="gone"
|
||||
tools:listitem="@layout/listitem_interface"
|
||||
tools:visibility="visible"/>
|
||||
</FrameLayout>
|
||||
android:layout_weight="1"
|
||||
android:clipToPadding="false"
|
||||
android:scrollbars="vertical"
|
||||
tools:listitem="@layout/listitem_interface"/>
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="4dp">
|
||||
android:paddingTop="8dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -12,14 +12,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp">
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@{data.icon}"
|
||||
android:tint="?android:attr/textColorPrimary"
|
||||
tools:src="@drawable/ic_device_network_wifi"/>
|
||||
@@ -32,13 +30,14 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:checked="@{data.active}"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@{data.iface}"
|
||||
android:checked="@{data.active}"
|
||||
tools:text="wlan0"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
29
mobile/src/main/res/layout/listitem_manage.xml
Normal file
29
mobile/src/main/res/layout/listitem_manage.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/ic_content_add"
|
||||
android:tint="@color/colorAccent"/>
|
||||
|
||||
<Space
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="0dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:text="Manage..."/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/systemTethering"
|
||||
android:icon="@drawable/ic_action_settings"
|
||||
android:title="@string/tethering_system_tethering"
|
||||
app:showAsAction="ifRoom"/>
|
||||
</menu>
|
||||
@@ -37,9 +37,6 @@
|
||||
<string name="repeater_failure_reason_no_service_requests">no service requests added</string>
|
||||
<string name="repeater_failure_reason_unknown">unknown #%d</string>
|
||||
|
||||
<string name="tethering_system_tethering">System tethering</string>
|
||||
<string name="tethering_no_interfaces">To use this feature, turn on any system tethering first.</string>
|
||||
|
||||
<string name="settings_service">Service</string>
|
||||
<string name="settings_service_dns">Downstream DNS server:port</string>
|
||||
<string name="settings_service_clean">Clean/reapply routing rules</string>
|
||||
|
||||
Reference in New Issue
Block a user