Kotlin use tabLayout with 2 activities - kotlin

I have 2 activities. Which I tried to convert into fragments but somehow, the code I use for the activities won't work within fragments. I get a bunch of errors.
The first activity is a card where when clicked a random text will appear and the card is flipping.
The second activity is a timer with a 3th party progress bar that the fragment gives an error.
Now my question is, how can I use 2 activities within a tabLayout since I cannot convert the activity code into a fragment?
I have been searching the internet for hours but I can only find that you can use fragments within a tabLayout.
Is there a tutorial on how to create a tabLayout with 2 activities?
I also followed the tutorial https://mkyong.com/android/android-tablayout-example/ but that didn't work.
This is my Timer activity:
Kotlin:
class NewTimerActivity : AppCompatActivity() {
enum class TimerState {
Stopped, Paused, Running
}
private lateinit var timer: CountDownTimer
private var timerLengthSeconds: Int = 30
private var timerState = TimerState.Stopped
private var secondsRemaining = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new_timer)
val display = supportActionBar
display?.title = ""
display?.setDisplayHomeAsUpEnabled(true)
val fab_start = findViewById<Button>(R.id.fab_start)
val fab_pause = findViewById<Button>(R.id.fab_pause)
val fab_stop = findViewById<Button>(R.id.fab_stop)
fab_start.setOnClickListener {
startTimer()
timerState = TimerState.Running
updateButtons()
}
fab_pause.setOnClickListener {
timer.cancel()
timerState = TimerState.Paused
updateButtons()
}
fab_stop.setOnClickListener {
timer.cancel()
onTimerFinished()
updateButtons()
}
}
override fun onResume() {
super.onResume()
initTimer()
}
override fun onPause() {
super.onPause()
if (timerState == TimerState.Running) {
timer.cancel()
} else if (timerState == TimerState.Paused) {
}
PrefUtil.setPreviousTimerLengthSeconds(timerLengthSeconds, this)
PrefUtil.setSecondsRemaining(secondsRemaining, this)
PrefUtil.setTimerState(timerState, this)
}
fun initTimer() {
timerState = PrefUtil.getTimerState(this)
if (timerState == TimerState.Stopped)
setNewTimerLength()
else
setPreviousTimerLength()
secondsRemaining = if (timerState == TimerState.Running || timerState == TimerState.Paused)
PrefUtil.getSecondsRemaining(this).toInt()
else
timerLengthSeconds
if (timerState == TimerState.Running)
startTimer()
updateButtons()
updateCountdownUI()
}
private fun onTimerFinished() {
var progress_countdown = findViewById<ProgressBar>(R.id.progress_countdown)
timerState = TimerState.Stopped
setNewTimerLength()
progress_countdown.progress = 0
PrefUtil.setSecondsRemaining(timerLengthSeconds, this)
secondsRemaining = timerLengthSeconds
updateButtons()
updateCountdownUI()
}
private fun startTimer() {
timerState = TimerState.Running
timer = object : CountDownTimer((secondsRemaining * 1000).toLong(), 1000) {
override fun onFinish() = onTimerFinished()
override fun onTick(millisUntilFinished: Long) {
secondsRemaining = (millisUntilFinished / 1000).toInt()
updateCountdownUI()
}
}.start()
}
private fun setNewTimerLength() {
var progress_countdown = findViewById<ProgressBar>(R.id.progress_countdown)
var lengthInMinutes = PrefUtil.getTimerLength(this)
timerLengthSeconds = ((lengthInMinutes * 60L).toInt())
progress_countdown.max = timerLengthSeconds.toInt()
}
private fun setPreviousTimerLength() {
var progress_countdown = findViewById<ProgressBar>(R.id.progress_countdown)
timerLengthSeconds = PrefUtil.getPreviousTimerLengthSeconds(this).toInt()
progress_countdown.max = timerLengthSeconds.toInt()
}
private fun updateCountdownUI() {
var progress_countdown = findViewById<ProgressBar>(R.id.progress_countdown)
val textView_Countdown = findViewById<TextView>(R.id.timer_textview)
val minutesUntilFinished = secondsRemaining / 60
val secondsInMinutesUntilFinished = secondsRemaining - minutesUntilFinished * 60
val secondsStr = secondsInMinutesUntilFinished.toString()
textView_Countdown.text = "$minutesUntilFinished:${
if (secondsStr.length == 2) secondsStr
else "0" + secondsStr}"
progress_countdown.progress = (timerLengthSeconds - secondsRemaining).toInt()
}
private fun updateButtons() {
val fab_start = findViewById<Button>(R.id.fab_start)
val fab_pause = findViewById<Button>(R.id.fab_pause)
val fab_stop = findViewById<Button>(R.id.fab_stop)
when (timerState) {
TimerState.Running -> {
fab_start.isEnabled = false
fab_pause.isEnabled = true
fab_stop.isEnabled = true
}
TimerState.Stopped -> {
fab_start.isEnabled = true
fab_pause.isEnabled = false
fab_stop.isEnabled = false
}
TimerState.Paused -> {
fab_start.isEnabled = true
fab_pause.isEnabled = false
fab_stop.isEnabled = true
}
}
}
}
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/achtergrondnowhite"
tools:context=".NewTimerActivity">
<TextView
android:id="#+id/timer_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="30"
android:textColor="#FFF"
android:textSize="70dp" />
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/Theme.KlimaatAmbitieGame.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/Theme.KlimaatAmbitieGame.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="#+id/progress_countdown"
style="#style/Widget.MaterialProgressBar.ProgressBar"
android:layout_width="306dp"
android:layout_height="306dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginStart="52dp"
android:layout_marginTop="215dp"
android:layout_marginEnd="52dp"
android:layout_marginBottom="210dp"
android:minWidth="306dp"
android:minHeight="306dp"
app:mpb_indeterminateTint="#color/white"
app:mpb_progressBackgroundTint="#color/white"
app:mpb_progressTint="#color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/fab_stop"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|end"
android:layout_marginStart="274dp"
android:layout_marginEnd="27dp"
android:layout_marginBottom="131dp"
android:background="#color/black"
android:minWidth="50dp"
android:minHeight="50dp"
android:text="Stop"
android:textColor="#color/white"
app:srcCompat="#drawable/ic_stop" />
<Button
android:id="#+id/fab_pause"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|center"
android:layout_marginStart="156dp"
android:layout_marginTop="#dimen/bigger_fab_margin"
android:layout_marginEnd="151dp"
android:layout_marginBottom="131dp"
android:background="#color/black"
android:minWidth="150dp"
android:minHeight="50dp"
android:text="Pause"
android:textColor="#color/white"
app:srcCompat="#drawable/ic_pause" />
<Button
android:id="#+id/fab_start"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|start"
android:layout_marginStart="45dp"
android:layout_marginTop="#dimen/bigger_fab_margin"
android:layout_marginEnd="280dp"
android:layout_marginBottom="133dp"
android:background="#color/black"
android:minWidth="100dp"
android:minHeight="50dp"
android:text="Start"
android:textColor="#color/white" />
</RelativeLayout>
PrefUtil
class PrefUtil {
companion object {
fun getTimerLength(context: Context): Double {
//placeholder
return 0.5
}
//private var defValue: Long
private const val PREVIOUS_TIMER_LENGTH_SECONDS_ID = "com.resoconder.timer.previous_timer_length"
fun getPreviousTimerLengthSeconds(context: Context): Long {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
return preferences.getLong(PREVIOUS_TIMER_LENGTH_SECONDS_ID, 0)
}
fun setPreviousTimerLengthSeconds(seconds: Int, context: Context) {
val editor = PreferenceManager.getDefaultSharedPreferences(context).edit()
editor.putLong(PREVIOUS_TIMER_LENGTH_SECONDS_ID, seconds.toLong())
editor.apply()
}
private const val TIMER_STATE_ID = "com.resocoder.timer.timer_state"
fun getTimerState(context: Context): NewTimerActivity.TimerState {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
val ordinal = preferences.getInt(TIMER_STATE_ID, 0)
return NewTimerActivity.TimerState.values()[ordinal]
}
fun setTimerState(state: NewTimerActivity.TimerState, context: Context) {
val editor = PreferenceManager.getDefaultSharedPreferences(context).edit()
val ordinal = state.ordinal
editor.putInt(TIMER_STATE_ID, ordinal)
editor.apply()
}
private const val SECONDS_REMAINING_ID = "com.resoconder.timer.previous_timer_length"
fun getSecondsRemaining(context: Context): Long {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
return preferences.getLong(SECONDS_REMAINING_ID, 0)
}
fun setSecondsRemaining(seconds: Int, context: Context) {
val editor = PreferenceManager.getDefaultSharedPreferences(context).edit()
editor.putLong(SECONDS_REMAINING_ID, seconds.toLong())
editor.apply()
}
}
}
And this is my Card activity:
Kotlin:
class StartKaartActivity : AppCompatActivity() {
lateinit var front_anim:AnimatorSet
lateinit var back_anim:AnimatorSet
var isFront = false
#SuppressLint("ResourceType")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_start_kaart)
//Set Backbutton Bar
val display = supportActionBar
display?.title = ""
display?.setDisplayHomeAsUpEnabled(true)
display?.setDisplayUseLogoEnabled(true)
display?.setLogo(R.drawable.logo)
val scale = applicationContext.resources.displayMetrics.density
val tvstartkaart = findViewById<TextView>(com.fotf.klimaatambitiegame.R.id.tvstartkaart)
val tvstartkaartachterkant = findViewById<TextView>(com.fotf.klimaatambitiegame.R.id.tvstartkaartachterkant)
tvstartkaart.cameraDistance = 8000 * scale
tvstartkaartachterkant.cameraDistance = 8000 * scale
front_anim = AnimatorInflater.loadAnimator(applicationContext, R.anim.font_animation) as AnimatorSet
back_anim = AnimatorInflater.loadAnimator(applicationContext, R.anim.back_animation) as AnimatorSet
val Carts = arrayOf("" +
"Random Text",
)
tvstartkaart.setOnClickListener() {
if (isFront) {
front_anim.setTarget(tvstartkaart)
back_anim.setTarget(tvstartkaartachterkant)
front_anim.start()
back_anim.start()
isFront = false
} else {
val random = Carts.random()
tvstartkaart.setText(random)
front_anim.setTarget(tvstartkaartachterkant)
back_anim.setTarget(tvstartkaart)
front_anim.start()
back_anim.start()
isFront = true
}
};
}
}
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/achtergrondnowhite"
tools:context=".StartKaartActivity">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="176dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="177dp"
android:paddingHorizontal="10dp"
android:text="Klik op de kaart om een nieuwe kaart te krijgen. \n\nGeef het goede antwoord op de vraag en verdien een houder."
android:textColor="#color/white"
android:textSize="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tvstartkaart"
android:layout_width="350dp"
android:layout_height="450dp"
android:layout_marginTop="28dp"
android:background="#drawable/kaartstartvoorkant"
android:gravity="left|center"
android:padding="15dp"
android:paddingHorizontal="10dp"
android:text=""
android:textColor="#color/black"
android:textSize="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.491"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView" />
<TextView
android:id="#+id/tvstartkaartachterkant"
android:layout_width="350dp"
android:layout_height="450dp"
android:layout_marginTop="28dp"
android:background="#drawable/kaartstartachterkant"
android:gravity="left|center"
android:padding="15dp"
android:paddingHorizontal="10dp"
android:textColor="#color/black"
android:textSize="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.491"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>

