Introduce and switch to FragmentViewBindingDelegate (#797)

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Harsh Shandilya
2020-05-27 00:14:10 +05:30
committed by GitHub
parent eb936e1f36
commit 6ee19f79e7
10 changed files with 89 additions and 41 deletions

View File

@@ -80,6 +80,7 @@ dependencies {
implementation deps.androidx.core_ktx
implementation deps.androidx.documentfile
implementation deps.androidx.fragment_ktx
implementation deps.androidx.lifecycle_common
implementation deps.androidx.lifecycle_livedata_ktx
implementation deps.androidx.lifecycle_viewmodel_ktx
implementation deps.androidx.local_broadcast_manager

View File

@@ -33,6 +33,7 @@ import com.zeapo.pwdstore.ui.adapters.PasswordItemRecyclerAdapter
import com.zeapo.pwdstore.ui.dialogs.ItemCreationBottomSheet
import com.zeapo.pwdstore.utils.PasswordItem
import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.viewBinding
import me.zhanghai.android.fastscroll.FastScrollerBuilder
import java.io.File
import java.util.Stack
@@ -44,10 +45,9 @@ class PasswordFragment : Fragment() {
private var recyclerViewStateToRestore: Parcelable? = null
private var actionMode: ActionMode? = null
private var _binding: PasswordRecyclerViewBinding? = null
private val model: SearchableRepositoryViewModel by activityViewModels()
private val binding get() = _binding!!
private val binding by viewBinding(PasswordRecyclerViewBinding::bind)
private fun requireStore() = requireActivity() as PasswordStore
@@ -56,7 +56,6 @@ class PasswordFragment : Fragment() {
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = PasswordRecyclerViewBinding.inflate(inflater, container, false)
settings = PreferenceManager.getDefaultSharedPreferences(requireContext())
initializePasswordList()
binding.fab.setOnClickListener {
@@ -149,11 +148,6 @@ class PasswordFragment : Fragment() {
}
}
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
private val actionModeCallback = object : ActionMode.Callback {
// Called when the action mode is created; startActionMode() was called
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {

View File

@@ -10,25 +10,19 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_to_clone_or_not.clone_from_server_button
import kotlinx.android.synthetic.main.fragment_to_clone_or_not.local_directory_button
import kotlinx.android.synthetic.main.fragment_to_clone_or_not.settings_button
import com.zeapo.pwdstore.databinding.FragmentToCloneOrNotBinding
import com.zeapo.pwdstore.utils.viewBinding
class ToCloneOrNot : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_to_clone_or_not, container, false)
}
private val binding by viewBinding(FragmentToCloneOrNotBinding::bind)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = binding.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
settings_button.setOnClickListener { startActivity(Intent(requireContext(), UserPreference::class.java)) }
local_directory_button.setOnClickListener { (requireActivity() as PasswordStore).initRepository(PasswordStore.NEW_REPO_BUTTON) }
clone_from_server_button.setOnClickListener { (requireActivity() as PasswordStore).initRepository(PasswordStore.CLONE_REPO_BUTTON) }
binding.settingsButton.setOnClickListener { startActivity(Intent(requireContext(), UserPreference::class.java)) }
binding.localDirectoryButton.setOnClickListener { (requireActivity() as PasswordStore).initRepository(PasswordStore.NEW_REPO_BUTTON) }
binding.cloneFromServerButton.setOnClickListener { (requireActivity() as PasswordStore).initRepository(PasswordStore.CLONE_REPO_BUTTON) }
}
}

View File

@@ -36,6 +36,7 @@ import com.zeapo.pwdstore.autofill.oreo.DirectoryStructure
import com.zeapo.pwdstore.autofill.oreo.FormOrigin
import com.zeapo.pwdstore.databinding.ActivityOreoAutofillFilterBinding
import com.zeapo.pwdstore.utils.PasswordItem
import com.zeapo.pwdstore.utils.viewBinding
@TargetApi(Build.VERSION_CODES.O)
class AutofillFilterView : AppCompatActivity() {
@@ -72,7 +73,7 @@ class AutofillFilterView : AppCompatActivity() {
private lateinit var formOrigin: FormOrigin
private lateinit var directoryStructure: DirectoryStructure
private lateinit var binding: ActivityOreoAutofillFilterBinding
private val binding by viewBinding(ActivityOreoAutofillFilterBinding::inflate)
private val model: SearchableRepositoryViewModel by viewModels {
ViewModelProvider.AndroidViewModelFactory(application)
@@ -80,7 +81,6 @@ class AutofillFilterView : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityOreoAutofillFilterBinding.inflate(layoutInflater)
setContentView(binding.root)
setFinishOnTouchOutside(true)

View File

@@ -22,6 +22,7 @@ import com.zeapo.pwdstore.autofill.oreo.AutofillPublisherChangedException
import com.zeapo.pwdstore.autofill.oreo.FormOrigin
import com.zeapo.pwdstore.autofill.oreo.computeCertificatesHash
import com.zeapo.pwdstore.databinding.ActivityOreoAutofillPublisherChangedBinding
import com.zeapo.pwdstore.utils.viewBinding
@TargetApi(Build.VERSION_CODES.O)
class AutofillPublisherChangedActivity : AppCompatActivity() {
@@ -45,11 +46,10 @@ class AutofillPublisherChangedActivity : AppCompatActivity() {
}
private lateinit var appPackage: String
private lateinit var binding: ActivityOreoAutofillPublisherChangedBinding
private val binding by viewBinding(ActivityOreoAutofillPublisherChangedBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityOreoAutofillPublisherChangedBinding.inflate(layoutInflater)
setContentView(binding.root)
setFinishOnTouchOutside(true)

View File

@@ -14,15 +14,15 @@ import com.google.android.material.snackbar.Snackbar
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.databinding.ActivityGitConfigBinding
import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.viewBinding
import org.eclipse.jgit.lib.Constants
class GitConfigActivity : BaseGitActivity() {
private lateinit var binding: ActivityGitConfigBinding
private val binding by viewBinding(ActivityGitConfigBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityGitConfigBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.setDisplayHomeAsUpEnabled(true)

View File

@@ -17,6 +17,7 @@ import com.zeapo.pwdstore.databinding.ActivityGitCloneBinding
import com.zeapo.pwdstore.git.config.ConnectionMode
import com.zeapo.pwdstore.git.config.Protocol
import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.viewBinding
import java.io.IOException
/**
@@ -25,11 +26,10 @@ import java.io.IOException
*/
class GitServerConfigActivity : BaseGitActivity() {
private lateinit var binding: ActivityGitCloneBinding
private val binding by viewBinding(ActivityGitCloneBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityGitCloneBinding.inflate(layoutInflater)
val isClone = intent?.extras?.getInt(REQUEST_ARG_OP) ?: -1 == REQUEST_CLONE
if (isClone) {
binding.saveButton.text = getString(R.string.clone_button)

View File

@@ -19,6 +19,7 @@ import com.jcraft.jsch.JSch
import com.jcraft.jsch.KeyPair
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.databinding.FragmentSshKeygenBinding
import com.zeapo.pwdstore.utils.viewBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -28,17 +29,9 @@ import java.io.FileOutputStream
class SshKeyGenFragment : Fragment() {
private var keyLength = 4096
private var _binding: FragmentSshKeygenBinding? = null
private val binding get() = _binding!!
private val binding by viewBinding(FragmentSshKeygenBinding::bind)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentSshKeygenBinding.inflate(inflater, container, false)
return binding.root
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = binding.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -60,7 +53,6 @@ class SshKeyGenFragment : Fragment() {
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
// Invoked when 'Generate' button of SshKeyGenFragment clicked. Generates a

View File

@@ -0,0 +1,66 @@
/*
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
*/
package com.zeapo.pwdstore.utils
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.observe
import androidx.viewbinding.ViewBinding
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
/**
* Imported from https://medium.com/@Zhuinden/simple-one-liner-viewbinding-in-fragments-and-activities-with-kotlin-961430c6c07c
*/
class FragmentViewBindingDelegate<T : ViewBinding>(
val fragment: Fragment,
val viewBindingFactory: (View) -> T
) : ReadOnlyProperty<Fragment, T> {
private var binding: T? = null
init {
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner ->
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onDestroy(owner: LifecycleOwner) {
binding = null
}
})
}
}
})
}
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
val binding = binding
if (binding != null) {
return binding
}
val lifecycle = fragment.viewLifecycleOwner.lifecycle
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.")
}
return viewBindingFactory(thisRef.requireView()).also { this.binding = it }
}
}
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
FragmentViewBindingDelegate(this, viewBindingFactory)
inline fun <T : ViewBinding> AppCompatActivity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T) =
lazy(LazyThreadSafetyMode.NONE) {
bindingInflater.invoke(layoutInflater)
}

View File

@@ -32,6 +32,7 @@ ext.deps = [
core_ktx: 'androidx.core:core-ktx:1.3.0-rc01',
documentfile: 'androidx.documentfile:documentfile:1.0.1',
fragment_ktx: 'androidx.fragment:fragment-ktx:1.3.0-alpha03',
lifecycle_common: 'androidx.lifecycle:lifecycle-common-java8:2.3.0-alpha01',
lifecycle_livedata_ktx: 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0-alpha01',
lifecycle_viewmodel_ktx: 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-alpha01',
local_broadcast_manager: 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0-alpha01',