setonkeylistner not working in my Fragment - android

I almost wrote a book application and I want to pass volume key events,
when user press a volume key goes to next page but I've got issue nextPage method is not running when up volume button is being pressed.
I used setonkeylistner in OnViewCreated but it's not working and I don't know any other way.
my project has only one activity(MainActivity) with many Fragments
sorry for my bad English
here is my Fragment code
class ShowPoemBodyFragment : Fragment(){
private val viewModel: PoemBodyViewModel by viewModels()
private var binding: FragmentShowPoemsBinding by autoCleared()
private var shared: SharedPreferences? = null
private var mId by Delegates.notNull<Int>()
private var fId by Delegates.notNull<Int>()
private var size by Delegates.notNull<Int>()
private var sharedBackground: SharedPreferences? = null
private var sharedImage: SharedPreferences? = null
private val poemAdapter = PoemBodyAdapter()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentShowPoemsBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.root.setOnKeyListener(View.OnKeyListener { _, keyCode, _ ->
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
nextPage()
return#OnKeyListener true
} else
false
})
binding.poemBodyRecyclerView.adapter = poemAdapter
fId = requireArguments().getString("id")?.toIntOrNull()!!
size = requireArguments().getString("size")?.toIntOrNull()!!
viewModel.getPoemById(fId.toString())
Hawk.put("lastSeen", fId.toString())
Log.i("pomeid", fId.toString())
mId = fId
obserViewModel()
var fav: Int? = null
binding.markFavShowPoem.setOnClickListener {
if (fav == 1) {
fav = 0
binding.markFavShowPoem.setImageResource(R.drawable.ic_star)
} else {
fav = 1
binding.markFavShowPoem.setImageResource(R.drawable.ic_star_bold)
shared = context?.getSharedPreferences("shared_fav", Context.MODE_PRIVATE)
val editor: SharedPreferences.Editor = shared!!.edit()
editor.putString("id", mId.toString())
Toast.makeText(context, "به لیست علاقه مندی ها اضافه شد", Toast.LENGTH_SHORT).show()
editor.apply()
}
}
binding.textOption.setOnClickListener {
SubDialogFragmentPopUp().show(
requireActivity().supportFragmentManager,
"popUp"
)
}
if (mId == mId + size) {
binding.nextPage.visibility = View.INVISIBLE
} else
binding.nextPage.setOnClickListener {
nextPage()
}
if (mId == fId) {
binding.previousPage.visibility = View.INVISIBLE
}
else {
binding.previousPage.setOnClickListener{
mId -= 1
viewModel.getPoemById(mId.toString())
Hawk.put("lastSeen", mId.toString())
Log.i("pomeid", mId.toString())
obserViewModel()
}
}
//Set Background Text
sharedBackground =
context?.getSharedPreferences("shared_background_color", Context.MODE_PRIVATE)
val bbcg: Boolean = sharedBackground!!.getBoolean("bbcg", false)
if (bbcg) {
val color: Int = sharedBackground!!.getInt("background_color", 0)
view.background_show_poem_body.setBackgroundColor(color)
}
//Set Image
sharedImage = context?.getSharedPreferences("imagePoem", Context.MODE_PRIVATE)
val image: String? = sharedImage!!.getString("image", "")
context?.let {
Glide.with(this)
.load(image)
.centerCrop()
.error(R.drawable.ic_launcher_foreground)
.into(view.image_poem_adapter)
}
}
fun obserViewModel() {
viewModel.poemBody.observe(viewLifecycleOwner, Observer {
when (it.status) {
SUCCESS -> it.data?.let {
poemAdapter.updatePoems(it)
}
ERROR -> {
it.message?.let { requireActivity().toast(it) }
}
LOADING -> {
}
}
})
}
private fun nextPage(){
mId += 1
viewModel.getPoemById(mId.toString())
Hawk.put("lastSeen", mId.toString())
Log.i("pomeid", mId.toString())
obserViewModel()
}
}

override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP){
// Do something
}
return super.onKeyUp(keyCode, event)
}
Try this and see

You need to use ACTION_MEDIA_BUTTON to listen for volume button presses. You will have to Override the key event. Here is a link that explains its use and why

Related

Problem with local database and remote database data when updating local data

I'm doing a practise with the rick and morty api and I have two fragments, one with a recycleview with the characters and another one with the detail of the character, where also you can update some of the values.
My problem is that when I update a value, if I go back to the main fragment with the recycle view, that value is updated but when I go back again to the detail, the value is again the original one. I don't know how to fix it.
This is my detail fragment:
class GetCharacterDetail: Fragment() {
private var binding: CharacterDetailFragmentBinding by autoCleared()
private val viewModel: CharacterDetailViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = CharacterDetailFragmentBinding.inflate(inflater, container, false)
val edit = binding.editButton
val save = binding.saveBotton
changeStateOnEdit(edit, save)
save.setOnClickListener {
val gender = binding.characterGenderText.text.toString()
val status = binding.characterStatusText.text.toString()
val species = binding.characterSpeciesText.text.toString()
updateCharacterDetails(gender, status, species, edit, save)
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
arguments?.getInt("id")?.let { viewModel.start(it) }
setupObservers()
}
private fun setupObservers() {
viewModel.character.observe(viewLifecycleOwner, Observer {
when (it.status) {
Status.StatusEnum.SUCCESS -> {
bindCharacter(it.data!! as CharacterEntity)
binding.progressBar.visibility = View.GONE
binding.characterDetailLayout.visibility = View.VISIBLE
}
Status.StatusEnum.ERROR ->
Toast.makeText(activity, it.message, Toast.LENGTH_SHORT).show()
Status.StatusEnum.LOADING -> {
binding.progressBar.visibility = View.VISIBLE
binding.characterDetailLayout.visibility = View.GONE
}
}
})
}
private fun bindCharacter(character: CharacterEntity) {
if (character != null) {
binding.characterName.text = character.name
binding.characterSpeciesText.setText(character.species)
binding.characterStatusText.setText(character.status)
binding.characterGenderText.setText(character.gender)
Glide.with(binding.root)
.load(character.image)
.into(binding.characterImage)
}
}
private fun changeStateOnEdit(edit: ImageButton, save: MaterialButton) {
edit.setOnClickListener(View.OnClickListener {
edit.isVisible = false
binding.characterGender.isEnabled = true
binding.characterSpecies.isEnabled = true
binding.characterStatus.isEnabled = true
save.isVisible = true
})
}
private fun updateCharacterDetails(gender: String, status: String, species: String,edit: ImageButton, save: MaterialButton) {
viewModel.updateCharacterDetails(gender, status, species)
viewModel.character.observe(viewLifecycleOwner, Observer {
when (it.status) {
Status.StatusEnum.SUCCESS -> {
Toast.makeText(activity, "Personaje actualizado correctamente", Toast.LENGTH_SHORT).show()
edit.isVisible = true
binding.characterGender.isEnabled = false
binding.characterSpecies.isEnabled = false
binding.characterStatus.isEnabled = false
save.isVisible = false
bindCharacter(it.data!!)
}
Status.StatusEnum.ERROR ->
Toast.makeText(activity, it.message, Toast.LENGTH_SHORT).show()
Status.StatusEnum.LOADING -> {
binding.progressBar.visibility = View.VISIBLE
binding.characterDetailLayout.visibility = View.GONE
}
}
})
}
}
And this is my ViewModel:
class CharacterDetailViewModel #Inject constructor(
private val repository: CharacterRepository
) : ViewModel() {
private val idCharacter = MutableLiveData<Int>()
val character = idCharacter.switchMap { id ->
repository.getCharacter(id)
}
fun updateCharacterDetails(gender: String, status: String, species: String) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
val id = idCharacter.value ?: return#withContext
repository.updateCharacterDetail(id, gender, status, species)
}
}
}
fun start(id: Int) {
idCharacter.value = id
}
}
Herew is the repository:
class CharacterRepository #Inject constructor(
private val api : CharacterService,
private val characterDao: CharacterDao
) {
fun getAllCharacters() = getEntitiesOperation(
databaseQuery = { characterDao.getAllCharacters() },
networkCall = { api.getCharacters() },
saveCallResult = { characterDao.insertAll(it.results) }
)
fun getCharacter(id: Int) = getEntitiesOperation(
databaseQuery = { characterDao.getCharacter(id) },
networkCall = { api.getCharacter(id) },
saveCallResult = { characterDao.insert(it) }
)
fun deleteCharacter(id: Int) = characterDao.deleteCharacter(id)
fun updateCharacterDetail(id: Int, gender:String, status:String, species:String) =
characterDao.updateCharacterDetail(id, gender, status, species)
}
And the function I use to take the data from local database if there is data in it. Here is where I think it is the problem since I think that something has to be recovered wrong and that localData is null and then the method look for the data on the api
fun <T, A> getEntitiesOperation(databaseQuery: () -> LiveData<T>,
networkCall: suspend () -> Status<A>,
saveCallResult: suspend (A) -> Unit):
LiveData<Status<T>> = liveData(Dispatchers.IO) {
emit(Status.loading())
val source = databaseQuery.invoke().map { Status.success(it) }
emitSource(source)
val localData = source.value?.data
if (localData != null) return#liveData
val responseStatus = networkCall.invoke()
if (responseStatus.status == StatusEnum.SUCCESS) {
saveCallResult(responseStatus.data!!)
} else if (responseStatus.status == StatusEnum.ERROR) {
emit(Status.error(responseStatus.message!!))
emitSource(source)
}
}
I've been with this problem all day and I don't know what to do or how to fix it. Thank you in advance for the help

java.lang.NumberFormatException: For input string: "null" at jdk.internal.math.FloatingDecimal.readJavaFormatString

I tried to pass data from DetailActivity to PaymentActivity, but I got this error
Here is the error and here is PaymentFragment after I comment textview that contain number format on it here it is. The user data pass successfully, but the product data unsuccess to pass I think. So how to pass the product data from DetailActivity to PaymentActivity
here is my code:
Helpers File
object Helpers {
fun getDefaultGson(): Gson {
return GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setDateFormat(Cons.DATE_FORMAT_SERVER)
.registerTypeAdapter(Date::class.java, JsonDeserializer { json, _, _ ->
val formatServer = SimpleDateFormat(Cons.DATE_FORMAT_SERVER, Locale.ENGLISH)
formatServer.timeZone = TimeZone.getTimeZone("UTC")
formatServer.parse(json.asString)
})
.registerTypeAdapter(Date::class.java, JsonSerializer<Date> { src, _, _ ->
val format = SimpleDateFormat(Cons.DATE_FORMAT_SERVER, Locale.ENGLISH)
format.timeZone = TimeZone.getTimeZone("UTC")
if (src != null) {
JsonPrimitive(format.format(src))
} else {
null
}
})
.create()
}
fun Throwable.getErrorBodyMessage(): String {
return if (this is HttpException) {
val errorCode = this.code()
if (errorCode == 405) {
"Method yang digunakan salah"
} else if (errorCode == 503) {
"Error Server"
} else {
val parseErrorBody = this.response()?.errorBody()!!.parseErrorBody()
if (parseErrorBody?.meta?.message == null) {
"Permintaan anda belum berhasil di proses. Silakan coba kembali"
} else {
parseErrorBody?.meta?.message.toString()
}
}
} else if (this is ConnectException || this is UnknownHostException) {
"Maaf Anda sedang Offline. Silakan coba kembali"
} else {
return if (this.message == null)
"Permintaan anda belum berhasil di proses. Silakan coba kembali"
else if (this.message.equals(""))
""
else
this.message!!
}
}
fun ResponseBody.parseErrorBody(): Wrapper<*>? {
val gson = Gson()
val adapter = gson.getAdapter(Wrapper::class.java)
try {
return adapter.fromJson(string())
} catch (e: IOException) {
e.printStackTrace()
}
return null
}
fun TextView.formatPrice(value: String) {
this.text = getCurrencyIdr(java.lang.Double.parseDouble(value))
}
fun getCurrencyIdr(price: Double): String {
val format = DecimalFormat("#,###,###")
return "Rp. " + format.format(price).replace(",".toRegex(), ".")
}
fun Long.convertLongToTime(formatTanggal: String): String {
val date = Date(this)
val format = SimpleDateFormat(formatTanggal)
return format.format(date)
}
}
PaymentFragment:
class PaymentFragment : Fragment(), PaymentContract.View {
var progressDialog: Dialog? = null
var total : Int = 0
lateinit var presenter: PaymentPresenter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_payment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
(activity as DetailActivity?)!!.toolbarPayment()
var data = arguments?.getParcelable<Data>("data")
initView(data)
initView()
presenter = PaymentPresenter(this)
}
private fun initView(data: Data?) {
tvTitle.text = data?.name
tvPrice.formatPrice(data?.price.toString())
Glide.with(requireContext())
.load(data?.picturePath)
.into(tvPoster)
tvNameItem.text = data?.name
tvHarga.formatPrice(data?.price.toString())
if (!data?.price.toString().isNullOrEmpty()) {
var totalTax = data?.price?.div(10)
tvTax.formatPrice(totalTax.toString())
total = data?.price!! + totalTax!! + 50000
tvTotalPrice.formatPrice(total.toString())
} else {
tvPrice.text = "IDR. 0"
tvTax.text = "IDR. 0"
tvTotalPrice.text = "IDR. 0"
total = 0
}
var user = IcaCraft.getApp().getUser()
var userResponse = Gson().fromJson(user, User::class.java)
tvNameDeliver.text = userResponse?.name
tvPhoneDeliver.text = userResponse?.phoneNumber
tvAddressDeliver.text = userResponse?.address
tvHouseNo.text = userResponse?.houseNumber
tvCityDeliver.text = userResponse?.postalCode
btn_CheckoutNow.setOnClickListener {
presenter.getCheckout(
data?.id.toString(),
userResponse?.id.toString(),
"1",
total.toString(), it
)
}
}
private fun initView() {
progressDialog = Dialog(requireContext())
val dialogLayout = layoutInflater.inflate(R.layout.dialog_loader, null)
progressDialog?.let {
it.setContentView(dialogLayout)
it.setCancelable(false)
it.window?.setBackgroundDrawableResource(android.R.color.transparent)
}
}
override fun onCheckoutSuccess(checkoutResponse: CheckoutResponse, view: View) {
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse(checkoutResponse.paymentUrl)
startActivity(i)
Navigation.findNavController(view).navigate(R.id.action_fragmentPayment_to_fragmentPaymentSuccess)
}
override fun onCheckoutFailed(message: String) {
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
}
override fun showLoading() {
progressDialog?.show()
}
override fun dismissLoading() {
progressDialog?.dismiss()
}
}
DetailActivity:
class DetailFragment : Fragment() {
var data:Data?= null
var bundle:Bundle?= null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_detail, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
(activity as DetailActivity?)!!.toolbarDetail()
arguments?.let {
DetailFragmentArgs.fromBundle(it).data?.let {
initView(it)
}
}
btnOrderNow.setOnClickListener { view ->
Navigation.findNavController(view).navigate(R.id.action_fragmentDetail_to_fragmentPayment, bundle)
}
}
private fun initView(data: Data?) {
data?.let {
// bundle = bundleOf("data" to data)
Glide.with(requireContext())
.load(it.picturePath)
.into(ivPoster)
tvTitle.text = it.name
tvDescription.text = it.description
tvMaterials.text = it.materials
tvTotal.formatPrice(it.price.toString())
}
}
}
I am not sure about the bug in the code but there are many other ways you can share data in between activities. Try using viewModel to share data between your activities. SharedPreferences are another way too. There are plenty of other ways but I think viewModel is the best way. I personally find it difficult to transfer data between fragments using arguments. So Good Luck!
Your code is a bit complicated but the error is understandable. It says "You can not parse null to double". You define "price" as "Int?", so it can be "null" and when it is null, app crashes. This can be solved by doing the followings:
1- You can define your price "not null" and set a default value as 0.
#Expose
#SerializedName("price")
val price: Int = 0
2- If you do not want to change your data class, you need to control what to pass to "parseDouble()" method.
fun TextView.formatPrice(value: String) {
if (value != null) {
this.text = getCurrencyIdr(java.lang.Double.parseDouble(value))
} else {
this.text = getCurrencyIdr(0.0) // we didn't parse the null
}
}
If one of these individually will not solve your error try both, it has to solve it.

Jitsi Meet mute audio and video issue

i'm trying to mute everyone in the conference call to prevent trolling/spamming, but the problem is allowed to umute audio and video at anytime even tho i'm setting the options to be muted by default. P.S : if you have any idea that may help me to prevent this issue from going on into the conference call please just write it down.
MeetingUtils.kt
object MeetingUtils {
private var isMuted: Boolean = true
fun startMeeting(context: Context, meetingCode: String) {
val serverUrl = URL(context.getString(R.string.app_server_url))
val defaultOptions = JitsiMeetConferenceOptions.Builder()
.setServerURL(serverUrl)
.setWelcomePageEnabled(false)
.setAudioMuted(isMuted)
.setVideoMuted(true)
.setFeatureFlag("invite.enabled", false)
.setFeatureFlag("live-streaming.enabled", false)
.setFeatureFlag("meeting-name.enabled", false)
.setFeatureFlag("call-integration.enabled", false)
.setFeatureFlag("recording.enabled", false)
.build()
JitsiMeet.setDefaultConferenceOptions(defaultOptions)
val options = JitsiMeetConferenceOptions.Builder()
.setRoom(meetingCode)
.setUserInfo(null)
val sharedPrefData= SharedPrefData(context)
val currentUser = FirebaseAuth.getInstance().currentUser
if (sharedPrefData.getSkip().equals("Skip"))
{
val userInfoBundle = bundleOf(
"displayName" to "User Not Sign in",
"email" to "Please Sign In",
"avatarURL" to R.drawable.ic_account
)
options.setUserInfo(JitsiMeetUserInfo(userInfoBundle))
}
else
{
if (currentUser != null) {
val userInfoBundle = bundleOf(
"displayName" to sharedPrefData.getName(),
"email" to sharedPrefData.getEmail(),
"avatarURL" to sharedPrefData.getImage()
)
options.setUserInfo(JitsiMeetUserInfo(userInfoBundle))
}
val userInfoBundle = bundleOf(
"displayName" to sharedPrefData.getName() ,
"email" to sharedPrefData.getEmail(),
"avatarURL" to "http://graph.facebook.com/${sharedPrefData.getAuthId()}/picture?type=square"
)
options.setUserInfo(JitsiMeetUserInfo(userInfoBundle))
}
JitsiMeetActivity.launch(context, options.build())
}
}
HomeFragment.kt
class HomeFragment : Fragment() {
private var binding: FragmentHomeBinding? = null
private val minMeetingCodeLength = 10
private var currentUser: FirebaseUser? = null
var email:String?=null
var firstName:String?=null
var lastName:String?=null
var profileImage:String?=null
private val viewModel by viewModel<MainViewModel>()
lateinit var auth: FirebaseAuth
private var sharedPrefData: SharedPrefData?=null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
binding=FragmentHomeBinding.inflate(inflater,container,false)
val view = binding!!.root
onCreateMeetingCodeChange()
onCopyMeetingCodeFromClipboardClick()
onShareMeetingCodeClick()
onJoinMeetingClick()
onCreateMeetingClick()
onMeetingToggleChange()
val mAdView: AdView = view.findViewById(R.id.adView)
val adRequest = AdRequest.Builder().build()
mAdView.loadAd(adRequest)
return view
}
companion object{
fun newInstance(text: String?): HomeFragment? {
val f = HomeFragment()
val b = Bundle()
b.putString("msg", text)
f.arguments = b
return f
}
}
private fun onMeetingToggleChange() {
binding?.tgMeeting?.addOnButtonCheckedListener { toggleGroup, checkedId, isChecked ->
if (isChecked) {
when (checkedId) {
R.id.btnToggleJoinMeeting -> {
binding?.groupCreateMeeting?.makeGone()
binding?.groupJoinMeeting?.makeVisible()
}
R.id.btnToggleCreateMeeting -> {
binding?.groupJoinMeeting?.makeGone()
binding?.groupCreateMeeting?.makeVisible()
val meetingCode = generateMeetingCode()
binding?.etCodeCreateMeeting?.setText(meetingCode)
}
}
}
}
}
private fun onCreateMeetingCodeChange() {
binding?.tilCodeCreateMeeting?.etCodeCreateMeeting?.doOnTextChanged { text, start, before, count ->
if (count >= minMeetingCodeLength) binding?.tilCodeCreateMeeting!!.error = null
}
}
private fun generateMeetingCode(): String {
val allowedChars = ('A'..'Z') + ('a'..'z')
return (1..10)
.map { allowedChars.random() }
.joinToString("")
}
private fun onCopyMeetingCodeFromClipboardClick() {
binding?.tilCodeJoinMeeting?.setEndIconOnClickListener {
val clipboardText = activity?.getTextFromClipboard()
if (clipboardText != null) {
binding?.etCodeJoinMeeting?.setText(clipboardText)
activity?.toast(getString(R.string.main_meeting_code_copied))
} else {
activity?.toast(getString(R.string.main_empty_clipboard))
}
}
}
private fun onShareMeetingCodeClick() {
binding?.tilCodeCreateMeeting?.setEndIconOnClickListener {
if (binding?.etCodeCreateMeeting?.text.toString().length >= minMeetingCodeLength) {
binding!!.tilCodeCreateMeeting.error = null
activity?.startShareTextIntent(
getString(R.string.main_share_meeting_code_title),
"Meeting Code: "+binding!!.etCodeCreateMeeting.text.toString()+"\n "+
getString(R.string.profile_share_app_text, activity!!. applicationContext.packageName)
)
} else {
binding!!.tilCodeCreateMeeting.error =
getString(R.string.main_error_meeting_code_length, minMeetingCodeLength)
}
}
}
private fun onJoinMeetingClick() {
binding?.btnJoinMeeting?.setOnClickListener {
if (binding!!.etCodeJoinMeeting.text.toString().length >= minMeetingCodeLength) {
joinMeeting()
} else {
activity?.toast(getString(R.string.main_error_meeting_code_length, minMeetingCodeLength))
}
}
}
private fun joinMeeting() {
activity?.let {
MeetingUtils.startMeeting(
it,
binding?.etCodeJoinMeeting?.text.toString())
} // Start Meeting
viewModel.addMeetingToDb(
Meeting(
binding?.etCodeJoinMeeting?.text.toString(),
System.currentTimeMillis()
)
) // Add meeting to db
}
private fun onCreateMeetingClick() {
binding?.btnCreateMeeting?.setOnClickListener {
if (binding!!.etCodeCreateMeeting.text.toString().length >= minMeetingCodeLength) {
createMeeting()
} else {
activity?.toast(getString(R.string.main_error_meeting_code_length, minMeetingCodeLength))
}
}
}
private fun createMeeting() {
activity?.let {
MeetingUtils.startMeeting(
it,
binding?.etCodeCreateMeeting?.text.toString()
)
} // Start Meeting
viewModel.addMeetingToDb(
Meeting(
binding?.etCodeCreateMeeting?.text.toString(),
System.currentTimeMillis()
)
) // Add meeting to db
}
}

Exo player fast scroll playing video sound mixed

I am using exoplayer for playing videos .And for this we are used Fragment instance with pagerstateadapter and viewpager2.
But when scroll fast previous played video sound listen in background as well as in screen video means mix the sound in same video player.
Please help me how to solve this.
1.State adapter
class StoriesPagerAdapter(
fragment: Fragment,
val onClick1: VideoItemAdapter.OnItemClicked?,
val onlikeClick: VideoItemAdapter.OnLikeCLicked?,
val onFollowClick: VideoItemAdapter.OnFollowCLicked?,
val ontrendingClick: VideoItemAdapter.OnTrendCLicked?,
val oniconCLick: VideoItemAdapter.OnIconClick?) : FragmentStateAdapter(fragment) {
val dataList:MutableList<Gettrendingvideos.Data.Postlist>=mutableListOf()
override fun getItemCount(): Int {
return dataList.size
}
fun addAll(movies: MutableList<Gettrendingvideos.Data.Postlist>) {
for (movie in movies) {
add(movie)
}
}
fun add(moive: Gettrendingvideos.Data.Postlist) {
dataList.add(moive)
notifyItemInserted(dataList.size - 1)
}
override fun createFragment(position: Int): Fragment {
return StoryViewFragment.newInstance(
onClick1,
onlikeClick,
onFollowClick,
ontrendingClick,
oniconCLick,
dataList[position]
)
}}
2 Fragment
class StoryViewFragment : Fragment(), CommentFragment.onCommentCountIncrease {
private var storyUrl: String? = null
private var storiesDataModel: Gettrendingvideos.Data.Postlist? = null
lateinit var mView: View
private var simplePlayer: SimpleExoPlayer? = null
private var cacheDataSourceFactory: CacheDataSourceFactory? = null
private val simpleCache = MainApplication.simpleCache
private var toPlayVideoPosition: Int = -1
lateinit var viewModel: MainViewModel
lateinit var preferences: SecurePreferences
private var bool: Boolean? = false
var onItemClick: VideoItemAdapter.OnItemClicked? = null
var onlikeCLicked: VideoItemAdapter.OnLikeCLicked? = null
var onFollowCLicked: VideoItemAdapter.OnFollowCLicked? = null
var onTrendCLicked: VideoItemAdapter.OnTrendCLicked? = null
var onIconClick: VideoItemAdapter.OnIconClick? = null
lateinit var huserId: String
lateinit var token: String
companion object {
fun newInstance(
itemClicked: VideoItemAdapter.OnItemClicked?,
likeCLicked: VideoItemAdapter.OnLikeCLicked?,
onFollowCLicked: VideoItemAdapter.OnFollowCLicked?,
onTrendCLicked: VideoItemAdapter.OnTrendCLicked?,
onIconClick: VideoItemAdapter.OnIconClick?,
storiesDataModel: Gettrendingvideos.Data.Postlist
) = StoryViewFragment()
.apply {
arguments = Bundle().apply {
putParcelable(Constants.KEY_STORY_DATA, storiesDataModel)
}
this.onItemClick = itemClicked
this.onlikeCLicked = likeCLicked
this.onFollowCLicked = onFollowCLicked
this.onTrendCLicked = onTrendCLicked
this.onIconClick = onIconClick
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
mView = inflater.inflate(
R.layout.layout_main,
container,
false
)
return mView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this, ViewModelFactory(RetrofitBuilder.apiService))
.get(MainViewModel::class.java)
preferences =
SecurePreferences(
requireActivity(),
AppConstants.preferenceName,
AppConstants.USER_DETAILS,
true
)
storiesDataModel = arguments?.getParcelable(Constants.KEY_STORY_DATA)
setData()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (!preferences.getString(AppConstants.USER_ID).equals(null)) {
huserId = preferences.getString(AppConstants.USER_ID)!!
Log.d("TAG", "onActivityCreated: $huserId")
}
}
#SuppressLint("SetTextI18n")
private fun setData() {
Log.d("TAG", "setData: $storiesDataModel")
mView.textview2.text = storiesDataModel?.user_name
mView.like_count.text = storiesDataModel?.total_likes.toString()
comment_count.text = storiesDataModel?.total_comments.toString()
mView.textview.text = storiesDataModel?.type
Glide.with(this).load(storiesDataModel?.user_profile_pic).placeholder(R.drawable.profile).into(mView.image)
if (storiesDataModel?.is_like == 0) {
mView.imageView4.setImageResource(R.drawable.ic_like)
} else {
mView.imageView4.setImageResource(R.drawable.ic_like_red)
}
if (storiesDataModel?.is_following!! == 0) {
mView.textview3.text = "Follow"
} else {
mView.textview3.text = "Following"
}
if (storiesDataModel?.user_id.toString()==preferences.getString(AppConstants.USER_ID)) {
mView.textview3.visibility = View.GONE
}
image.setOnClickListener {
if (preferences.getString(AppConstants.token).equals(null)) {
MainActivity().show(childFragmentManager, "")
} else {
preferences.put(
AppConstants.OtherProfile_UserId,
storiesDataModel?.user_id.toString()
)
}
}
val simplePlayer = getPlayer()
player_view_story.player = simplePlayer player_view_story.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL)
simplePlayer?.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING)
storyUrl = storiesDataModel?.video_url
prepareMedia(storiesDataModel)
}
override fun onPause() {
pauseVideo()
super.onPause()
}
override fun onResume() {
restartVideo()
super.onResume()
}
override fun onDestroy() {
releasePlayer()
super.onDestroy()
}
private fun pausePlayer() {
simplePlayer?.setPlayWhenReady(false)
simplePlayer?.getPlaybackState()
}
private fun startPlayer() {
simplePlayer?.setPlayWhenReady(true)
simplePlayer?.getPlaybackState()
}
private val playerCallback: Player.EventListener? = object : Player.EventListener {
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
}
override fun onPlayerError(error: com.google.android.exoplayer2.ExoPlaybackException?) {
super.onPlayerError(error)
}
}
private fun prepareVideoPlayer() {
simplePlayer = ExoPlayerFactory.newSimpleInstance(context)
cacheDataSourceFactory = CacheDataSourceFactory(
simpleCache,
DefaultHttpDataSourceFactory(
Util.getUserAgent(
context,
"exo"
)
)
)
}
private fun getPlayer(): SimpleExoPlayer? {
if (simplePlayer == null) {
prepareVideoPlayer()
}
return simplePlayer
}
private fun prepareMedia(datamodel: Gettrendingvideos.Data.Postlist?{
val uri = Uri.parse(datamodel?.video_url)
simplePlayer.repeatMode = Player.REPEAT_MODE_ONE
simplePlayer.playWhenReady = true
if (storiesDataModel!!.type == "following") {
following_page.typeface = Typeface.DEFAULT_BOLD
trending_page.setTypeface(null, Typeface.NORMAL)
} else {
following_page.setTypeface(null, Typeface.BOLD)
}
if (storiesDataModel.type == "treading") {
trending_page.typeface = Typeface.DEFAULT_BOLD
following_page.setTypeface(null, Typeface.NORMAL)
} else {
trending_page.setTypeface(null, Typeface.BOLD)
}
if (simplePlayer.playWhenReady == true) {
}
simplePlayer.addListener(playerCallback)
toPlayVideoPosition = -1
}
private fun setArtwork(drawable: Drawable, playerView: PlayerView) {
playerView.useArtwork = true
playerView.defaultArtwork = drawable
}
private fun playVideo() {
simplePlayer.playWhenReady = true
}
private fun restartVideo() {
if (simplePlayer == null) {
prepareMedia(storiesDataModel)
} else {
simplePlayer.seekToDefaultPosition()
simplePlayer.playWhenReady = true
}
}
private fun pauseVideo() {
simplePlayer.playWhenReady = false
}
private fun releasePlayer() {
simplePlayer.stop(true)
simplePlayer.release()
}}
override fun setMenuVisibility(menuVisible: Boolean) {
if (!menuVisible){
simplePlayer?.playWhenReady = false
simplePlayer?.pause()
}
super.setMenuVisibility(menuVisible)
}
JUST ADD THIS IN YOUR StoryViewFragment.