Related

Recycler View not displaying inside a Fragment

I'm using Recycler view to show items in my Fragment named Recent History. I want to display user data from firebase to my recent history Fragment. But the recycler view is not showing on the fragment. Please Help me out in this. What did I do wrong here? or what's the solution so that my fragment shows the recycler view.
Recent History Fragment Code:
`
class recent_history : Fragment(R.layout.fragment_recent_history) {
private var binding: FragmentRecentHistoryBinding? = null
private lateinit var auth: FirebaseAuth
var database: FirebaseDatabase? = null
var databaseReference: DatabaseReference? = null
private lateinit var userArrayList: ArrayList<User>
private lateinit var myAdapter: AdapterClass
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentRecentHistoryBinding.inflate(inflater, container, false)
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
auth = FirebaseAuth.getInstance()
database = FirebaseDatabase.getInstance()
databaseReference = database?.reference!!.child("Users").child("result")
userArrayList= arrayListOf()
myAdapter=AdapterClass(userArrayList)
binding?.recyclerview.apply {
binding?.recyclerview?.adapter = myAdapter
var linearLayoutManager = LinearLayoutManager(activity)
binding?.recyclerview?.layoutManager = linearLayoutManager
binding?.recyclerview?.setHasFixedSize(true)
}
getData()
}
override fun onDestroy() {
super.onDestroy()
binding = null
}
private fun getData() {
val user = auth.currentUser
databaseReference?.child(user?.uid!!)
databaseReference!!.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
for (data in snapshot.children) {
var model = data.getValue(User::class.java)
userArrayList.add(model!!)
}
}
}
override fun onCancelled(error: DatabaseError) {
Log.e("cancel", error.toString())
}
})
}
}
`
Fragment Recent History Layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
tools:context=".recent_history">
<TextView
android:id="#+id/profiletitle"
android:layout_width="389dp"
android:layout_height="46dp"
android:layout_marginStart="4dp"
android:layout_marginTop="28dp"
android:fontFamily="monospace"
android:text="Recent Search History"
android:textAlignment="center"
android:textColor="#color/Twit"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</TextView>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="220dp"
android:layout_height="664dp"
android:layout_marginBottom="100dp"
android:orientation="vertical"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/profiletitle"
tools:listitem="#layout/adapterview">
</androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>
I'm sending data to firebase from another Fragment named keyword Fragment.
Code of keyword fragment:
class keywordfrag : Fragment() {
private var binding: FragmentKeywordfragBinding? = null
private lateinit var auth: FirebaseAuth
var database: FirebaseDatabase? = null
var databaseReference: DatabaseReference? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentKeywordfragBinding.inflate(inflater, container, false)
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
auth = FirebaseAuth.getInstance()
database = FirebaseDatabase.getInstance()
val user = auth.currentUser
databaseReference?.child(user?.uid!!)
databaseReference = database?.getReference("Users")?.child("result")
binding?.analyzebtn?.setOnClickListener {
sendData()
}
}
override fun onDestroy() {
super.onDestroy()
binding = null
}
private fun sendData() {
val keywordtext = binding?.userenteredkeyword?.text.toString().trim()
val timestamp = Timestamp(System.currentTimeMillis())
val timendate = timestamp.toString().trim()
// val username=auth.currentUser?.uid.toString().trim()
// val email=auth.currentUser?.email.toString().trim()
if (TextUtils.isEmpty(keywordtext)) {
binding?.userenteredkeyword?.error = "keyword can not be Empty"
} else {
val model = User(keywordtext, timendate)
val user = auth.currentUser
databaseReference?.child(user?.uid!!)?.setValue(model)
binding?.userenteredkeyword?.setText("")
val currentUser = auth.currentUser
val currentUserdb = databaseReference?.child((currentUser?.uid!!))
currentUserdb?.child("keywordtext")?.setValue(keywordtext)
currentUserdb?.child("timendate")?.setValue(timendate)
}
}
}
Adapter Class:
package com.example.emotela_finalyearproject
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.emotela_finalyearproject.databinding.AdapterviewBinding
import kotlin.collections.ArrayList
class AdapterClass(var list:ArrayList<User>) :RecyclerView.Adapter<AdapterClass.ViewHolder>() {
class ViewHolder(val binding: AdapterviewBinding) : RecyclerView.ViewHolder(binding.root) {
var keyword = binding.keywordtv
var timenddate=binding.timendatetv
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = AdapterviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
with(holder) {
with(list[position]) {
keyword.text = this.keywordtext
timenddate.text= this.timendate
}
}
}
}
Adapter view Layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardElevation="8dp"
app:cardCornerRadius="8dp"
android:layout_margin="16dp">
<LinearLayout
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginVertical="6dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="336dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="#+id/keywordtitle"
android:layout_width="141dp"
android:layout_height="35dp"
android:fontFamily="monospace"
android:text="searched word: "
android:textSize="10sp"
android:textStyle="bold">
</TextView>
<TextView
android:id="#+id/keywordtv"
android:layout_width="143dp"
android:layout_height="36dp"
android:fontFamily="monospace"
android:textSize="10sp">
</TextView>
<TextView
android:id="#+id/timeanddatetitle"
android:layout_width="160dp"
android:layout_height="36dp"
android:fontFamily="monospace"
android:text="Time and Date: "
android:textSize="10sp"
android:textStyle="bold">
</TextView>
<TextView
android:id="#+id/timendatetv"
android:layout_width="171dp"
android:layout_height="48dp"
android:fontFamily="monospace"
android:textSize="10sp">
</TextView>
<LinearLayout
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginVertical="6dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="336dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
Result in Emulator:
You need to calll
imageAdapter.notifyDataSetChanged()
after you fetch the data from firebase.

