Migrate from kotlin-android-extensions (synthetics) to data binding

This commit is contained in:
William Brawner 2020-12-08 16:36:42 -07:00
parent 3164bee01b
commit 53cd8c7312
18 changed files with 213 additions and 148 deletions

View file

@ -1,6 +1,22 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="java.util" alias="false" withSubpackages="false" />
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
<package name="io.ktor" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">

6
.idea/compiler.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" />
</component>
</project>

View file

@ -15,6 +15,7 @@
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings>
</option>
</component>

25
.idea/jarRepositories.xml Normal file
View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinScriptingSettings">
<scriptDefinition className="org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate" definitionName="KotlinBuildScript">
<order>2147483647</order>
<autoReloadConfigurations>true</autoReloadConfigurations>
</scriptDefinition>
</component>
</project>

View file

@ -5,7 +5,7 @@
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
</configurations>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View file

@ -1,6 +1,5 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
def keystoreProperties = new Properties()
try {
@ -47,6 +46,9 @@ android {
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
viewBinding true
}
}
dependencies {
@ -54,17 +56,17 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.2.0-alpha06'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.google.android.material:material:1.3.0-alpha04'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.security:security-crypto:1.0.0-rc01'
implementation 'androidx.preference:preference:1.1.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
def navigation_version = '2.2.2'
implementation 'androidx.preference:preference-ktx:1.1.1'
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
def navigation_version = '2.3.2'
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
def lifecycle_version = '2.2.0'

View file

@ -10,45 +10,47 @@ import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.fragment_add_pi_hole.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import com.wbrawner.pihelper.databinding.FragmentAddPiHoleBinding
import org.koin.android.ext.android.inject
import kotlin.coroutines.CoroutineContext
class AddPiHoleFragment : Fragment(), CoroutineScope {
class AddPiHoleFragment : Fragment() {
override val coroutineContext: CoroutineContext = Dispatchers.Main
private val viewModel: AddPiHelperViewModel by inject()
private var _binding: FragmentAddPiHoleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_add_pi_hole, container, false)
): View {
_binding = FragmentAddPiHoleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val navController = findNavController()
scanNetworkButton.setOnClickListener {
binding.scanNetworkButton.setOnClickListener {
navController.navigate(
R.id.action_addPiHoleFragment_to_scanNetworkFragment,
null,
null,
FragmentNavigatorExtras(piHelperLogo to "piHelperLogo")
FragmentNavigatorExtras(binding.piHelperLogo to "piHelperLogo")
)
}
ipAddress.setOnEditorActionListener { _, _, _ ->
connectButton.performClick()
binding.ipAddress.setOnEditorActionListener { _, _, _ ->
binding.connectButton.performClick()
}
connectButton.setSuspendingOnClickListener(this) {
binding.connectButton.setSuspendingOnClickListener(lifecycleScope) {
showProgress(true)
if (viewModel.connectToIpAddress(ipAddress.text.toString())) {
if (viewModel.connectToIpAddress(binding.ipAddress.text.toString())) {
navController.navigate(
R.id.action_addPiHoleFragment_to_retrieveApiKeyFragment,
null,
null,
FragmentNavigatorExtras(piHelperLogo to "piHelperLogo")
FragmentNavigatorExtras(binding.piHelperLogo to "piHelperLogo")
)
} else {
AlertDialog.Builder(view.context)
@ -63,7 +65,7 @@ class AddPiHoleFragment : Fragment(), CoroutineScope {
private fun showProgress(show: Boolean) {
if (show) {
piHelperLogo.startAnimation(
binding.piHelperLogo.startAnimation(
RotateAnimation(
0f,
360f,
@ -81,13 +83,13 @@ class AddPiHoleFragment : Fragment(), CoroutineScope {
}
)
} else {
piHelperLogo.clearAnimation()
binding.piHelperLogo.clearAnimation()
}
connectionForm.visibility = if (show) View.GONE else View.VISIBLE
binding.connectionForm.visibility = if (show) View.GONE else View.VISIBLE
}
override fun onDestroyView() {
cancel()
super.onDestroyView()
_binding = null
}
}

View file

@ -15,16 +15,13 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.wbrawner.pihelper.MainActivity.Companion.ACTION_FORGET_PIHOLE
import kotlinx.android.synthetic.main.fragment_info.*
import kotlinx.android.synthetic.main.fragment_main.toolbar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import com.wbrawner.pihelper.databinding.FragmentInfoBinding
import org.koin.android.ext.android.inject
import kotlin.coroutines.CoroutineContext
class InfoFragment : Fragment(), CoroutineScope {
override val coroutineContext: CoroutineContext = Dispatchers.Main
class InfoFragment : Fragment() {
private val viewModel: AddPiHelperViewModel by inject()
private var _binding: FragmentInfoBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -34,20 +31,23 @@ class InfoFragment : Fragment(), CoroutineScope {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_info, container, false)
): View {
_binding = FragmentInfoBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
(activity as? AppCompatActivity)?.setSupportActionBar(toolbar)
(activity as? AppCompatActivity)?.setSupportActionBar(binding.toolbar)
(activity as? AppCompatActivity)?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
(activity as? AppCompatActivity)?.supportActionBar?.setTitle(R.string.action_settings)
val html = getString(R.string.content_info)
@Suppress("DEPRECATION")
infoContent?.text = if (Build.VERSION.SDK_INT < 24)
binding.infoContent.text = if (Build.VERSION.SDK_INT < 24)
Html.fromHtml(html)
else
Html.fromHtml(html, 0)
infoContent.movementMethod = LinkMovementMethod.getInstance()
forgetPiHoleButton?.setOnClickListener {
binding.infoContent.movementMethod = LinkMovementMethod.getInstance()
binding.forgetPiHoleButton.setOnClickListener {
AlertDialog.Builder(view.context)
.setTitle(R.string.confirm_forget_pihole)
.setMessage(R.string.warning_cannot_be_undone)
@ -77,4 +77,9 @@ class InfoFragment : Fragment(), CoroutineScope {
}
return super.onOptionsItemSelected(item)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View file

@ -9,6 +9,7 @@ import androidx.navigation.findNavController
import com.wbrawner.pihelper.MainFragment.Companion.ACTION_DISABLE
import com.wbrawner.pihelper.MainFragment.Companion.ACTION_ENABLE
import com.wbrawner.pihelper.MainFragment.Companion.EXTRA_DURATION
import com.wbrawner.pihelper.databinding.ActivityMainBinding
import org.koin.android.ext.android.inject
class MainActivity : AppCompatActivity() {
@ -19,10 +20,9 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
window.setBackgroundDrawable(ColorDrawable(getColor(R.color.colorSurface)))
val analyticsBundle = Bundle()
analyticsBundle.putString("intent_action", intent.action)
val args = when (intent.action) {
ACTION_ENABLE -> {
if (addPiHoleViewModel.apiKey == null) {

View file

@ -11,32 +11,28 @@ import android.view.*
import android.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
import android.widget.EditText
import android.widget.RadioGroup
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat.getColor
import androidx.core.text.set
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.wbrawner.pihelper.databinding.DialogDisableCustomTimeBinding
import com.wbrawner.pihelper.databinding.FragmentMainBinding
import com.wbrawner.piholeclient.Status
import kotlinx.android.synthetic.main.fragment_main.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import kotlin.coroutines.CoroutineContext
class MainFragment : Fragment(), CoroutineScope {
override val coroutineContext: CoroutineContext = Dispatchers.Main
class MainFragment : Fragment() {
private val viewModel: PiHelperViewModel by inject()
private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
launch {
lifecycleScope.launch {
if (arguments?.getBoolean(ACTION_ENABLE) == true) {
viewModel.enablePiHole()
} else if (arguments?.getBoolean(ACTION_DISABLE) == true) {
@ -44,52 +40,53 @@ class MainFragment : Fragment(), CoroutineScope {
}
viewModel.monitorSummary()
}
viewModel.status.observe(this, Observer {
viewModel.status.observe(this, {
showProgress(false)
val (statusColor, statusText) = when (it) {
Status.DISABLED -> {
enableButton?.visibility = View.VISIBLE
disableButtons?.visibility = View.GONE
binding.enableButton.visibility = View.VISIBLE
binding.disableButtons.visibility = View.GONE
Pair(R.color.colorDisabled, R.string.status_disabled)
}
Status.ENABLED -> {
enableButton?.visibility = View.GONE
disableButtons?.visibility = View.VISIBLE
binding.enableButton.visibility = View.GONE
binding.disableButtons.visibility = View.VISIBLE
Pair(R.color.colorEnabled, R.string.status_enabled)
}
else -> {
enableButton?.visibility = View.GONE
disableButtons?.visibility = View.GONE
binding.enableButton.visibility = View.GONE
binding.disableButtons.visibility = View.GONE
Pair(R.color.colorUnknown, R.string.status_unknown)
}
}
status?.let { textView ->
val status = getString(statusText)
val statusLabel = getString(R.string.label_status, status)
val start = statusLabel.indexOf(status)
val end = start + status.length
val statusSpan = SpannableString(statusLabel)
statusSpan[start, end] = StyleSpan(Typeface.BOLD)
statusSpan[start, end] =
ForegroundColorSpan(getColor(textView.context, statusColor))
textView.text = statusSpan
}
val status = getString(statusText)
val statusLabel = getString(R.string.label_status, status)
val start = statusLabel.indexOf(status)
val end = start + status.length
val statusSpan = SpannableString(statusLabel)
statusSpan[start, end] = StyleSpan(Typeface.BOLD)
statusSpan[start, end] =
ForegroundColorSpan(getColor(binding.status.context, statusColor))
binding.status.text = statusSpan
})
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_main, container, false)
): View {
_binding = FragmentMainBinding.inflate(inflater, container, false)
return binding.root
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.main, menu)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
(activity as? AppCompatActivity)?.setSupportActionBar(toolbar)
(activity as? AppCompatActivity)?.setSupportActionBar(binding.toolbar)
showProgress(true)
enableButton?.setSuspendingOnClickListener(this) {
binding.enableButton.setSuspendingOnClickListener(lifecycleScope) {
showProgress(true)
try {
viewModel.enablePiHole()
@ -97,7 +94,7 @@ class MainFragment : Fragment(), CoroutineScope {
Log.e("Pi-helper", "Failed to enable Pi-Hole", ignored)
}
}
disable10SecondsButton?.setSuspendingOnClickListener(this) {
binding.disable10SecondsButton.setSuspendingOnClickListener(lifecycleScope) {
showProgress(true)
try {
viewModel.disablePiHole(10)
@ -105,7 +102,7 @@ class MainFragment : Fragment(), CoroutineScope {
Log.e("Pi-helper", "Failed to disable Pi-Hole", ignored)
}
}
disable30SecondsButton?.setSuspendingOnClickListener(this) {
binding.disable30SecondsButton.setSuspendingOnClickListener(lifecycleScope) {
showProgress(true)
try {
viewModel.disablePiHole(30)
@ -113,7 +110,7 @@ class MainFragment : Fragment(), CoroutineScope {
Log.e("Pi-helper", "Failed to disable Pi-Hole", ignored)
}
}
disable5MinutesButton?.setSuspendingOnClickListener(this) {
binding.disable5MinutesButton.setSuspendingOnClickListener(lifecycleScope) {
showProgress(true)
try {
viewModel.disablePiHole(300)
@ -121,40 +118,43 @@ class MainFragment : Fragment(), CoroutineScope {
Log.e("Pi-helper", "Failed to disable Pi-Hole", ignored)
}
}
disableCustomTimeButton?.setOnClickListener {
val dialogView = LayoutInflater.from(it.context)
.inflate(R.layout.dialog_disable_custom_time, view as ViewGroup, false)
binding.disableCustomTimeButton.setOnClickListener {
val dialogView = DialogDisableCustomTimeBinding.inflate(
LayoutInflater.from(it.context),
view as ViewGroup,
false
)
AlertDialog.Builder(it.context)
.setTitle(R.string.action_disable_custom)
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.setPositiveButton(R.string.action_disable, null)
.setView(dialogView)
.setView(dialogView.root)
.create()
.apply {
setOnShowListener {
getButton(AlertDialog.BUTTON_POSITIVE).setSuspendingOnClickListener(this@MainFragment) {
getButton(AlertDialog.BUTTON_POSITIVE).setSuspendingOnClickListener(
lifecycleScope
) {
try {
val rawTime = dialogView.findViewById<EditText>(R.id.time)
val rawTime = dialogView.time
.text
.toString()
.toLong()
val checkedId =
dialogView.findViewById<RadioGroup>(R.id.timeUnit)
.checkedRadioButtonId
dialogView.timeUnit.checkedRadioButtonId
val computedTime = if (checkedId == R.id.seconds) rawTime
else rawTime * 60
viewModel.disablePiHole(computedTime)
dismiss()
} catch (e: Exception) {
dialogView.findViewById<EditText>(R.id.time)
.error = "Failed to disable Pi-hole"
dialogView.time.error = "Failed to disable Pi-hole"
}
}
}
}
.show()
}
disablePermanentlyButton?.setSuspendingOnClickListener(this) {
binding.disablePermanentlyButton.setSuspendingOnClickListener(lifecycleScope) {
showProgress(true)
try {
viewModel.disablePiHole()
@ -173,8 +173,8 @@ class MainFragment : Fragment(), CoroutineScope {
}
private fun showProgress(show: Boolean) {
progressBar?.visibility = if (show) {
progressBar?.startAnimation(RotateAnimation(
binding.progressBar.visibility = if (show) {
binding.progressBar.startAnimation(RotateAnimation(
0f,
360f,
Animation.RELATIVE_TO_SELF,
@ -190,10 +190,10 @@ class MainFragment : Fragment(), CoroutineScope {
})
View.VISIBLE
} else {
progressBar?.clearAnimation()
binding.progressBar.clearAnimation()
View.GONE
}
statusContent?.visibility = if (show) {
binding.statusContent.visibility = if (show) {
View.GONE
} else {
View.VISIBLE
@ -201,7 +201,7 @@ class MainFragment : Fragment(), CoroutineScope {
}
override fun onDestroyView() {
coroutineContext[Job]?.cancel()
_binding = null
super.onDestroyView()
}

View file

@ -11,17 +11,16 @@ import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.transition.TransitionInflater
import kotlinx.android.synthetic.main.fragment_retrieve_api_key.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import com.wbrawner.pihelper.databinding.FragmentRetrieveApiKeyBinding
import org.koin.android.ext.android.inject
import kotlin.coroutines.CoroutineContext
class RetrieveApiKeyFragment : Fragment(), CoroutineScope {
override val coroutineContext: CoroutineContext = Dispatchers.Main
class RetrieveApiKeyFragment : Fragment() {
private val viewModel: AddPiHelperViewModel by inject()
private var _binding: FragmentRetrieveApiKeyBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -36,32 +35,35 @@ class RetrieveApiKeyFragment : Fragment(), CoroutineScope {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_retrieve_api_key, container, false)
): View {
_binding = FragmentRetrieveApiKeyBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
password.setOnEditorActionListener { _, _, _ ->
connectWithPasswordButton.performClick()
binding.password.setOnEditorActionListener { _, _, _ ->
binding.connectWithPasswordButton.performClick()
}
connectWithPasswordButton.setSuspendingOnClickListener(this) {
binding.connectWithPasswordButton.setSuspendingOnClickListener(lifecycleScope) {
showProgress(true)
try {
viewModel.authenticateWithPassword(password.text.toString())
viewModel.authenticateWithPassword(binding.password.text.toString())
} catch (ignored: Exception) {
Log.e("Pi-helper", "Failed to authenticate with password", ignored)
password.error = "Failed to authenticate with given password. Please verify " +
binding.password.error = "Failed to authenticate with given password. Please verify " +
"you've entered it correctly and try again."
showProgress(false)
}
}
apiKey.setOnEditorActionListener { _, _, _ ->
connectWithApiKeyButton.performClick()
binding.apiKey.setOnEditorActionListener { _, _, _ ->
binding.connectWithApiKeyButton.performClick()
}
connectWithApiKeyButton.setSuspendingOnClickListener(this) {
binding.connectWithApiKeyButton.setSuspendingOnClickListener(lifecycleScope) {
showProgress(true)
try {
viewModel.authenticateWithApiKey(apiKey.text.toString())
viewModel.authenticateWithApiKey(binding.apiKey.text.toString())
} catch (ignored: Exception) {
apiKey.error = "Failed to authenticate with given API key. Please verify " +
binding.apiKey.error = "Failed to authenticate with given API key. Please verify " +
"you've entered it correctly and try again."
showProgress(false)
}
@ -70,7 +72,7 @@ class RetrieveApiKeyFragment : Fragment(), CoroutineScope {
private fun showProgress(show: Boolean) {
if (show) {
piHelperLogo.startAnimation(
binding.piHelperLogo.startAnimation(
RotateAnimation(
0f,
360f,
@ -88,13 +90,13 @@ class RetrieveApiKeyFragment : Fragment(), CoroutineScope {
}
)
} else {
piHelperLogo.clearAnimation()
binding.piHelperLogo.clearAnimation()
}
authenticationForm.visibility = if (show) View.GONE else View.VISIBLE
binding.authenticationForm.visibility = if (show) View.GONE else View.VISIBLE
}
override fun onDestroyView() {
cancel()
_binding = null
super.onDestroyView()
}
}

View file

@ -16,23 +16,23 @@ import android.view.animation.RotateAnimation
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import androidx.transition.Transition
import androidx.transition.TransitionInflater
import kotlinx.android.synthetic.main.fragment_scan_network.*
import kotlinx.coroutines.CoroutineScope
import com.wbrawner.pihelper.databinding.FragmentScanNetworkBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import java.net.Inet4Address
import kotlin.coroutines.CoroutineContext
class ScanNetworkFragment : Fragment(), CoroutineScope {
class ScanNetworkFragment : Fragment() {
override val coroutineContext: CoroutineContext = Dispatchers.Main
private val viewModel: AddPiHelperViewModel by inject()
private var _binding: FragmentScanNetworkBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -60,12 +60,15 @@ class ScanNetworkFragment : Fragment(), CoroutineScope {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_scan_network, container, false)
): View {
_binding = FragmentScanNetworkBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.scanningIp.observe(viewLifecycleOwner, Observer {
ipAddress?.text = it
binding.ipAddress.text = it
})
viewModel.piHoleIpAddress.observe(viewLifecycleOwner, Observer { ipAddress ->
if (ipAddress == null) {
@ -78,7 +81,7 @@ class ScanNetworkFragment : Fragment(), CoroutineScope {
.show()
return@Observer
}
piHelperLogo?.animation?.let {
binding.piHelperLogo.animation?.let {
it.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(animation: Animation?) {
}
@ -93,7 +96,7 @@ class ScanNetworkFragment : Fragment(), CoroutineScope {
it.repeatCount = 0
} ?: navigateToApiKeyScreen()
})
launch(Dispatchers.IO) {
lifecycleScope.launch(Dispatchers.IO) {
if (BuildConfig.DEBUG && Build.MODEL == "Android SDK built for x86") {
// For emulators, just begin scanning the host machine directly
viewModel.beginScanning("10.0.2.2")
@ -120,9 +123,9 @@ class ScanNetworkFragment : Fragment(), CoroutineScope {
}
}
}
launch {
lifecycleScope.launch {
delay(500)
if (piHelperLogo?.animation == null) {
if (binding.piHelperLogo.animation == null) {
animatePiHelperLogo()
}
}
@ -130,7 +133,7 @@ class ScanNetworkFragment : Fragment(), CoroutineScope {
private fun navigateToApiKeyScreen() {
val extras = FragmentNavigatorExtras(
piHelperLogo to "piHelperLogo"
binding.piHelperLogo to "piHelperLogo"
)
findNavController().navigate(
@ -142,13 +145,13 @@ class ScanNetworkFragment : Fragment(), CoroutineScope {
}
override fun onDestroyView() {
piHelperLogo.clearAnimation()
cancel()
binding.piHelperLogo.clearAnimation()
_binding = null
super.onDestroyView()
}
private fun animatePiHelperLogo() {
piHelperLogo?.startAnimation(
binding.piHelperLogo.startAnimation(
RotateAnimation(
0f,
360f,

View file

@ -1,17 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:background="@color/colorSurface">
<fragment
android:id="@+id/content_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</merge>
android:fitsSystemWindows="true"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"
tools:background="@color/colorSurface" />

View file

@ -23,6 +23,6 @@
</style>
<style name="AppTheme.Button.Red" parent="AppTheme.Button">
<item name="backgroundTint">@color/colorRedDark</item>
<item name="backgroundTint">@color/colorAccent</item>
</style>
</resources>

View file

@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.3.61'
ext.kotlin_version = '1.4.20'
ext.coroutines_version = '1.3.2'
ext.koin_version = '2.0.1'
ext.okhttp_version = '4.2.2'
@ -8,7 +8,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.2'
classpath 'com.android.tools.build:gradle:4.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

View file

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip

View file

@ -1,6 +1,5 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {