How to retrun a String in Kotlin Initialization - kotlin

I have the following Firebase Initialization, how can I make it return snapshot so that when I call it I get the val snapshot?
var homeRef_Host_Name: Unit = FirebaseDatabase.getInstance().getReference().child("users").child(firebaseAuth.currentUser.uid)
.child("name")
.addListenerForSingleValueEvent(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
val snapshot = snapshot.value.toString()
}
override fun onCancelled(error: DatabaseError) {
}
})

fun getValue(callback:(String)->Unit){
FirebaseDatabase.getInstance().getReference().child("users").child(firebaseAuth.currentUser.uid)
.child("name")
.addListenerForSingleValueEvent(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
callback(snapshot.value.toString())
}
override fun onCancelled(error: DatabaseError) {
}
})
}
fun main(){
getValue {
var yourvariable = it
//place your further code here
}
}
you cant directly assign it to variable because it async operation

Related

Why is a for loop causing an infinite loop in retrofit?

I'm using MutableLiveData<List> for the backing field. I've tried everything but it will continue to make API calls and it never breaks out of the for loop. I'm not sure whether changing the immutable list value of MutableLiveData triggers this.
fun getTweets(userIDs: List<Long>){
val ids = listOf(1168929753237094400, 3456625696)
viewModelScope.launch {
for(id in ids){
tweetService.getUsername(id).enqueue(object : Callback<TwitterUserResponse> {
override fun onResponse(call: Call<TwitterUserResponse>, response: Response<TwitterUserResponse>) {
val username = response.body()!!.user.username
tweetService.getTweets(id).enqueue(object: Callback<TwitterResponse>{
override fun onResponse(call: Call<TwitterResponse>, response: Response<TwitterResponse>) {
_tweets.value = response.body()!!.tweets.apply { forEach { it.username = username } }
}
override fun onFailure(call: Call<TwitterResponse>, t: Throwable) {}
})
}
override fun onFailure(call: Call<TwitterUserResponse>, t: Throwable) {}
})
}
}
}

mutableListOf() I want to use it globally using lateinit Kotli

private lateinit var participants: mutableListOf<ParticipantDTO>
participants = mutableListOf<ParticipantDTO>()
private fun searchMatchInfoByMatchID(matchId: String,summonerName: String){
lolAPIForMatch.getMatchInfoByMatchID(matchId, API_KEY)
.enqueue(object : Callback<MatchDTO>{
override fun onResponse(call: Call<MatchDTO>, response: Response<MatchDTO>) {
if(response.isSuccessful.not()){
return
}
response.body()?.let {
it.info.participants.filter {
it.summonerName == "${summonerName}"
}.forEach {
participants.add(it)
}
participantAdapter.participants = participants
}
}
override fun onFailure(call: Call<MatchDTO>, t: Throwable) {
}
})
}
participantAdapter.participants = participants
First, declare it as a global variable.
After initialization in onCreate function.
By putting data into participants in the searchMatchInfoByMatchID function
I want to assign participants to participantAdapter.participants, but the data is not being allocated.

I want to use recyclerView, but the view doesn't have any values. kotlin