Failed to take picture from arFragment - ARCore - Augmented Face

I follow code here but failed to capture my ArFragment. Please help me.
https://codelabs.developers.google.com/codelabs/sceneform-intro/index.html?index=..%2F..io2018#15
This project's link: https://github.com/DSparda/Breakable-Cam
Always get IOException: "Failed to save bitmap to disk"
My WritingArFragment:
class FaceArFragment : ArFragment() {
override fun getSessionConfiguration(session: Session?): Config {
return Config(session).apply { augmentedFaceMode = Config.AugmentedFaceMode.MESH3D }
}
override fun getSessionFeatures(): MutableSet<Session.Feature> {
return EnumSet.of(Session.Feature.FRONT_CAMERA)
}
override fun getAdditionalPermissions(): Array<String?>? {
val additionalPermissions = super.getAdditionalPermissions()
val permissionLength =
additionalPermissions?.size ?: 0
val permissions =
arrayOfNulls<String>(permissionLength + 1)
permissions[0] = Manifest.permission.WRITE_EXTERNAL_STORAGE
if (permissionLength > 0) {
System.arraycopy(
additionalPermissions,
0,
permissions,
1,
additionalPermissions!!.size
)
}
return permissions
}
/**
* Override to turn off planeDiscoveryController. Plane trackables are not supported with the
* front camera.
*/
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val layout = super.onCreateView(inflater, container, savedInstanceState) as FrameLayout
planeDiscoveryController.apply {
hide()
setInstructionView(null)
}
return layout
}
}
My Activity XML:
<layout 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">
<data>
<variable
name="takePictureViewModel"
type="com.example.breakablecam.screens.takingPicture.TakePictureViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/face_fragment_cointanier"
android:layout_width="match_parent"
android:layout_height="0dp"
android:paddingBottom="16dp"
app:layout_constraintBottom_toTopOf="#+id/takePhotoView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
<fragment
android:id="#+id/face_fragment"
android:name="com.example.breakablecam.screens.takingPicture.FaceArFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.fragment.app.FragmentContainerView>
<ImageView
android:id="#+id/backArrow"
android:layout_width="#android:dimen/app_icon_size"
android:layout_height="#android:dimen/app_icon_size"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="#+id/stickerView"
android:layout_width="#android:dimen/app_icon_size"
android:layout_height="#android:dimen/app_icon_size"
android:layout_marginBottom="16dp"
android:onClick="#{() -> takePictureViewModel.tapSticker()}"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/takePhotoView"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="#+id/stickerView1"
android:layout_width="#android:dimen/app_icon_size"
android:layout_height="#android:dimen/app_icon_size"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:visibility="gone"
android:onClick="#{() -> takePictureViewModel.tapSticker1()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="#+id/takePhotoView"
android:layout_width="#android:dimen/app_icon_size"
android:layout_height="#android:dimen/app_icon_size"
android:layout_marginBottom="16dp"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/makeupView"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/stickerView" />
<ImageView
android:id="#+id/makeupView"
android:layout_width="#android:dimen/app_icon_size"
android:layout_height="#android:dimen/app_icon_size"
android:layout_marginBottom="16dp"
android:onClick="#{() -> takePictureViewModel.tapMakeupView()}"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/takePhotoView" />
<ImageView
android:id="#+id/makeupView1a"
android:layout_width="#android:dimen/app_icon_size"
android:layout_height="#android:dimen/app_icon_size"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:onClick="#{() -> takePictureViewModel.tapMakeup1aView()}"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
<ImageView
android:id="#+id/makeupView1"
android:layout_width="#android:dimen/app_icon_size"
android:layout_height="#android:dimen/app_icon_size"
android:layout_marginStart="16dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="#id/makeupView1a"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
My Activity:
class TakePictureActivity : AppCompatActivity() {
private lateinit var viewModel: TakePictureViewModel
private lateinit var arFragment: FaceArFragment
private var modelRenderable: ModelRenderable? = null
private var meshTexture: Texture? = null
private val faceNodeMap = HashMap<AugmentedFace, AugmentedFaceNode?>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityTakePictureBinding =
DataBindingUtil.setContentView(this, R.layout.activity_take_picture)
viewModel = ViewModelProvider(this).get(TakePictureViewModel::class.java)
binding.apply {
viewModel.apply {
setStickerViewSource(stickerView)
setMakeupViewSource(makeupView)
setTakePhotoViewSource(takePhotoView)
setMakeup1ViewSource(makeupView1)
setMakeup1aViewSource(makeupView1a)
setSticker1ViewSource(stickerView1)
setBackArrowSource(backArrow)
}
}
binding.takePictureViewModel = viewModel
binding.lifecycleOwner = this
viewModel.makeupTap.observe(this, Observer { check ->
if (check == 1) {
binding.apply {
makeupView.visibility = GONE
takePhotoView.visibility = GONE
stickerView.visibility = GONE
makeupView1.visibility = VISIBLE
makeupView1a.visibility = VISIBLE
backArrow.visibility = VISIBLE
val params =
faceFragmentCointanier.layoutParams as ConstraintLayout.LayoutParams
params.bottomToTop = R.id.makeupView1
}
viewModel.doneTapMakeup()
}
})
viewModel.stickerTap.observe(this, Observer { check ->
if (check == 1) {
binding.apply {
makeupView.visibility = GONE
takePhotoView.visibility = GONE
stickerView.visibility = GONE
stickerView1.visibility = VISIBLE
backArrow.visibility = VISIBLE
val params =
faceFragmentCointanier.layoutParams as ConstraintLayout.LayoutParams
params.bottomToTop = R.id.stickerView1
}
viewModel.doneTapSticker()
}
})
binding.apply {
backArrow.setOnClickListener {
makeupView.visibility = VISIBLE
takePhotoView.visibility = VISIBLE
stickerView.visibility = VISIBLE
makeupView1.visibility = GONE
makeupView1a.visibility = GONE
backArrow.visibility = GONE
stickerView1.visibility = GONE
backArrow.visibility = GONE
val params =
faceFragmentCointanier.layoutParams as ConstraintLayout.LayoutParams
params.bottomToTop = R.id.takePhotoView
}
}
arFragment = supportFragmentManager.findFragmentById(R.id.face_fragment) as FaceArFragment
val sceneView = arFragment.arSceneView
sceneView.cameraStreamRenderPriority = Renderable.RENDER_PRIORITY_FIRST
val scene = sceneView.scene
loadTexture(R.drawable.makeup1a)
loadModel(R.raw.fox_face)
viewModel.makeupTap1a.observe(this, Observer { check ->
when (check) {
1 -> {
scene.addOnUpdateListener {
val collection: Collection<AugmentedFace>? =
sceneView.session?.getAllTrackables(AugmentedFace::class.java)
collection?.forEach { face ->
if (!faceNodeMap.containsKey(face)) {
val faceNode = AugmentedFaceNode(face)
faceNode.apply {
setParent(scene)
faceMeshTexture = meshTexture
}
faceNodeMap[face] = faceNode
}
}
val iterator = faceNodeMap.entries.iterator()
while (iterator.hasNext()) {
val entry = iterator.next()
val face = entry.key
if (face.trackingState == TrackingState.STOPPED) {
val faceNode = entry.value
faceNode!!.setParent(null)
iterator.remove()
}
}
}
}
2 -> {
val children: List<Node> =
ArrayList(arFragment.arSceneView.scene.children)
for (node in children) {
if (node is AnchorNode) {
if (node.anchor != null) {
node.anchor?.detach()
}
}
if (node !is Camera && node !is Sun) {
node.setParent(null)
}
}
}
}
})
viewModel.sticker1Tap.observe(this, Observer { check ->
when (check) {
1 -> {
scene.addOnUpdateListener {
val collection: Collection<AugmentedFace>? =
sceneView.session?.getAllTrackables(AugmentedFace::class.java)
collection?.forEach { face ->
if (!faceNodeMap.containsKey(face)) {
val faceNode = AugmentedFaceNode(face)
faceNode.apply {
setParent(scene)
faceRegionsRenderable = modelRenderable
}
faceNodeMap[face] = faceNode
}
}
val iterator = faceNodeMap.entries.iterator()
while (iterator.hasNext()) {
val entry = iterator.next()
val face = entry.key
if (face.trackingState == TrackingState.STOPPED) {
val faceNode = entry.value
faceNode!!.setParent(null)
iterator.remove()
}
}
}
}
2 -> {
val children: List<Node> =
ArrayList(arFragment.arSceneView.scene.children)
for (node in children) {
if (node is AnchorNode) {
if (node.anchor != null) {
node.anchor?.detach()
}
}
if (node !is Camera && node !is Sun) {
node.setParent(null)
}
}
}
}
})
binding.takePhotoView.setOnClickListener {
takePhoto()
}
}
private fun loadTexture(tex: Int) {
Texture.builder()
.setSource(this, tex)
.build()
.thenAccept { texture -> meshTexture = texture }
}
private fun loadModel(mod: Int) {
ModelRenderable.builder()
.setSource(this, mod)
.build()
.thenAccept { model ->
model.apply {
isShadowCaster = false // optional
isShadowReceiver = false
}
modelRenderable = model
}
}
private fun generateFilename(): String? {
val date =
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault())
.format(Date())
return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES
).toString() + File.separator + "Sceneform/" + date + "_screenshot.jpg"
}
#Throws(IOException::class)
private fun saveBitmapToDisk(bitmap: Bitmap, filename: String) {
val out = File(filename)
if (!out.parentFile.exists()) {
out.parentFile.mkdirs()
}
try {
FileOutputStream(filename).use { outputStream ->
ByteArrayOutputStream().use { outputData ->
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputData)
outputData.writeTo(outputStream)
outputStream.flush()
outputStream.close()
}
}
} catch (ex: IOException) {
throw IOException("Failed to save bitmap to disk", ex)
}
}
private fun takePhoto() {
val filename = generateFilename()
val view: ArSceneView = arFragment.getArSceneView()
// Create a bitmap the size of the scene view.
val bitmap = Bitmap.createBitmap(
view.width, view.height,
Bitmap.Config.ARGB_8888
)
// Create a handler thread to offload the processing of the image.
val handlerThread = HandlerThread("PixelCopier")
handlerThread.start()
// Make the request to copy.
PixelCopy.request(view, bitmap, { copyResult ->
if (copyResult === PixelCopy.SUCCESS) {
try {
saveBitmapToDisk(bitmap, filename!!)
} catch (e: IOException) {
val toast = Toast.makeText(
this, e.toString(),
Toast.LENGTH_LONG
)
toast.show()
return#request
}
val snackbar = Snackbar.make(
findViewById(android.R.id.content),
"Photo saved", Snackbar.LENGTH_LONG
)
snackbar.setAction(
"Open in Photos"
) { v: View? ->
val photoFile = File(filename)
val photoURI = FileProvider.getUriForFile(
this,
this.getPackageName()
.toString() + ".ar.codelab.name.provider",
photoFile
)
val intent = Intent(Intent.ACTION_VIEW, photoURI)
intent.setDataAndType(photoURI, "image/*")
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent)
}
snackbar.show()
} else {
val toast = Toast.makeText(
this,
"Failed to copyPixels: $copyResult", Toast.LENGTH_LONG
)
toast.show()
}
handlerThread.quitSafely()
}, Handler(handlerThread.looper))
}
}
Thank everyone so much.
try add
<application android:requestLegacyExternalStorage="true" ... > ... </application>
to your manifest.
I assume that this is related to the upcoming scoped storage enforcement in Android 11.
You have to add the tags in your AndroidManifest.xml specified in the codelabs(permissions and file provider.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.ar.codelab.name.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/paths"/>
</provider>
Then you have to migrate from using File to using Uris and ContentResolvers because Android 10 deprecated the File functionality and you have to use MediaStore.
https://developer.android.com/training/data-storage/shared/media

Admob native ads of UnifiedNativeAdView are not clickable in Kotlin

I faced a strange bug with admob native ads. Everything works fine except that I cannot find url value for an ads on callback and that UnifiedNativeAdView is not clickable, even button inside don't go anywhere as I use admob templates.
Here is the XML view code:
<com.google.android.gms.ads.formats.UnifiedNativeAdView
android:paddingTop="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/native_ad_view"
android:background="#000000"
android:elevation="20dp">
<LinearLayout
android:id="#+id/native_ad_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
style="#style/AdAttribution"
android:id="#+id/native_ad_attribution"
android:visibility="gone"/>
<LinearLayout
android:id="#+id/native_ad_inside_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="1"
android:orientation="horizontal"
android:visibility="gone">
<ImageView
android:id="#+id/ad_app_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:maxWidth="85dp"
android:scaleType="centerCrop" />
<TextView
android:id="#+id/ad_headline"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:maxLines="3"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:textSize="16sp"
android:textStyle="bold" />
<Button
android:id="#+id/ad_call_to_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:maxWidth="50dp"
android:maxLines="3"
android:textSize="12sp"
/>
</LinearLayout>
</LinearLayout>
</com.google.android.gms.ads.formats.UnifiedNativeAdView>
Code of Viewholder:
inner class TripViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val adView: UnifiedNativeAdView = view.native_ad_view as UnifiedNativeAdView
var layoutView: LinearLayout = view.native_ad_layout
var insideLayoutView: LinearLayout = view.native_ad_inside_layout
var attributionView: TextView = view.native_ad_attribution
var headlineView: TextView = view.ad_headline
var callToActionView: Button = view.ad_call_to_action
var iconView: ImageView = view.ad_app_icon
}
Code of onBindViewHolder:
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = mValues[position]
if (item.nativeAd == null) {
return
}
if (item.nativeAd!!.headline == null) {
return
}
holder.attributionView.setVisibility(View.VISIBLE);
holder.insideLayoutView.setVisibility(View.VISIBLE);
holder.headlineView.text = item.nativeAd!!.headline
if (item.nativeAd!!.callToAction == null) {
holder.callToActionView.visibility = View.INVISIBLE
} else {
holder.callToActionView.visibility = View.VISIBLE
(holder.callToActionView as Button).text = item.nativeAd!!.callToAction
}
if (item.nativeAd!!.icon == null) {
holder.iconView.visibility = View.GONE
} else {
(holder.iconView as ImageView).setImageDrawable(item.nativeAd!!.icon.drawable)
holder.iconView.visibility = View.VISIBLE
}
// Assign native ad object to the native view.
holder.adView.setNativeAd(item.nativeAd!!)
}
And this is where I call adLoader. I call it in the background using BroadcastReceiver:
fun loadNativeAds(context: Context, listener: MyTripsContent.ContentEventsListener?) {
if (!adList.isEmpty()) {
return
}
try {
var unitId: String? = ""
lateinit var adLoader: AdLoader
//test key
unitId = "ca-app-pub-3940256099942544/2247696110"
adLoader = AdLoader.Builder(context, unitId)
.forUnifiedNativeAd { ad: UnifiedNativeAd ->
// Show the ad.
adList.add(ad)
if (adList.size == 5) {
listener?.onLoadedDocuments(0)
}
}
.withAdListener(object : AdListener() {
override fun onAdFailedToLoad(errorCode: Int) {
// Handle the failure by logging, altering the UI, and so on.
if (!adLoader.isLoading) {
}
}
override fun onAdClicked() {
super.onAdClicked()
}
})
.withNativeAdOptions(
NativeAdOptions.Builder()
// Methods in the NativeAdOptions.Builder class can be
// used here to specify individual options settings.
.build()
)
.build()
adLoader.loadAds(AdRequest.Builder().build(), 5)
} catch (exception: Exception) {
exception.printStackTrace()
}
}
So as I mentioned ads work well but they are not clickable. I'm not sure if this is a bug from admob, or my code or something I am missing. I think other developers will face the same issue in the future, so it would be good to solve it.
Thank you in advance.
So due to lack of detailed documentation and examples I found I was missing one line of code. Many other examples do not have this line also. I only added one line in onBindViewHolder
From this:
if (item.nativeAd!!.callToAction == null) {
holder.callToActionView.visibility = View.INVISIBLE
} else {
holder.callToActionView.visibility = View.VISIBLE
(holder.callToActionView as Button).text = item.nativeAd!!.callToAction
}
to this:
if (item.nativeAd!!.callToAction == null) {
holder.callToActionView.visibility = View.INVISIBLE
} else {
holder.callToActionView.visibility = View.VISIBLE
(holder.callToActionView as Button).text = item.nativeAd!!.callToAction
holder.adView.callToActionView = (holder.callToActionView as Button)
}
That means I needed to attach my button as callToActionView

