PreferenceDialog in PreferenceActivity - Target fragment must implement TargetFragment interface - kotlin

I'm trying to implement a DialogPreference in a Preference activity. I achieved it but it seems setTargetFragment is marked as deprecated.
Here is my old code:
override fun onDisplayPreferenceDialog(preference: Preference?) {
val clearStatsDialog = preference as? DialogPreferenceClearStats
if (clearStatsDialog != null) {
val dialogFragment = DialogPrefCompat.newInstance(clearStatsDialog.key)
dialogFragment.setTargetFragment(this, 0)
dialogFragment.positiveResult = {
Toast.makeText(activity, "yes", Toast.LENGTH_LONG).show()
}
dialogFragment.show(this.parentFragmentManager, null)
} else {
super.onDisplayPreferenceDialog(preference)
}
}
I wanted to replace it with setFragmentResultListener but I'm always getting "Target fragment must implement TargetFragment interface" exception.
Can someone help me?
Here is the complete code:
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.AttributeSet
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.DialogPreference
import androidx.preference.Preference
import androidx.preference.PreferenceDialogFragmentCompat
import androidx.preference.PreferenceFragmentCompat
import com.workout.intervaltimer.R
import com.workout.intervaltimer.util.WorkoutTimerUtil
class DialogPreferenceClearStats(context: Context, attrs: AttributeSet?) : DialogPreference(context, attrs), DialogPreference.TargetFragment {
override fun <T : Preference?> findPreference(key: CharSequence): T? {
TODO("Not yet implemented")
}
}
class DialogPrefCompat : PreferenceDialogFragmentCompat() {
lateinit var positiveResult: ()->Unit
override fun onDialogClosed(positiveResult: Boolean) {
if (positiveResult) {
positiveResult()
}
}
companion object {
fun newInstance(key: String): DialogPrefCompat {
val fragment = DialogPrefCompat()
val bundle = Bundle(1)
bundle.putString(PreferenceDialogFragmentCompat.ARG_KEY, key)
fragment.arguments = bundle
return fragment
}
}
}
class SettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.settings_activity)
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.replace(R.id.settings, SettingsFragment())
.commit()
}
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
this.parentFragmentManager.setFragmentResultListener("requestKey", viewLifecycleOwner) { requestKey, bundle ->
val dialogFragment = DialogPrefCompat.newInstance("clearStatsDialog.key")
dialogFragment.positiveResult = {
Toast.makeText(activity, "yes", Toast.LENGTH_LONG).show()
}
dialogFragment.show(this.parentFragmentManager, null)
}
}
override fun onDisplayPreferenceDialog(preference: Preference?) {
val clearStatsDialog = preference as? DialogPreferenceClearStats
if (clearStatsDialog != null) {
this.parentFragmentManager.setFragmentResult("requestKey", Bundle())
}
}
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
//It should be possible to launchs activities and websites from xml intent but I didn't achieve it.
when (preference!!.key) {
getString(R.string.force_dark_theme) -> {
WorkoutTimerUtil.setDayNightThemeForApp(requireActivity().applicationContext)
}
getString(R.string.third_party_software) -> {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(WEB_THIRD_PARTY_SOFTWARE)
startActivity(intent)
}
getString(R.string.terms_conditions) -> {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(WEB_TERMS)
startActivity(intent)
}
getString(R.string.privacy_policy) -> {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(WEB_PRIVACY_POLICY)
startActivity(intent)
}
}
return super.onPreferenceTreeClick(preference)
}
}
companion object {
const val WEB_THIRD_PARTY_SOFTWARE = "https://sites.google.com/view/that-third-party-software"
const val WEB_TERMS = "https://sites.google.com/view/that-terms-conditions"
const val WEB_PRIVACY_POLICY = "https://sites.google.com/view/that-privacy-policy"
}
}
Thanks in advance.