class SummonerInfoActivity: AppCompatActivity() {
private lateinit var participantAdapter: ParticipantAdapter
private val recycler: RecyclerView by lazy {
findViewById(R.id.RecyclerView)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val Summoner = intent.getParcelableExtra<SummonerDTO>("SummonerDTO")
Log.e("Info","${Summoner}")
val retrofit2 = Retrofit.Builder()
.baseUrl("https://asia.api.riotgames.com")
.addConverterFactory(GsonConverterFactory.create())
.build()
lolAPIForMatch = retrofit2.create(lolAPIService::class.java)
setContentView(R.layout.activity_summoner_info)
participantAdapter = ParticipantAdapter()
recycler.adapter = participantAdapter
recycler.layoutManager = LinearLayoutManager(this#SummonerInfoActivity)
getMatchIdBypuuid(Summoner?.puuId.orEmpty(),Summoner?.summonerName.orEmpty())
}
private fun getMatchIdBypuuid(puuid: String,summonerName: String){
lolAPIForMatch.getMatchIdBypuuid(puuid, 0,20, API_KEY)
.enqueue(object : Callback<List<String>> {
override fun onResponse(
call: Call<List<String>>,
response: Response<List<String>>
) {
if(response.isSuccessful.not()){
return
}
response.body()?.let {
it.forEach {
searchMatchInfoByMatchID(it,summonerName)
}
}
}
override fun onFailure(call: Call<List<String>>, t: Throwable) {
}
})
}
private fun searchMatchInfoByMatchID(matchId: String,summonerName: String){
lolAPIForMatch.getMatchInfoByMatchID(matchId, API_KEY)
.enqueue(object : Callback<MatchDTO>{
override fun onResponse(call: Call<MatchDTO>, response: Response<MatchDTO>) {
if(response.isSuccessful.not()){
return
}
response.body()?.let {
it.info.participants.filter {
it.summonerName == "${summonerName}"
}.forEach {
participantAdapter.participants.add(it)
}
}
}
override fun onFailure(call: Call<MatchDTO>, t: Throwable) {
}
})
}
}
class ParticipantAdapter: RecyclerView.Adapter<ParticipantAdapter.ViewHolder>() {
var participants = mutableListOf<ParticipantDTO>()
inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
fun bind(participant: ParticipantDTO){
itemView.findViewById<TextView>(R.id.kill).text = participant.kills.toString()
itemView.findViewById<TextView>(R.id.death).text = participant.deaths.toString()
itemView.findViewById<TextView>(R.id.assist).text = participant.assists.toString()
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ParticipantAdapter.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ViewHolder(inflater.inflate(R.layout.item_match, parent, false))
}
override fun onBindViewHolder(holder: ParticipantAdapter.ViewHolder, position: Int) {
holder.bind(participants[position])
}
override fun getItemCount(): Int {
return participants.size
}
}
participantAdapter.participants.add(it)
The command is searchMatchInfoByMatchID
In the function, you can check that the value is entered correctly, but
If you check in the onCreate function, the value is not assigned.
I want to use the recycler view by properly assigning a value
I tried to solve this problem, but my skills were not enough.
I desperately need the advice of seniors.
I would really appreciate it if you could show me a code sample if possible

how to play audio offline by url?

i want to if i press play - the audio start playing, but if I will turn off the internet after some time, I can still listen to the audio to the end, how can i do this using andoid-mediaPlayer?
in kotlin
class MusicService:Service(),MediaPlayer.OnPreparedListener,MediaPlayer.OnCompletionListener,MediaPlayer.OnErrorListener {
override fun onBind(intent: Intent?): IBinder? {
return musicBind
}
override fun onCreate() {
super.onCreate()
player= MediaPlayer()
}
fun initMusic(){
player.setWakeMode(applicationContext,PowerManager.PARTIAL_WAKE_LOCK)
player.setAudioStreamType(AudioManager.STREAM_MUSIC)
player.setOnPreparedListener(this)
player.setOnCompletionListener(this)
player.setOnErrorListener(this)
}
override fun onUnbind(intent: Intent?): Boolean {
player.stop()
player.reset()
player.release()
return false
}
inner class MusicBinder:Binder(){
val service: MusicService
get() = this#MusicService
}
companion object{
const val STOPPED = 0
const val PAUSED =1
const val PLAYING = 2
}
override fun onPrepared(mp: MediaPlayer?) {
mp!!.start()
val duration=mp.duration
seekBar.max=duration
seekBar.postDelayed(progressRunner,instentval.toLong())
end_point.text= String.format("%d:%02d",TimeUnit.MILLISECONDS.toMinutes(duration.toLong()),
TimeUnit.MILLISECONDS.toSeconds(duration.toLong())-
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration.toLong())))
}
private fun playSong(){
try {
player.reset()
val playSong=songs
val uri = playSong.link
player=MediaPlayer.create(this, Uri.parse(uri))
try {
player.start()
progressRunner.run()
}catch (e:HttpException){
Toast.makeText(this,"Http went wrong!",Toast.LENGTH_SHORT).show()
}
} catch (e:Exception){
Toast.makeText(this,"something went wrong!",Toast.LENGTH_SHORT).show()
}
}
fun setUI(seekBar: SeekBar,start_int:TextView,end_int:TextView){
this.seekBar=seekBar
start_point=start_int
end_point=end_int
seekBar.setOnSeekBarChangeListener(object :SeekBar.OnSeekBarChangeListener
{
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser){
player.seekTo(progress)
}
start_point.text = String.format(
"%d:%02d",TimeUnit.MILLISECONDS.toMinutes(progress.toLong()),
TimeUnit.MILLISECONDS.toSeconds(progress.toLong())-
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(progress.toLong())))
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
TODO("Not yet implemented")
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
TODO("Not yet implemented")
}
}
)
}
private var progressRunner:Runnable = object :Runnable {
override fun run() {
if (seekBar!=null){
seekBar.progress=player.currentPosition
if (player.isPlaying){
seekBar.postDelayed(this,instentval.toLong())
}
}
}
}
fun setSong(result: Result){
songs=result
playerState= PLAYING
playSong()
}
fun setSongmp3(mp3: Mp3){
songsmp3=mp3
playerState= PLAYING
playSongmp3()
}
public fun puseSong()
{
player.pause()
playerState = PAUSED
}
public fun resumeSong(){
player.start()
playerState= PLAYING
}
override fun onCompletion(mp: MediaPlayer?) {
}
override fun onError(mp: MediaPlayer?, what: Int, extra: Int): Boolean {
return false
}
}