My Kotlin Android Studio App Keeps Crashing When Pushing Button While Value Is Not Given

Its a simle app that shows some information about any given number like if its odd or prime.
Like the title said app works fine when any value is given but as soon as i push buton without it app crashes.
I have tried many thing but nothing changes. Here is code:
package com.example.numbers
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
fun oddOrEven(num: Int): String
{
return when (num % 2 == 0)
{
true -> "Number is: even\n"
false -> "Number is: odd\n"
}
}
fun isPrime(num: Int): String
{
var flag = false
for (i in 2..num / 2 + 1)
{
if (num % i == 0)
{
flag = true
break
}
}
return when (flag)
{
false -> "Number is: prime\n"
true -> "Number is: not prime\n"
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textButton = findViewById<Button>(R.id.printButton)
val entry = findViewById<TextView>(R.id.entry)
val textView = findViewById<TextView>(R.id.textView)
textButton?.setOnClickListener {
textView.text = ""
val num = entry.text.toString().toInt()
var toPrint = ""
toPrint += oddOrEven(num) + isPrime(num)
textView.text = toPrint
}
}
}
Here is XML
<TextView
android:id="#+id/text1"
android:layout_width="266dp"
android:layout_height="62dp"
android:layout_marginTop="28dp"
android:gravity="center"
android:text="Podaj liczbÄ™"
android:textSize="36sp"
app:fontFamily="#font/anonymous_pro"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.496"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="#+id/entry"
android:layout_width="291dp"
android:layout_height="57dp"
android:ems="10"
android:gravity="center"
android:inputType="number"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/text1" />
<Button
android:id="#+id/printButton"
android:layout_width="180dp"
android:layout_height="42dp"
android:layout_marginTop="36dp"
android:text="Ok"
android:textColor="#000000"
android:textColorHint="#000000"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/entry" />
<TextView
android:id="#+id/textView"
android:layout_width="0dp"
android:layout_height="400dp"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginTop="52dp"
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/printButton"
app:layout_constraintVertical_bias="0.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
I hope my problem is clear. I will answer any question.
Without entering a number entry.text.toString() will return "" (an empty string) and "".toInt() results in an exception similar or equal to this:
Exception in thread "main" java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString (NumberFormatException.java:65)
at java.lang.Integer.parseInt (Integer.java:592)
at java.lang.Integer.parseInt (Integer.java:615)
I suggest replacing toInt() with toIntOrNull() (which will also prevent crashes if you enter e.g. a letter or a decimal number) and modify your click listener as following:
textButton?.setOnClickListener {
textView.text = ""
val num = entry.text.toString().toIntOrNull()
val toPrint = if (num != null) {
oddOrEven(num) + isPrime(num)
} else {
""
}
textView.text = toPrint
}