Doesn't look too promising: https://android-review.googlesource.com/c/platform/frameworks/support/+/1843122/8/preference/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java just added #SuppressWarnings("deprecation") over the relevant code. I think that until PreferenceDialogFragmentCompat is switched over to the new API we're stuck calling setTargetFragment :(

I think the such behavour is related to the next Android bug:
https://issuetracker.google.com/issues/181793702
It still not fixed for Preferences lib 1.2.0 (Nov 1, 2022).
setTargetFragment() must be called even if lint says it is deprecated.

Related

Room data doesn't appear in activity RecyclerView

I am simply trying to setup the ClickListener for changing the user name. Google's tutorials emphasize fragments, which for the moment feels like overkill.
The app crashes when I navigate to the ManageUsers activity. Based on what I've seen in other examples and the Android documentation, I thought I had the View Binding set up properly.
UserListAdapter.kt
package com.neillbarrett.debitsandcredits
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.neillbarrett.debitsandcredits.database.UsersTable
import com.neillbarrett.debitsandcredits.databinding.ActivityManageUsersBinding
class UserListAdapter(private val userSelect: (UsersTable?) -> Unit) :
ListAdapter<UsersTable, UserListAdapter.UserViewHolder>(UsersComparator()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserListAdapter.UserViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder.create(parent)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val current = getItem(position)
holder.bind(current, userSelect)
}
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val userView: TextView = itemView.findViewById(R.id.tv_UserName)
// var text: String? = null
fun bind(usersTable: UsersTable?, userSelect: (UsersTable?) -> Unit, text: String = usersTable?.userName.toString()) {
userView.text = text
itemView.setOnClickListener { View.OnClickListener {
/* if (View.) { }*/
val nameSelected = userSelect(usersTable)
//userSelect(usersTable)
//need to assign the result of the clicklistener to the editText
//binding.etEditName.setText(R.layout.activity_list_of_users.toString())
}}
}
companion object {
fun create(parent: ViewGroup) : UserViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder(view)
}
}
}
class UsersComparator : DiffUtil.ItemCallback<UsersTable>() {
override fun areItemsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
return oldItem.userName == newItem.userName
}
override fun areContentsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
return oldItem == newItem
}
}
}
ManageUsers.kt
package com.neillbarrett.debitsandcredits
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.activity.viewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.neillbarrett.debitsandcredits.database.CreditsAndDebitsApp
import com.neillbarrett.debitsandcredits.database.UsersTable
import com.neillbarrett.debitsandcredits.databinding.ActivityManageUsersBinding
class ManageUsers : AppCompatActivity() {
lateinit var binding: ActivityManageUsersBinding
lateinit var recyclerView: RecyclerView
lateinit var editTextAddUser: EditText
lateinit var editTextChangeUser: EditText
lateinit var newUser: String
var userSelect: ((UsersTable?) -> Unit) = {}
var position: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityManageUsersBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//setContentView(R.layout.activity_manage_users)
val userViewModel: UserViewModel by viewModels {
UserViewModelFactory((application as CreditsAndDebitsApp).repository)
}
recyclerView = findViewById(R.id.rec_view_userList)
editTextAddUser = findViewById(R.id.et_AddUser)
editTextChangeUser = findViewById(R.id.et_Edit_Name)
val adapter = UserListAdapter(userSelect)
binding.recViewUserList.adapter = adapter
binding.recViewUserList.layoutManager = LinearLayoutManager(this)
//recyclerView.adapter = adapter
//recyclerView.layoutManager = LinearLayoutManager(this)
userViewModel.allUsers.observe(this, Observer() {user ->
user?.let { adapter.submitList(it) }
})
val btnAddUser = findViewById<Button>(R.id.btn_AddUser)
binding.btnAddUser.setOnClickListener {
// btnAddUser.setOnClickListener {
if (TextUtils.isEmpty(editTextAddUser.text)) {
Toast.makeText(this, "User name cannot be empty", Toast.LENGTH_SHORT).show()
} else {
newUser = editTextAddUser.text.toString()
// Log.i("Add user button", "Username put into newUser")
userViewModel.insertUser(UsersTable(0, newUser))
// Toast.makeText(this, "Username added to table", Toast.LENGTH_SHORT).show()
// Log.i("Add user button", "Username added to table")
}
}
val btnChangeUser = findViewById<Button>(R.id.btn_ChangeUserName)
binding.btnChangeUserName.setOnClickListener {
// btnChangeUser.setOnClickListener {
Toast.makeText(this, "Selected position is ${recyclerView.getChildAdapterPosition(it)}", Toast.LENGTH_SHORT).show()
/* if (recyclerView.getChildAdapterPosition(it) == -1) {
Toast.makeText(this, "Select a name.", Toast.LENGTH_SHORT).show()
} else {
if (editTextChangeUser.text.toString() == recyclerView.adapter.toString()) {
Toast.makeText(this, "Name has not been changed.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Name would have been changed.", Toast.LENGTH_SHORT).show()
val rvItemRecId: Long
rvItemRecId = adapter.getItemId(position.toInt())
userViewModel.updateUser(UsersTable(rvItemRecId.toInt(), adapter.toString()))
}
}*/
}
}
}
UserViewModel.kt
package com.neillbarrett.debitsandcredits
import androidx.lifecycle.*
import com.neillbarrett.debitsandcredits.database.UsersTable
import kotlinx.coroutines.launch
import java.lang.IllegalArgumentException
class UserViewModel(private val repository: UserRepository) : ViewModel() {
// Using LiveData and caching what allWords returns has several benefits:
// - We can put an observer on the data (instead of polling for changes) and only update the
// the UI when the data actually changes.
// - Repository is completely separated from the UI through the ViewModel.
val allUsers: LiveData<List<UsersTable>> = repository.allUsers.asLiveData()
/**
* Launching a new coroutine to insert the data in a non-blocking way
*/
fun insertUser(user: UsersTable) = viewModelScope.launch {
repository.insertUser(user)
//repository.insertUser(usersTable = List<UsersTable>())
//repository.insertUser(UsersTable(0, userName = user.userName))
}
fun updateUser(user: UsersTable) = viewModelScope.launch {
repository.updateUser(user)
}
fun deleteUser(user: UsersTable) = viewModelScope.launch {
repository.deleteUser(user)
}
}
class UserViewModelFactory(private val repository: UserRepository) : ViewModelProvider.Factory{
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return UserViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
The LogCat shows this:
2022-11-25 11:43:59.427 8217-8217/com.neillbarrett.debitsandcredits E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.neillbarrett.debitsandcredits, PID: 8217
java.lang.NullPointerException: itemView.findViewById(R.id.tv_UserName) must not be null
at com.neillbarrett.debitsandcredits.UserListAdapter$UserViewHolder.<init>(UserListAdapter.kt:30)
at com.neillbarrett.debitsandcredits.UserListAdapter$UserViewHolder$Companion.create(UserListAdapter.kt:51)
at com.neillbarrett.debitsandcredits.UserListAdapter.onCreateViewHolder(UserListAdapter.kt:21)
at com.neillbarrett.debitsandcredits.UserListAdapter.onCreateViewHolder(UserListAdapter.kt:15)
at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:7078)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6235)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3540)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:26411)
at androidx.constraintlayout.widget.ConstraintLayout$Measurer.measure(ConstraintLayout.java:811)
at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.measure(BasicMeasure.java:466)
at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.measureChildren(BasicMeasure.java:134)
at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.solverMeasure(BasicMeasure.java:278)
at androidx.constraintlayout.core.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:120)
at androidx.constraintlayout.widget.ConstraintLayout.resolveSystem(ConstraintLayout.java:1594)
at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1708)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:145)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at androidx.appcompat.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:496)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:1050)
at android.view.View.measure(View.java:26411)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3635)
It's clear I'm missing something, but I have no idea what.

Kotlin: lateinit var needs to be initialized

I'm struggling with both a dilemma and an issue here. The dilemma is that I want to keep the ManagerUsers screen simple, but Google is pushing fragments. I can see a use for fragments for this screen later on, but for now, it seems like overkill. Should I use them anyway?
Second, if I do avoid fragments for now, I am struggling to finish setting up the ClickListener to simply change the name of the user in the ListView. Kotlin seems to have at least 6 different methods for doing most things, making it confusing to figure this out. Google's tutorials force you to use fragments, and others' tutorials don't really cover what I'm trying to do. When I navigate to the Manage Users activity, the app crashes. LogCat shows that userSelect needs to be initialized. I have no idea how to do that.
UserListAdapter.kt
package com.neillbarrett.debitsandcredits
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.neillbarrett.debitsandcredits.database.UsersTable
import com.neillbarrett.debitsandcredits.databinding.ActivityManageUsersBinding
class UserListAdapter(private val userSelect: (UsersTable?) -> Unit) :
ListAdapter<UsersTable, UserListAdapter.UserViewHolder>(UsersComparator()) {
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val current = getItem(position)
holder.bind(current, userSelect)
}
class UserViewHolder(private val binding: ActivityManageUsersBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(usersTable: UsersTable?, userSelect: (UsersTable?) -> Unit) {
binding.root.setOnClickListener( View.OnClickListener {
userSelect(usersTable)
binding.etEditName.setText(R.layout.activity_list_of_users.toString())
})
}
companion object {
fun create(parent: ViewGroup) : UserViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder(ActivityManageUsersBinding.bind(view))
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder.create(parent)
}
class UsersComparator : DiffUtil.ItemCallback<UsersTable>() {
override fun areItemsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
return oldItem.userName == newItem.userName
}
}
}
ManageUsers.kt
package com.neillbarrett.debitsandcredits
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.activity.viewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.neillbarrett.debitsandcredits.database.CreditsAndDebitsApp
import com.neillbarrett.debitsandcredits.database.UsersTable
import com.neillbarrett.debitsandcredits.databinding.ActivityManageUsersBinding
class ManageUsers : AppCompatActivity() {
lateinit var binding: ActivityManageUsersBinding
lateinit var recyclerView: RecyclerView
lateinit var editTextAddUser: EditText
lateinit var editTextChangeUser: EditText
lateinit var newUser: String
lateinit var userSelect: ((UsersTable?) -> Unit)
var position: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityManageUsersBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//setContentView(R.layout.activity_manage_users)
val userViewModel: UserViewModel by viewModels {
UserViewModelFactory((application as CreditsAndDebitsApp).repository)
}
recyclerView = findViewById(R.id.rec_view_userList)
editTextAddUser = findViewById(R.id.et_UserName)
editTextChangeUser = findViewById(R.id.et_Edit_Name)
val adapter = UserListAdapter(userSelect)
binding.recViewUserList.adapter = adapter
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
userViewModel.allUsers.observe(this, Observer() {user ->
user?.let { adapter.submitList(it) }
})
val btnAddUser = findViewById<Button>(R.id.btn_AddUser)
btnAddUser.setOnClickListener {
if (TextUtils.isEmpty(editTextAddUser.text)) {
Toast.makeText(this, "User name cannot be empty", Toast.LENGTH_SHORT).show()
} else {
newUser = editTextAddUser.text.toString()
userViewModel.insertUser(UsersTable(0, newUser))
}
}
val btnChangeUser = findViewById<Button>(R.id.btn_ChangeUserName)
btnChangeUser.setOnClickListener {
if (recyclerView.getChildAdapterPosition(it) == -1) {
Toast.makeText(this, "Select a name.", Toast.LENGTH_SHORT).show()
} else {
if (editTextChangeUser.text.toString() == recyclerView.adapter.toString()) {
Toast.makeText(this, "Name has not been changed.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Name would have been changed.", Toast.LENGTH_SHORT).show()
/*val rvItemRecId: Long
rvItemRecId = adapter.getItemId(position.toInt())
userViewModel.updateUser(UsersTable(rvItemRecId.toInt(), adapter.toString()))*/
}
}
}
}
}
UserViewModel.kt
package com.neillbarrett.debitsandcredits
import androidx.lifecycle.*
import com.neillbarrett.debitsandcredits.database.UsersTable
import kotlinx.coroutines.launch
import java.lang.IllegalArgumentException
class UserViewModel(private val repository: UserRepository) : ViewModel() {
val allUsers: LiveData<List<UsersTable>> = repository.allUsers.asLiveData()
fun insertUser(user: UsersTable) = viewModelScope.launch {
repository.insertUser(user)
}
fun updateUser(user: UsersTable) = viewModelScope.launch {
repository.updateUser(user)
}
fun deleteUser(user: UsersTable) = viewModelScope.launch {
repository.deleteUser(user)
}
}
class UserViewModelFactory(private val repository: UserRepository) : ViewModelProvider.Factory{
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return UserViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
If you check your ManageUsers activity implementation
lateinit var userSelect: ((UsersTable?) -> Unit)
userSelect is lateinit and it must be initialised before you pass the reference to UserListAdapter adapter. Something like this
userSelect = {
// do something
}
before passing to adapter, or
val userSelect: ((UsersTable?) -> Unit) = {
}
at the time of declaration.

How can I use notification on Fragment?

I want to use notification on Fragment. And if I touch button, notification will appear.
I use many ways, and I find one way to use it. but it is not worked. only run like this
2022-06-08 15:30:10.765 28876-28876/com.ebookfrenzy.sample D/MyFragment: startMyService: Intent: Intent { cmp=com.ebookfrenzy.sample/.ui.MyService }
And below one is mainFragment.kt
package com.ebookfrenzy.sample.ui.main
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.ebookfrenzy.sample.databinding.FragmentMainBinding
import com.ebookfrenzy.sample.ui.MyService
class MainFragment : Fragment() {
private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
companion object {
fun newInstance() = MainFragment()
private const val TAG = "MyFragment"
}
private lateinit var viewModel: MainViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentMainBinding.inflate(inflater, container, false)
binding.button.setOnClickListener { startMyService()
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun startMyService() {
Intent(context, MyService::class.java).also {
Log.d(TAG, "startMyService: Intent: $it")
// Check Android Policy
context?.startForegroundService(it)
// Less than Android Oreo
context?.startService(it)
}
}
}
MyService
package com.ebookfrenzy.sample.ui
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.annotation.Nullable
import androidx.core.app.NotificationCompat
import com.ebookfrenzy.sample.BuildConfig
import com.ebookfrenzy.sample.R
class MyService : Service() {
companion object {
private const val TAG = "MyService"
}
private val FOREGROUND_ID: Int = 290348
private val foregroundNotificationBuilder: NotificationCompat.Builder
get() = NotificationCompat.Builder(this, applicationContext.packageName).
setSmallIcon(R.mipmap.ic_launcher_round).
setContentTitle("title").
setContentText("content text").
setPriority(NotificationCompat.PRIORITY_HIGH)
override fun onCreate() {
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForegroundService()
return START_STICKY
}
#Nullable
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onDestroy() {
super.onDestroy()
stopForeground(true)
}
private fun createNotificationChannel() {
val channelId = applicationContext.packageName
val channelName = BuildConfig.APPLICATION_ID
val channel = NotificationChannel(
channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
private fun startForegroundService() {
val notification = foregroundNotificationBuilder.build()
// when Over android oreo, need to notification channel.
createNotificationChannel()
startForeground(FOREGROUND_ID, notification)
}
}
And other codes are not corrected after created
I want to resolve this problem. Please help me...

How to send a variable to an Adapter to send it through an Intent to another activity?

I have an adapter for my RecyclerView where I program that when I click on the element (of my RecyclerView) it executes an Intent with a putExtra to take me to another activity, the variable that contains my putExtra comes from the element that I clicked, but now I need to add a More variable that comes from the activity. The issue is that I don't know how to send it from the adapter.
this is my adapter.
package com.example.atipicoapp
import android.app.Activity
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.view.menu.ActionMenuItemView
import androidx.core.content.ContextCompat.startActivity
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.list_item.view.*
class MyAdapter(private val platoList : ArrayList<Plato>
) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyAdapter.MyViewHolder {
val itemView =
LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
itemView.platoTouch.setOnClickListener(View.OnClickListener { v: View ->
})
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyAdapter.MyViewHolder, position: Int) {
val plato: Plato = platoList[position]
holder.platoName.text = plato.platoName
holder.platoDescription.text = plato.platoDescription
holder.platoPrecio.text = plato.platoPrecio.toString()
holder.platoCantidad.text = plato.platoCantidad.toString()
when(holder){
is MyViewHolder -> {
holder.bind(platoList[position])
}
}
}
override fun getItemCount(): Int {
return platoList.size
}
public class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val platoName: TextView = itemView.findViewById(R.id.platoNombre)
val platoDescription: TextView = itemView.findViewById(R.id.platoDescripcion)
val platoPrecio: TextView = itemView.findViewById(R.id.platoPrecio)
val platoTouch: LinearLayout = itemView.findViewById(R.id.platoTouch)
val platoCantidad: TextView = itemView.findViewById(R.id.platoCant)
private val mActivity = itemView.context as Activity
private val intent = Intent(mActivity,SlotActivity::class.java)
fun bind(plato: Plato){
platoTouch.setOnClickListener{
intent.putExtra("id", platoName.text.toString())
mActivity.startActivity(intent)
}
}
}
}
And this is my Activity which contains my RecyclerView and the variable I want to send.
package com.example.atipicoapp
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.firebase.firestore.*
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_menu_atipico.*
class MenuAtipicoActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var platoArrayList: ArrayList<Plato>
private lateinit var myAdapter: MyAdapter
private lateinit var db: FirebaseFirestore
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_menu_atipico)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.setHasFixedSize(true)
platoArrayList = arrayListOf()
myAdapter = MyAdapter(platoArrayList)
recyclerView.adapter = myAdapter
pedidoId = intent.extras?.getString("pedidoId") //This is the variable I need to send
EventChangeListener()
Setup()
}
private fun EventChangeListener() {
db = FirebaseFirestore.getInstance()
db.collection("Platos").addSnapshotListener(object : EventListener<QuerySnapshot> {
override fun onEvent(
value: QuerySnapshot?,
error: FirebaseFirestoreException?
) {
if (error != null) {
Log.e("Firestore Error", error.message.toString())
return
}
for (dc: DocumentChange in value?.documentChanges!!) {
if (dc.type == DocumentChange.Type.ADDED) {
platoArrayList.add(dc.document.toObject(Plato::class.java))
}
}
myAdapter.notifyDataSetChanged()
}
})
}
private fun Setup() {
botonAceptar.setOnClickListener {
val SlotIntent = Intent(this, SlotActivity::class.java).apply {
}
startActivity(SlotIntent)
}
}
}
How can I send the variable if the Intent is executed from the Adapter?
Or... If it is not recommended to send intent from my Adapter, how can I send them from the activity?
Knowing that I have to carry a variable that is in the item of the RecyclerView.
Thanks for your help <3
Firstly, create a MyAdapter constructor where you pass arrayList as well as pedidoId like, your MyAdapter should be something like below:
MyAdapter.class
class MyAdapter(private val platoList : ArrayList<Plato>, val pedidoId:String
) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
........
.....
....
//in your bind(..) method
fun bind(plato: Plato){
platoTouch.setOnClickListener{
intent.putExtra("id", platoName.text.toString())
intent.putExtra("pedidoId", pedidoId)
mActivity.startActivity(intent)
}
}
}
And, in your MenuAtipicoActivity you need to do something like:
MenuAtipicoActivity
class MenuAtipicoActivity : AppCompatActivity() {
...............
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_menu_atipico)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.setHasFixedSize(true)
platoArrayList = arrayListOf()
pedidoId = intent.extras?.getString("pedidoId") //This is the variable I need to send
myAdapter = MyAdapter(platoArrayList,pedidoId)
recyclerView.adapter = myAdapter
EventChangeListener()
Setup()
}
..........
........
}