Kotlin Coroutine Unit Test Flow collection with viewModelScope

I want to test a method of my ViewModel that collects a Flow. Inside the collector a LiveData object is mutated, which I want to check in the end. This is roughly how the setup looks:
//Outside viewmodel
val f = flow { emit("Test") }.flowOn(Dispatchers.IO)
//Inside viewmodel
val liveData = MutableLiveData<String>()
fun action() {
viewModelScope.launch { privateAction() }
}
suspend fun privateAction() {
f.collect {
liveData.value = it
}
}
When I now call the action() method in my unit test, the test finishes before the flow is collected. This is how the test might look:
#Test
fun example() = runBlockingTest {
viewModel.action()
assertEquals(viewModel.liveData.value, "Test")
}
I am using the TestCoroutineDispatcher via this Junit5 extension and also the instant executor extension for LiveData:
class TestCoroutineDispatcherExtension : BeforeEachCallback, AfterEachCallback, ParameterResolver {
#SuppressLint("NewApi") // Only used in unit tests
override fun supportsParameter(parameterContext: ParameterContext?, extensionContext: ExtensionContext?): Boolean {
return parameterContext?.parameter?.type === testDispatcher.javaClass
}
override fun resolveParameter(parameterContext: ParameterContext?, extensionContext: ExtensionContext?): Any {
return testDispatcher
}
private val testDispatcher = TestCoroutineDispatcher()
override fun beforeEach(context: ExtensionContext?) {
Dispatchers.setMain(testDispatcher)
}
override fun afterEach(context: ExtensionContext?) {
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
}
class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance()
.setDelegate(object : TaskExecutor() {
override fun executeOnDiskIO(runnable: Runnable) = runnable.run()
override fun postToMainThread(runnable: Runnable) = runnable.run()
override fun isMainThread(): Boolean = true
})
}
override fun afterEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(null)
}
}
You can try either,
fun action() = viewModelScope.launch { privateAction() }
suspend fun privateAction() {
f.collect {
liveData.value = it
}
}
#Test
fun example() = runBlockingTest {
viewModel.action().join()
assertEquals(viewModel.liveData.value, "Test")
}
or
fun action() {
viewModelScope.launch { privateAction()
}
suspend fun privateAction() {
f.collect {
liveData.value = it
}
}
#Test
fun example() = runBlockingTest {
viewModel.action()
viewModel.viewModelScope.coroutineContext[Job]!!.join()
assertEquals(viewModel.liveData.value, "Test")
}
You could also try this,
suspend fun <T> LiveData<T>.awaitValue(): T? {
return suspendCoroutine { cont ->
val observer = object : Observer<T> {
override fun onChanged(t: T?) {
removeObserver(this)
cont.resume(t)
}
}
observeForever(observer)
}
}
#Test
fun example() = runBlockingTest {
viewModel.action()
assertEquals(viewModel.liveData.awaitValue(), "Test")
}
So what I ended up doing is just passing the Dispatcher to the viewmodel constructor:
class MyViewModel(..., private val dispatcher = Dispatchers.Main)
and then using it like this:
viewModelScope.launch(dispatcher) {}
So now I can override this when I instantiate the ViewModel in my test with a TestCoroutineDispatcher and then advance the time, use testCoroutineDispatcher.runBlockingTest {}, etc.