Root casue for NoSuchElementException: Collection is empty

I am facing Crash in Firebase Crashlytics on bind method but unable to find the root cause. I have added a Null check but still, the issue not fixed.
Firebase Crash Report
Fatal Exception: java.util.NoSuchElementException: Collection is empty.
at kotlin.collections.CollectionsKt___CollectionsKt.first(_Collections.kt:184)
at com.transferhome.contacts.ContactsAdapter.bind(ContactsAdapter.kt:25)
at com.transferhome.contacts.ContactsBaseAdapter.onBindViewHolder(ContactsBaseAdapter.kt:38)
at com.transferhome.contacts.ContactsBaseAdapter.onBindViewHolder(ContactsBaseAdapter.kt:19)
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:6781)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:6823)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:5752)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6019)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5858)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5854)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2230)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1557)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1517)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:612)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924)
at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3336)
at android.view.View.measure(View.java:22216)
at androidx.constraintlayout.widget.ConstraintLayout.internalMeasureChildren(ConstraintLayout.java:1227)
at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1572)
at android.view.View.measure(View.java:22216)
at androidx.viewpager.widget.ViewPager.onMeasure(ViewPager.java:1638)
at android.view.View.measure(View.java:22216)
at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1676)
at android.view.View.measure(View.java:22216)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6671)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:733)
at com.google.android.material.appbar.HeaderScrollingViewBehavior.onMeasureChild(HeaderScrollingViewBehavior.java:95)
at com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:1556)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:803)
at android.view.View.measure(View.java:22216)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6671)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:143)
at android.view.View.measure(View.java:22216)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6671)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1539)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:823)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:702)
at android.view.View.measure(View.java:22216)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6671)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at android.view.View.measure(View.java:22216)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6671)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1539)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:823)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:702)
at android.view.View.measure(View.java:22216)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6671)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:831)
at android.view.View.measure(View.java:22216)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2589)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1631)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1885)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1515)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7266)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:981)
at android.view.Choreographer.doCallbacks(Choreographer.java:790)
at android.view.Choreographer.doFrame(Choreographer.java:721)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:967)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:101)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7529)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
ContactsAdapter.kt
class ContactsAdapter ( private val onItemSelect: (contact: Contact) -> Unit )
: ContactsBaseAdapter<ItemContactFavListBinding>() {
var contactFavSignal = MutableLiveData<Contact>().apply { }
override fun bind(contact: Contact, holder: ContactVH<ItemContactFavListBinding>) {
val binding = holder.binding
binding.contactName.text = contact.name
binding.contactNumber.text = contact.numbers.first().phone
binding.imgContactHeart.setOnClickListener(View.OnClickListener {
val backgroundImageName = (binding.imgContactHeart.getTag()).toString()
if(backgroundImageName.equals("2")) {
binding.imgContactHeart.setImageResource(R.drawable.heart_selected_yellow)
EventBus.getDefault().post(CustomEvent(contact,true));
binding.imgContactHeart.setTag("1")
binding.imgContactHeart.setImageResource(R.drawable.heart_selected_yellow)
}else if(backgroundImageName.equals("1")) {
binding.imgContactHeart.setImageResource(R.drawable.heart_selected_yellow)
EventBus.getDefault().post(CustomEvent(contact,false));
binding.imgContactHeart.setTag("2")
binding.imgContactHeart.setImageResource(R.drawable.heart)
}
})
var num = contact.numbers.first().phone
if(num.contains(" "))
num = num.replace(" ","")
if(num.startsWith("0"))
num = num.removePrefix("0")
if(num.startsWith("+"))
num = num.removePrefix("+")
var isFav:Boolean=false
for (x in 0..FavoritesFragment.listFavNumbers.size-1)
{
var num2 = FavoritesFragment.listFavNumbers[x]
if(num2.endsWith(num))
isFav=true
}
if(isFav)
{
binding.imgContactHeart.setTag("1")
binding.imgContactHeart.setImageResource(R.drawable.heart_selected_yellow)
}
else
{
binding.imgContactHeart.setTag("2")
binding.imgContactHeart.setImageResource(R.drawable.heart)
}
showContactProfileImage(contact, binding.contactImage)
holder.binding.root.setOnClickListener {
onItemSelect.invoke(contact)
}
}
override fun provideBinding(inflater: LayoutInflater, parent: ViewGroup): ItemContactFavListBinding {
return ItemContactFavListBinding.inflate(inflater, parent, false)
}
}
ContactBaseAdater.kt
abstract class ContactsBaseAdapter<T : ViewDataBinding> :
PagedListAdapter<Contact, ContactsBaseAdapter.ContactVH<T>>(object : DiffUtil.ItemCallback<Contact>() {
override fun areItemsTheSame(oldItem: Contact, newItem: Contact): Boolean {
return oldItem.name == newItem.name || oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Contact, newItem: Contact): Boolean {
return oldItem.name == newItem.name
}
} ) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactVH<T> {
return ContactVH(provideBinding(LayoutInflater.from(parent.context), parent))
}
override fun onBindViewHolder(holder: ContactVH<T>, position: Int) {
val contact = getItem(position)
if (contact != null) {
bind(contact, holder)
}
}
abstract fun bind(contact: Contact, holder: ContactVH<T>)
abstract fun provideBinding(inflater: LayoutInflater, parent: ViewGroup): T
class ContactVH<T : ViewDataBinding>(val binding: T) : RecyclerView.ViewHolder(binding.root)
fun showContactProfileImage(contact: Contact, imageHolder: ImageView?) {
UiUtils.showContactProfileImage(contact, imageHolder)
}
}
item_contact_fav_list.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<RelativeLayout android:layout_width="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="#+id/contact_image"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_alignParentLeft="true"
android:contentDescription="#null"/>
<LinearLayout android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="vertical"
android:weightSum="2"
android:layout_toLeftOf="#+id/img_contact_heart"
android:layout_toRightOf="#+id/contact_image"
android:layout_marginLeft="5dp">
<TextView
android:id="#+id/contact_name"
android:textSize="16sp"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="1"
android:singleLine="true"
android:ellipsize="middle"
android:focusable="false"
android:textColor="#color/colorBlack"
android:layout_marginTop="3dp"
tools:text="Contact Name"/>
<TextView
android:id="#+id/contact_number"
android:textSize="15sp"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="1"
android:singleLine="true"
android:textColor="#android:color/darker_gray"
tools:text="Contact Name"
android:layout_marginLeft="5dp"
/>
</LinearLayout>
<ImageView
android:layout_alignParentRight="true"
android:id="#+id/img_contact_heart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/heart"
android:layout_centerVertical="true"
/>
</RelativeLayout>
</layout>
Let's take a look at the stacktrace:
Fatal Exception: java.util.NoSuchElementException: Collection is empty.
at kotlin.collections.CollectionsKt___CollectionsKt.first(_Collections.kt:184)
at com.transferhome.contacts.ContactsAdapter.bind(ContactsAdapter.kt:25)
Most probably, the line referred to is this one:
binding.contactNumber.text = contact.numbers.first().phone
the call to first() in particular. If contact.numbers is empty, first() will throw a NoSuchElementException. Your code does not handle this case.
The easiest solution would be to set the text to null if there are no numbers:
binding.contactNumber.text = contact.numbers.firstOrNull()?.phone
Why would you add a nullcheck if the exception was not a NullPointerException?