Type mismatch: inferred type is fragment_Dep but Context! was expected

l have Create a Swipe Views with Tabs in my app . I want to initialize the list view in the fragment class activity . l added the list adapte , but the problem is with this code :
val view : View = LayoutInflater.from(context).inflate(R.layout.row_layout,parent,false)
l have problem with context l have error Type mismatch: inferred type is fragment_Dep but Context! was expected
ListAdapte class
package com.iraqairoirt.iraqairports
import android.annotation.SuppressLint
import android.support.v7.widget.AppCompatTextView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
class ListAdapte (val context: fragment_Dep, val list: ArrayList<FlightShdu>): BaseAdapter() {
#SuppressLint("ViewHolder")
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val view : View = LayoutInflater.from(context).inflate(R.layout.row_layout,parent,false)
val CallsingID = view.findViewById(R.id.callsign_id) as AppCompatTextView
val StatusID = view.findViewById(R.id.status_id) as AppCompatTextView
CallsingID.text = list[position].Callsign.toString()
StatusID.text = list[position].Status
return view
}
override fun getItem(position: Int): Any {
return list [position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
return list.size
}
}
fragment class
package com.iraqairoirt.iraqairports
import android.os.AsyncTask
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.iraqairoirt.iraqairports.flightsArr.ListAdapteArr
import kotlinx.android.synthetic.main.fragment_arrivel.*
import org.json.JSONArray
import org.json.JSONObject
import java.net.HttpURLConnection
import java.net.URL
#Suppress("UNREACHABLE_CODE")
class fragment_Arr :Fragment(), View.OnClickListener {
override fun onClick(v: View?) {
// val intent = Intent(context, FlightsArrbefor::class.java)
// context!!.startActivity(intent)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_arrivel,container,false)
val url = "xxxxxxxxxxxxxxx/airport.json?code=BGW"
Dep().execute(url)
return view
}
// full class for json api
inner class Dep : AsyncTask<String, String, String>(){
override fun onPreExecute() {
super.onPreExecute()
}
// for build connection
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
handleJson(result)
}
override fun onProgressUpdate(vararg text: String?) {
}
// for build connection
override fun doInBackground(vararg url: String?): String{
var text : String
val connection = URL(url[0]).openConnection() as HttpURLConnection
try {
connection.connect()
text = connection.inputStream.use { it.reader().use{reader -> reader.readText()} }
} finally{
connection.disconnect()
}
return text
}
private fun handleJson (jsonString: String?){
val jsonObj = JSONObject(jsonString)
val result = jsonObj.getJSONObject("result")
val response = result.getJSONObject("response")
val airport = response.getJSONObject("airport")
val pluginData = airport.getJSONObject("pluginData")
val schedule = pluginData.getJSONObject("schedule")
val departures = schedule.getJSONObject("departures")
// val data = arrivals.getJSONObject("data")
val jsonArray = JSONArray(departures.get("data").toString())
val list = ArrayList<FlightShdu>()
var x = 0
while (x < jsonArray.length()){
val jsonObject = jsonArray.getJSONObject(x)
list.add(FlightShdu(
jsonObject.getJSONObject("flight").getJSONObject("identification").getJSONObject("number").getString("default"),
jsonObject.getJSONObject("flight").getJSONObject("airline").getString("short"),
jsonObject.getJSONObject("flight").getJSONObject("status").getJSONObject("generic").getJSONObject("status").getString("text"),
jsonObject.getJSONObject("flight").getJSONObject("airline").getJSONObject("code").getString("icao"),
jsonObject.getJSONObject("flight").getJSONObject("time").getJSONObject("scheduled").getString("departure"),
jsonObject.getJSONObject("flight").getJSONObject("airport").getJSONObject("destination").getJSONObject("code").getString("iata"),
jsonObject.getJSONObject("flight").getJSONObject("aircraft").getJSONObject("model").getString("code"),
// for more information
jsonObject.getJSONObject("flight").getJSONObject("time").getJSONObject("real").getString("departure"),
jsonObject.getJSONObject("flight").getJSONObject("time").getJSONObject("estimated").getString("departure"),
// jsonObject.getJSONObject("flight").getJSONObject("time").getJSONObject("estimated").getString("arrival"),
jsonObject.getJSONObject("flight").getJSONObject("aircraft").getString("registration"),
jsonObject.getJSONObject("flight").getJSONObject("status").getJSONObject("generic").getJSONObject("status").getString("diverted"),
departures.getString("timestamp"),
jsonObject.getJSONObject("flight").getJSONObject("status").getString("icon")
))
x++
}
list.forEach(::println)
val adapter = ListAdapteArr(list)
flight_arrivel_list.adapter = adapter
}
}
}
You can simply pass Context into ListAdapter signature
class ListAdapter(val context: Context, val list: ArrayList<FlightShdu>): BaseAdapter() {
If you are creating the adapter from an activity you can simply do ListAdapter(this, <your_list>)
If you are creating it from a fragment you can do ListAdapter(this.activity, <your_list>)
I suggest you get familiar with the Kotlin language https://kotlinlang.org/docs/reference/