Handle LiveData and Observable to return inside a function a value emmited

I'm starting in MVVM architecture and Kotlin reactive programming.
How can I use kotlin LiveData, Coroutines and/or Observable to do in optionSelected() function a return only after the user click in par1 or par2 buttons to return the content to second while of insertionSort() function?
class PlaceholderFragment : Fragment() {
var par1:Button? = null
var par2:Button? = null
var parTextLive: LiveData<String> = MutableLiveData<String>()
var arr = arrayOf("homework", "chores", "shopping")
var i = -1
var j: Int = 0
var tmp: String = ""
var clicked: Boolean = false
var parText: String = ""
var len: Int = arr.size
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_main, container, false)
par1 = root!!.findViewById(R.id.par1) as Button
par2 = root!!.findViewById(R.id.par2) as Button
lifecycleScope.launch {
insertionSort()
}
par1!!.setOnClickListener {
lifecycleScope.launch {
//how to get in LiveData the String value?
//parTextLive = (par1!!.text.toString())
parText = (par1!!.text.toString())
}
}
par2!!.setOnClickListener {
lifecycleScope.launch {
//how to get in LiveData the String value?
//parTextLive = (par2!!.text.toString())
parText = (par2!!.text.toString())
}
}
return root
}
suspend fun optionSelected(str1: String, str2: String): String {
return withContext(Dispatchers.Main) {
println("opções setadas: $str1 or $str2")
par1!!.text = str1
par2!!.text = str2
// I setted the buttons pair1 and pair2 with texts of str1 and str2. When the user click in one of them, I want to return the text of clicked button
delay(5000) //substitute the delay by the user click event
return#withContext str1 //returning the variable parTextLive with the refreshed value
}
}
suspend fun insertionSort(): Array<String> {
return withContext(Dispatchers.Default) {
while(len-- != 0) {
tmp = arr[++i];
j = i
while (j-- != 0 && (optionSelected(arr[j], tmp) == arr[j])) {
arr[j + 1] = arr[j];
}
arr[j + 1] = tmp
}
return#withContext arr.apply { reverse() }
}
}
}
To assign value to MutableLiveData I used parTextLive.value
To wait for LiveData value be received by user click, I used parTextLive.asFlow().first(), it waits until parTextLive receives some value
class PlaceholderFragment : Fragment() {
var par1:Button? = null
var par2:Button? = null
var parTextLive: LiveData<String> = MutableLiveData<String>()
var arr = arrayOf("homework", "chores", "shopping")
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_main, container, false)
par1 = root!!.findViewById(R.id.par1) as Button
par2 = root!!.findViewById(R.id.par2) as Button
lifecycleScope.launch {
insertionSort(arr)
}
par1!!.setOnClickListener {
lifecycleScope.launch {
parTextLive.value = (par1!!.text.toString())
}
}
par2!!.setOnClickListener {
lifecycleScope.launch {
parTextLive.value = (par2!!.text.toString())
}
}
return root
}
suspend fun optionSelected(str1: String, str2: String): String {
return withContext(Dispatchers.Main) {
println("opções setadas: $str1 or $str2")
par1!!.text = str1
par2!!.text = str2
val parText = (parTextLive.asFlow().first())
parTextLive = MutableLiveData<String>()
return#withContext parText
}
}
suspend fun insertionSort(): Array<String> {
return withContext(Dispatchers.Default) {
while(len-- != 0) {
tmp = arr[++i];
j = i
while (j-- != 0 && (optionSelected(arr[j], tmp) == arr[j])) {
arr[j + 1] = arr[j];
}
arr[j + 1] = tmp
}
return#withContext arr.apply { reverse() }
}
}
}

Categories

Resources