Update dependencies
This commit is contained in:
@@ -12,7 +12,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
classpath 'com.android.tools.build:gradle:4.0.0-alpha08'
|
||||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0'
|
classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0'
|
||||||
classpath 'com.google.gms:google-services:4.3.3'
|
classpath 'com.google.gms:google-services:4.3.3'
|
||||||
classpath 'io.fabric.tools:gradle:1.31.2'
|
classpath 'io.fabric.tools:gradle:1.31.2'
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
29
gradlew
vendored
29
gradlew
vendored
@@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|||||||
else
|
else
|
||||||
eval `echo args$i`="\"$arg\""
|
eval `echo args$i`="\"$arg\""
|
||||||
fi
|
fi
|
||||||
i=$((i+1))
|
i=`expr $i + 1`
|
||||||
done
|
done
|
||||||
case $i in
|
case $i in
|
||||||
(0) set -- ;;
|
0) set -- ;;
|
||||||
(1) set -- "$args0" ;;
|
1) set -- "$args0" ;;
|
||||||
(2) set -- "$args0" "$args1" ;;
|
2) set -- "$args0" "$args1" ;;
|
||||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -175,14 +175,9 @@ save () {
|
|||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
echo " "
|
echo " "
|
||||||
}
|
}
|
||||||
APP_ARGS=$(save "$@")
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
|
||||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ if (!getGradle().getStartParameter().getTaskRequests().toString().contains("Fdro
|
|||||||
def javaVersion = JavaVersion.VERSION_1_8
|
def javaVersion = JavaVersion.VERSION_1_8
|
||||||
def aux = [
|
def aux = [
|
||||||
'com.crashlytics.sdk.android:crashlytics:2.10.1',
|
'com.crashlytics.sdk.android:crashlytics:2.10.1',
|
||||||
'com.google.firebase:firebase-analytics:17.2.1',
|
'com.google.firebase:firebase-analytics:17.2.2',
|
||||||
]
|
]
|
||||||
def lifecycleVersion = '2.2.0-rc03'
|
def lifecycleVersion = '2.2.0-rc03'
|
||||||
def roomVersion = '2.2.3'
|
def roomVersion = '2.2.3'
|
||||||
@@ -19,6 +19,7 @@ def roomVersion = '2.2.3'
|
|||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 29
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
coreLibraryDesugaringEnabled true
|
||||||
sourceCompatibility javaVersion
|
sourceCompatibility javaVersion
|
||||||
targetCompatibility javaVersion
|
targetCompatibility javaVersion
|
||||||
}
|
}
|
||||||
@@ -37,6 +38,10 @@ android {
|
|||||||
]
|
]
|
||||||
buildConfigField "boolean", "DONATIONS", "true"
|
buildConfigField "boolean", "DONATIONS", "true"
|
||||||
}
|
}
|
||||||
|
buildFeatures {
|
||||||
|
dataBinding = true
|
||||||
|
viewBinding = true
|
||||||
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
pseudoLocalesEnabled true
|
pseudoLocalesEnabled true
|
||||||
@@ -47,7 +52,6 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataBinding.enabled = true
|
|
||||||
flavorDimensions("freedom")
|
flavorDimensions("freedom")
|
||||||
packagingOptions.exclude '**/*.kotlin_*'
|
packagingOptions.exclude '**/*.kotlin_*'
|
||||||
productFlavors {
|
productFlavors {
|
||||||
@@ -73,22 +77,23 @@ androidExtensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.4'
|
||||||
kapt "androidx.room:room-compiler:$roomVersion"
|
kapt "androidx.room:room-compiler:$roomVersion"
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation 'androidx.browser:browser:1.2.0'
|
implementation 'androidx.browser:browser:1.2.0'
|
||||||
implementation 'androidx.core:core-ktx:1.1.0'
|
implementation 'androidx.core:core-ktx:1.1.0'
|
||||||
implementation 'androidx.emoji:emoji:1.0.0'
|
implementation 'androidx.emoji:emoji:1.0.0'
|
||||||
|
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc05'
|
||||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
|
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
|
||||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
|
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
|
||||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
|
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
|
|
||||||
implementation 'androidx.preference:preference:1.1.0'
|
implementation 'androidx.preference:preference:1.1.0'
|
||||||
implementation "androidx.room:room-ktx:$roomVersion"
|
implementation "androidx.room:room-ktx:$roomVersion"
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
|
||||||
implementation 'com.android.billingclient:billing-ktx:2.1.0'
|
implementation 'com.android.billingclient:billing-ktx:2.1.0'
|
||||||
implementation 'com.github.topjohnwu.libsu:core:2.5.1'
|
implementation 'com.github.topjohnwu.libsu:core:2.5.1'
|
||||||
implementation 'com.google.android.material:material:1.1.0-rc01'
|
implementation 'com.google.android.material:material:1.1.0-rc02'
|
||||||
implementation 'com.google.zxing:core:3.4.0'
|
implementation 'com.google.zxing:core:3.4.0'
|
||||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||||
implementation 'com.linkedin.dexmaker:dexmaker:2.25.1'
|
implementation 'com.linkedin.dexmaker:dexmaker:2.25.1'
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.Spinner
|
|
||||||
import androidx.appcompat.app.AppCompatDialogFragment
|
import androidx.appcompat.app.AppCompatDialogFragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
|
import be.mygod.vpnhotspot.databinding.FragmentEbegBinding
|
||||||
import be.mygod.vpnhotspot.util.launchUrl
|
import be.mygod.vpnhotspot.util.launchUrl
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import com.android.billingclient.api.*
|
import com.android.billingclient.api.*
|
||||||
import kotlinx.android.synthetic.main.fragment_ebeg.view.*
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -65,11 +64,11 @@ class EBegFragment : AppCompatDialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var googleSpinner: Spinner
|
private lateinit var binding: FragmentEbegBinding
|
||||||
private var skus: List<SkuDetails>? = null
|
private var skus: List<SkuDetails>? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
googleSpinner.apply {
|
binding.donationsGoogleAndroidMarketSpinner.apply {
|
||||||
val adapter = ArrayAdapter(context ?: return, android.R.layout.simple_spinner_item,
|
val adapter = ArrayAdapter(context ?: return, android.R.layout.simple_spinner_item,
|
||||||
value?.map { it.price } ?: listOf("…"))
|
value?.map { it.price } ?: listOf("…"))
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||||
@@ -77,12 +76,13 @@ class EBegFragment : AppCompatDialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
inflater.inflate(R.layout.fragment_ebeg, container, false)
|
binding = FragmentEbegBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
dialog!!.setTitle(R.string.settings_misc_donate)
|
dialog!!.setTitle(R.string.settings_misc_donate)
|
||||||
googleSpinner = view.donations__google_android_market_spinner
|
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
billingClient.querySkuDetails(SkuDetailsParams.newBuilder().apply {
|
billingClient.querySkuDetails(SkuDetailsParams.newBuilder().apply {
|
||||||
setSkusList(listOf("donate001", "donate002", "donate005", "donate010", "donate020", "donate050",
|
setSkusList(listOf("donate001", "donate002", "donate005", "donate010", "donate020", "donate050",
|
||||||
@@ -95,14 +95,15 @@ class EBegFragment : AppCompatDialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view.donations__google_android_market_donate_button.setOnClickListener {
|
binding.donationsGoogleAndroidMarketDonateButton.setOnClickListener {
|
||||||
val sku = skus?.getOrNull(googleSpinner.selectedItemPosition)
|
val sku = skus?.getOrNull(binding.donationsGoogleAndroidMarketSpinner.selectedItemPosition)
|
||||||
if (sku != null) billingClient.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder().apply {
|
if (sku != null) billingClient.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder().apply {
|
||||||
setSkuDetails(sku)
|
setSkuDetails(sku)
|
||||||
}.build()) else SmartSnackbar.make(R.string.donations__google_android_market_not_supported).show()
|
}.build()) else SmartSnackbar.make(R.string.donations__google_android_market_not_supported).show()
|
||||||
}
|
}
|
||||||
@Suppress("ConstantConditionIf")
|
@Suppress("ConstantConditionIf")
|
||||||
if (BuildConfig.DONATIONS) (view.donations__more_stub.inflate() as Button)
|
if (BuildConfig.DONATIONS) (binding.donationsMoreStub.inflate() as Button).setOnClickListener {
|
||||||
.setOnClickListener { requireContext().launchUrl("https://mygod.be/donate/") }
|
requireContext().launchUrl("https://mygod.be/donate/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ package be.mygod.vpnhotspot
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.get
|
|
||||||
import androidx.lifecycle.observe
|
import androidx.lifecycle.observe
|
||||||
import be.mygod.vpnhotspot.client.ClientViewModel
|
import be.mygod.vpnhotspot.client.ClientViewModel
|
||||||
import be.mygod.vpnhotspot.client.ClientsFragment
|
import be.mygod.vpnhotspot.client.ClientsFragment
|
||||||
@@ -22,16 +20,15 @@ import com.google.android.material.bottomnavigation.BottomNavigationView
|
|||||||
import java.net.Inet4Address
|
import java.net.Inet4Address
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
|
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
|
||||||
private lateinit var binding: ActivityMainBinding
|
lateinit var binding: ActivityMainBinding
|
||||||
val provider by lazy { ViewModelProvider(this) }
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
binding.lifecycleOwner = this
|
setContentView(binding.root)
|
||||||
binding.navigation.setOnNavigationItemSelectedListener(this)
|
binding.navigation.setOnNavigationItemSelectedListener(this)
|
||||||
if (savedInstanceState == null) displayFragment(TetheringFragment())
|
if (savedInstanceState == null) displayFragment(TetheringFragment())
|
||||||
val model = provider.get<ClientViewModel>()
|
val model by viewModels<ClientViewModel>()
|
||||||
if (RepeaterService.supported) ServiceForegroundConnector(this, model, RepeaterService::class)
|
if (RepeaterService.supported) ServiceForegroundConnector(this, model, RepeaterService::class)
|
||||||
model.clients.observe(this) { clients ->
|
model.clients.observe(this) { clients ->
|
||||||
val count = clients.count {
|
val count = clients.count {
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import android.os.Build
|
|||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.net.Routing
|
import be.mygod.vpnhotspot.net.Routing
|
||||||
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
|
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
|
||||||
import be.mygod.vpnhotspot.util.putIfAbsentCompat
|
|
||||||
import be.mygod.vpnhotspot.util.removeCompat
|
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@@ -56,7 +54,7 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
|
|||||||
val started get() = active[downstream] === this
|
val started get() = active[downstream] === this
|
||||||
private var routing: Routing? = null
|
private var routing: Routing? = null
|
||||||
|
|
||||||
fun start() = when (val other = active.putIfAbsentCompat(downstream, this)) {
|
fun start() = when (val other = active.putIfAbsent(downstream, this)) {
|
||||||
null -> {
|
null -> {
|
||||||
if (isWifi) WifiDoubleLock.acquire(this)
|
if (isWifi) WifiDoubleLock.acquire(this)
|
||||||
initRouting()
|
initRouting()
|
||||||
@@ -85,7 +83,7 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
|
|||||||
protected abstract fun Routing.configure()
|
protected abstract fun Routing.configure()
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
if (active.removeCompat(downstream, this)) {
|
if (active.remove(downstream, this)) {
|
||||||
if (isWifi) WifiDoubleLock.release(this)
|
if (isWifi) WifiDoubleLock.release(this)
|
||||||
routing?.revert()
|
routing?.revert()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.databinding.BaseObservable
|
import androidx.databinding.BaseObservable
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.get
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.observe
|
import androidx.lifecycle.observe
|
||||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
@@ -38,7 +37,6 @@ import be.mygod.vpnhotspot.room.ClientStats
|
|||||||
import be.mygod.vpnhotspot.room.TrafficRecord
|
import be.mygod.vpnhotspot.room.TrafficRecord
|
||||||
import be.mygod.vpnhotspot.room.macToString
|
import be.mygod.vpnhotspot.room.macToString
|
||||||
import be.mygod.vpnhotspot.util.SpanFormatter
|
import be.mygod.vpnhotspot.util.SpanFormatter
|
||||||
import be.mygod.vpnhotspot.util.computeIfAbsentCompat
|
|
||||||
import be.mygod.vpnhotspot.util.toPluralInt
|
import be.mygod.vpnhotspot.util.toPluralInt
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
@@ -182,7 +180,7 @@ class ClientsFragment : Fragment() {
|
|||||||
val client = getItem(position)
|
val client = getItem(position)
|
||||||
holder.binding.client = client
|
holder.binding.client = client
|
||||||
holder.binding.rate =
|
holder.binding.rate =
|
||||||
rates.computeIfAbsentCompat(Pair(client.iface, client.mac)) { TrafficRate() }
|
rates.computeIfAbsent(Pair(client.iface, client.mac)) { TrafficRate() }
|
||||||
holder.binding.executePendingBindings()
|
holder.binding.executePendingBindings()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +195,7 @@ class ClientsFragment : Fragment() {
|
|||||||
check(newRecord.receivedPackets == oldRecord.receivedPackets)
|
check(newRecord.receivedPackets == oldRecord.receivedPackets)
|
||||||
check(newRecord.receivedBytes == oldRecord.receivedBytes)
|
check(newRecord.receivedBytes == oldRecord.receivedBytes)
|
||||||
} else {
|
} else {
|
||||||
val rate = rates.computeIfAbsentCompat(Pair(newRecord.downstream, newRecord.mac)) { TrafficRate() }
|
val rate = rates.computeIfAbsent(Pair(newRecord.downstream, newRecord.mac)) { TrafficRate() }
|
||||||
if (rate.send < 0 || rate.receive < 0) {
|
if (rate.send < 0 || rate.receive < 0) {
|
||||||
rate.send = 0
|
rate.send = 0
|
||||||
rate.receive = 0
|
rate.receive = 0
|
||||||
@@ -215,8 +213,7 @@ class ClientsFragment : Fragment() {
|
|||||||
private var rates = HashMap<Pair<String, Long>, TrafficRate>()
|
private var rates = HashMap<Pair<String, Long>, TrafficRate>()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_clients, container, false)
|
binding = FragmentClientsBinding.inflate(inflater, container, false)
|
||||||
binding.lifecycleOwner = this
|
|
||||||
binding.clients.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
binding.clients.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||||
binding.clients.itemAnimator = DefaultItemAnimator()
|
binding.clients.itemAnimator = DefaultItemAnimator()
|
||||||
binding.clients.adapter = adapter
|
binding.clients.adapter = adapter
|
||||||
@@ -224,7 +221,7 @@ class ClientsFragment : Fragment() {
|
|||||||
binding.swipeRefresher.setOnRefreshListener {
|
binding.swipeRefresher.setOnRefreshListener {
|
||||||
IpNeighbourMonitor.instance?.flush()
|
IpNeighbourMonitor.instance?.flush()
|
||||||
}
|
}
|
||||||
(activity as MainActivity).provider.get<ClientViewModel>().clients.observe(this) {
|
(activity as MainActivity).viewModels<ClientViewModel>().value.clients.observe(this) {
|
||||||
adapter.submitList(it.toMutableList())
|
adapter.submitList(it.toMutableList())
|
||||||
}
|
}
|
||||||
return binding.root
|
return binding.root
|
||||||
|
|||||||
@@ -20,9 +20,8 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.databinding.BaseObservable
|
import androidx.databinding.BaseObservable
|
||||||
import androidx.databinding.Bindable
|
import androidx.databinding.Bindable
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.get
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import be.mygod.vpnhotspot.*
|
import be.mygod.vpnhotspot.*
|
||||||
import be.mygod.vpnhotspot.databinding.ListitemRepeaterBinding
|
import be.mygod.vpnhotspot.databinding.ListitemRepeaterBinding
|
||||||
@@ -136,7 +135,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
|||||||
internal var binder: RepeaterService.Binder? = null
|
internal var binder: RepeaterService.Binder? = null
|
||||||
private var p2pInterface: String? = null
|
private var p2pInterface: String? = null
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private val holder = ViewModelProvider(parent).get<ConfigHolder>()
|
private val holder by parent.viewModels<ConfigHolder>()
|
||||||
|
|
||||||
override fun bindTo(viewHolder: RecyclerView.ViewHolder) {
|
override fun bindTo(viewHolder: RecyclerView.ViewHolder) {
|
||||||
(viewHolder as ViewHolder).binding.data = data
|
(viewHolder as ViewHolder).binding.data = data
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
@@ -31,7 +30,6 @@ import be.mygod.vpnhotspot.util.ServiceForegroundConnector
|
|||||||
import be.mygod.vpnhotspot.util.broadcastReceiver
|
import be.mygod.vpnhotspot.util.broadcastReceiver
|
||||||
import be.mygod.vpnhotspot.util.isNotGone
|
import be.mygod.vpnhotspot.util.isNotGone
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@@ -107,8 +105,8 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateMonitorList(canMonitor: List<String> = emptyList()) {
|
private fun updateMonitorList(canMonitor: List<String> = emptyList()) {
|
||||||
val activity = activity
|
val activity = activity as? MainActivity
|
||||||
val item = activity?.toolbar?.menu?.findItem(R.id.monitor) ?: return // assuming no longer foreground
|
val item = activity?.binding?.toolbar?.menu?.findItem(R.id.monitor) ?: return // assuming no longer foreground
|
||||||
item.isNotGone = canMonitor.isNotEmpty()
|
item.isNotGone = canMonitor.isNotEmpty()
|
||||||
item.subMenu.apply {
|
item.subMenu.apply {
|
||||||
clear()
|
clear()
|
||||||
@@ -157,14 +155,13 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_tethering, container, false)
|
binding = FragmentTetheringBinding.inflate(inflater, container, false)
|
||||||
binding.lifecycleOwner = this
|
|
||||||
binding.interfaces.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
binding.interfaces.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||||
binding.interfaces.itemAnimator = DefaultItemAnimator()
|
binding.interfaces.itemAnimator = DefaultItemAnimator()
|
||||||
binding.interfaces.adapter = adapter
|
binding.interfaces.adapter = adapter
|
||||||
adapter.update(emptyList(), emptyList(), emptyList())
|
adapter.update(emptyList(), emptyList(), emptyList())
|
||||||
ServiceForegroundConnector(this, this, TetheringService::class)
|
ServiceForegroundConnector(this, this, TetheringService::class)
|
||||||
requireActivity().toolbar.apply {
|
(activity as MainActivity).binding.toolbar.apply {
|
||||||
inflateMenu(R.menu.toolbar_tethering)
|
inflateMenu(R.menu.toolbar_tethering)
|
||||||
setOnMenuItemClickListener(this@TetheringFragment)
|
setOnMenuItemClickListener(this@TetheringFragment)
|
||||||
}
|
}
|
||||||
@@ -173,7 +170,7 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
requireActivity().toolbar.apply {
|
(activity as MainActivity).binding.toolbar.apply {
|
||||||
menu.clear()
|
menu.clear()
|
||||||
setOnMenuItemClickListener(null)
|
setOnMenuItemClickListener(null)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import be.mygod.vpnhotspot.net.monitor.TrafficRecorder
|
|||||||
import be.mygod.vpnhotspot.net.monitor.UpstreamMonitor
|
import be.mygod.vpnhotspot.net.monitor.UpstreamMonitor
|
||||||
import be.mygod.vpnhotspot.room.AppDatabase
|
import be.mygod.vpnhotspot.room.AppDatabase
|
||||||
import be.mygod.vpnhotspot.util.RootSession
|
import be.mygod.vpnhotspot.util.RootSession
|
||||||
import be.mygod.vpnhotspot.util.computeIfAbsentCompat
|
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@@ -235,7 +234,7 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh
|
|||||||
AppDatabase.instance.clientRecordDao.lookupOrDefaultBlocking(neighbour.lladdr).blocked) continue
|
AppDatabase.instance.clientRecordDao.lookupOrDefaultBlocking(neighbour.lladdr).blocked) continue
|
||||||
toRemove.remove(neighbour.ip)
|
toRemove.remove(neighbour.ip)
|
||||||
try {
|
try {
|
||||||
clients.computeIfAbsentCompat(neighbour.ip) { Client(neighbour.ip, neighbour.lladdr) }
|
clients.computeIfAbsent(neighbour.ip) { Client(neighbour.ip, neighbour.lladdr) }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.w(e)
|
Timber.w(e)
|
||||||
SmartSnackbar.make(e).show()
|
SmartSnackbar.make(e).show()
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import be.mygod.vpnhotspot.room.TrafficRecord
|
|||||||
import be.mygod.vpnhotspot.util.Event2
|
import be.mygod.vpnhotspot.util.Event2
|
||||||
import be.mygod.vpnhotspot.util.RootSession
|
import be.mygod.vpnhotspot.util.RootSession
|
||||||
import be.mygod.vpnhotspot.util.parseNumericAddress
|
import be.mygod.vpnhotspot.util.parseNumericAddress
|
||||||
import be.mygod.vpnhotspot.util.putIfAbsentCompat
|
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
@@ -29,7 +28,7 @@ object TrafficRecorder {
|
|||||||
AppDatabase.instance.trafficRecordDao.insert(record)
|
AppDatabase.instance.trafficRecordDao.insert(record)
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
DebugHelper.log(TAG, "Registering $ip%$downstream")
|
DebugHelper.log(TAG, "Registering $ip%$downstream")
|
||||||
check(records.putIfAbsentCompat(Pair(ip, downstream), record) == null)
|
check(records.putIfAbsent(Pair(ip, downstream), record) == null)
|
||||||
scheduleUpdateLocked()
|
scheduleUpdateLocked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ import be.mygod.vpnhotspot.AlertDialogFragment
|
|||||||
import be.mygod.vpnhotspot.App.Companion.app
|
import be.mygod.vpnhotspot.App.Companion.app
|
||||||
import be.mygod.vpnhotspot.R
|
import be.mygod.vpnhotspot.R
|
||||||
import be.mygod.vpnhotspot.RepeaterService
|
import be.mygod.vpnhotspot.RepeaterService
|
||||||
|
import be.mygod.vpnhotspot.databinding.DialogWifiApBinding
|
||||||
import be.mygod.vpnhotspot.util.QRCodeDialog
|
import be.mygod.vpnhotspot.util.QRCodeDialog
|
||||||
import be.mygod.vpnhotspot.util.toByteArray
|
import be.mygod.vpnhotspot.util.toByteArray
|
||||||
import be.mygod.vpnhotspot.util.toParcelable
|
import be.mygod.vpnhotspot.util.toParcelable
|
||||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.dialog_wifi_ap.view.*
|
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,7 +72,7 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var dialogView: View
|
private lateinit var dialogView: DialogWifiApBinding
|
||||||
private lateinit var bandOptions: MutableList<BandOption>
|
private lateinit var bandOptions: MutableList<BandOption>
|
||||||
private var started = false
|
private var started = false
|
||||||
private val selectedSecurity get() =
|
private val selectedSecurity get() =
|
||||||
@@ -92,14 +92,14 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
|
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
|
||||||
val activity = requireActivity()
|
val activity = requireActivity()
|
||||||
@SuppressLint("InflateParams")
|
@SuppressLint("InflateParams")
|
||||||
dialogView = activity.layoutInflater.inflate(R.layout.dialog_wifi_ap, null)
|
dialogView = DialogWifiApBinding.inflate(activity.layoutInflater)
|
||||||
setView(dialogView)
|
setView(dialogView.root)
|
||||||
if (!arg.readOnly) setPositiveButton(R.string.wifi_save, listener)
|
if (!arg.readOnly) setPositiveButton(R.string.wifi_save, listener)
|
||||||
setNegativeButton(R.string.donations__button_close, null)
|
setNegativeButton(R.string.donations__button_close, null)
|
||||||
dialogView.toolbar.inflateMenu(R.menu.toolbar_configuration)
|
dialogView.toolbar.inflateMenu(R.menu.toolbar_configuration)
|
||||||
dialogView.toolbar.setOnMenuItemClickListener(this@WifiApDialogFragment)
|
dialogView.toolbar.setOnMenuItemClickListener(this@WifiApDialogFragment)
|
||||||
if (!arg.readOnly) dialogView.ssid.addTextChangedListener(this@WifiApDialogFragment)
|
if (!arg.readOnly) dialogView.ssid.addTextChangedListener(this@WifiApDialogFragment)
|
||||||
if (arg.p2pMode) dialogView.security_wrapper.isGone = true else dialogView.security.apply {
|
if (arg.p2pMode) dialogView.securityWrapper.isGone = true else dialogView.security.apply {
|
||||||
adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, 0,
|
adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, 0,
|
||||||
WifiConfiguration.KeyMgmt.strings).apply {
|
WifiConfiguration.KeyMgmt.strings).apply {
|
||||||
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||||
@@ -107,7 +107,7 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) = error("Must select something")
|
override fun onNothingSelected(parent: AdapterView<*>?) = error("Must select something")
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||||
dialogView.password_wrapper.isGone = position == WifiConfiguration.KeyMgmt.NONE
|
dialogView.passwordWrapper.isGone = position == WifiConfiguration.KeyMgmt.NONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,7 +133,7 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
if (Build.VERSION.SDK_INT < 23) {
|
if (Build.VERSION.SDK_INT < 23) {
|
||||||
setSelection(bandOptions.indexOfFirst { it.apChannel == RepeaterService.operatingChannel })
|
setSelection(bandOptions.indexOfFirst { it.apChannel == RepeaterService.operatingChannel })
|
||||||
}
|
}
|
||||||
} else dialogView.band_wrapper.isGone = true
|
} else dialogView.bandWrapper.isGone = true
|
||||||
populateFromConfiguration(arg.configuration)
|
populateFromConfiguration(arg.configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
|
|||||||
WifiConfiguration.KeyMgmt.WPA_PSK, WPA2_PSK -> dialogView.password.length() >= 8
|
WifiConfiguration.KeyMgmt.WPA_PSK, WPA2_PSK -> dialogView.password.length() >= 8
|
||||||
else -> true // do not try to validate
|
else -> true // do not try to validate
|
||||||
}
|
}
|
||||||
dialogView.password_wrapper.error = if (passwordValid) null else {
|
dialogView.passwordWrapper.error = if (passwordValid) null else {
|
||||||
requireContext().getString(R.string.credentials_password_too_short)
|
requireContext().getString(R.string.credentials_password_too_short)
|
||||||
}
|
}
|
||||||
(dialog as? AlertDialog)?.getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled = ssidValid && passwordValid
|
(dialog as? AlertDialog)?.getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled = ssidValid && passwordValid
|
||||||
|
|||||||
@@ -123,15 +123,3 @@ var MenuItem.isNotGone: Boolean
|
|||||||
isVisible = value
|
isVisible = value
|
||||||
isEnabled = value
|
isEnabled = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <K, V> MutableMap<K, V>.computeIfAbsentCompat(key: K, value: () -> V) = if (Build.VERSION.SDK_INT >= 24)
|
|
||||||
computeIfAbsent(key) { value() } else this[key] ?: value().also { put(key, it) }
|
|
||||||
fun <K, V> MutableMap<K, V>.putIfAbsentCompat(key: K, value: V) = if (Build.VERSION.SDK_INT >= 24)
|
|
||||||
putIfAbsent(key, value) else this[key] ?: put(key, value)
|
|
||||||
fun <K, V> MutableMap<K, V>.removeCompat(key: K, value: V) = if (Build.VERSION.SDK_INT >= 24) remove(key, value) else {
|
|
||||||
val curValue = get(key)
|
|
||||||
if (curValue === value && (curValue != null || containsKey(key))) {
|
|
||||||
remove(key)
|
|
||||||
true
|
|
||||||
} else false
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
@@ -46,5 +44,3 @@
|
|||||||
tools:ignore="PxUsage"/>
|
tools:ignore="PxUsage"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</layout>
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
tools:viewBindingIgnore="true">
|
||||||
<androidx.emoji.widget.EmojiEditText
|
<androidx.emoji.widget.EmojiEditText
|
||||||
android:id="@android:id/edit"
|
android:id="@android:id/edit"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
tools:viewBindingIgnore="true">
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@android:id/edit"
|
android:id="@android:id/edit"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/swipeRefresher"
|
android:id="@+id/swipeRefresher"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
@@ -16,4 +14,3 @@
|
|||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
tools:listitem="@layout/listitem_client"/>
|
tools:listitem="@layout/listitem_client"/>
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
</layout>
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Button xmlns:android="http://schemas.android.com/apk/res/android"
|
<Button
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/donations__more_donate_button"
|
android:id="@+id/donations__more_donate_button"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/settings_misc_donate_more"/>
|
android:text="@string/settings_misc_donate_more"
|
||||||
|
tools:viewBindingIgnore="true"/>
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/interfaces"
|
android:id="@+id/interfaces"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
tools:listitem="@layout/listitem_interface"/>
|
tools:listitem="@layout/listitem_interface"/>
|
||||||
</layout>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user