0

I am trying to build an app that uses the youtube API. I need to call the search API, whenever the user searches for something and then display the results in a new activity(SearchResultsActivity). However the scenario I get is that my app, rather than opening the SearchResultsActivity gets redirected to the MainActivity. The scenario is recorded, and can be better understood through this https://drive.google.com/file/d/1WESzXbRbjhmKCY1gfUq1d2gllIEsILCf/view?usp=sharing.

I want to know what is causing the SearchResultsActivity to be paused by itself and MainActivity being restarted instead. I tried logging the lifecycles to know what was happening and I found this: The logcat window shows this

Also Search box activity here means the SearchActivity:

package com.example.mytube.UI

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.view.View
import android.widget.EditText
import android.widget.ImageView
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.mytube.R
import com.example.mytube.adapters.SearchedHistoryAdapter
import com.example.mytube.db.SearchDatabase
import com.example.mytube.repository.VideosRepository

class SearchActivity : AppCompatActivity() {

    lateinit var searchAdapter: SearchedHistoryAdapter
    lateinit var viewModel: VideosViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_search)

        val repository = VideosRepository(SearchDatabase.getSearchDatabase(this))
        val viewModelFactory = VideosViewModelProviderFactory(repository)
        viewModel = ViewModelProvider(this,viewModelFactory).get(VideosViewModel::class.java)

        val recyclerView = findViewById<RecyclerView>(R.id.searched_terms)
        searchAdapter = SearchedHistoryAdapter()
        recyclerView.apply {
            adapter = searchAdapter
            layoutManager = LinearLayoutManager(this@SearchActivity)
        }

        val backButton = findViewById<ImageView>(R.id.ivGoback)
        backButton.apply {
            setOnClickListener {
                finish()
            }
        }

        val searchBar = findViewById<EditText>(R.id.search_box)
        val searchBtn = findViewById<ImageView>(R.id.ivSearch)
        searchBtn.setOnClickListener {
            if (searchBar.text.toString().isNotEmpty()) {
                viewModel.insertSearchItem(searchBar.text.toString())
                val intent = Intent(applicationContext, SearchResultsActivity::class.java)
                intent.putExtra("searchQuery", searchBar.text)
                startActivity(intent)
            }
        }
        searchBar.setOnKeyListener(View.OnKeyListener{v, keyCode, event ->
            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
                if (searchBar.text.toString().isNotEmpty()) {
                    viewModel.insertSearchItem(searchBar.text.toString())
                }
                return@OnKeyListener true
            }
            return@OnKeyListener false
        })

        viewModel.getSearchHistory().observe(this, Observer { searchItems ->
            searchAdapter.differ.submitList(searchItems)
        })

    }
    override fun onPause() {
        super.onPause()
        Log.d("searched", "Search box activity paused")
    }

    override fun onStop() {
        super.onStop()
        Log.d("searched", "Search box activity stopped")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("searched", "Search box activity destroyed")
    }
}

The SearchResultsActivity:

package com.example.mytube.UI

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.AbsListView
import android.widget.ProgressBar
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.mytube.R
import com.example.mytube.adapters.VideosAdapter
import com.example.mytube.db.SearchDatabase
import com.example.mytube.repository.VideosRepository
import com.example.mytube.util.Resource

class SearchResultsActivity : AppCompatActivity() {
    lateinit var viewModel: VideosViewModel
    lateinit var videosAdapter: VideosAdapter
    var searchQuery = ""

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_search_results)

        searchQuery = intent.getStringExtra("searchQuery").toString()

        val repository = VideosRepository(SearchDatabase.getSearchDatabase(this))
        val viewModelFactory = VideosViewModelProviderFactory(repository)
        viewModel = ViewModelProvider(this,viewModelFactory).get(VideosViewModel::class.java)
        videosAdapter = VideosAdapter(viewModel)
        val recyclerView = findViewById<RecyclerView>(R.id.videos)
        recyclerView.apply {
            layoutManager = LinearLayoutManager(this@SearchResultsActivity)
            adapter = videosAdapter
            addOnScrollListener(this@SearchResultsActivity.scrollListener)
        }
        val progressBar = findViewById<ProgressBar>(R.id.paginationProgressBar)
        viewModel.getSearchResults(searchQuery)

        viewModel.searchResults.observe(this, Observer { resource ->
            when(resource) {
                is Resource.Success -> {
                    hideProgressBar(progressBar)
                    resource.data?.let { videoResponse ->
                        if (viewModel.nextSearchPageId != videoResponse.nextPageToken) {
                            viewModel.nextSearchPageId = videoResponse.nextPageToken
                            viewModel.searchedVideos.addAll(videoResponse.items)
//                            videoResponse.items.forEach { viewModel.getChannel(it.snippet.channelId) }
                            Log.d("Channels", viewModel.channels.toString())
                            videosAdapter.differ.submitList(viewModel.searchedVideos.toList())
                        }
                        else {
                            Log.d("Videos", "next token dekh ${viewModel.nextPageId}")
                            videosAdapter.differ.submitList(viewModel.searchedVideos.toList())
                        }
                    }
                }
                is Resource.Error -> {
                    hideProgressBar(progressBar)
                    Log.e("Videos", resource.message.toString())
                }
                is Resource.Loading -> {
                    showProgressBar(progressBar)
                }
            }
        })

        viewModel.channelResponse.observe(this, Observer { resource ->
            when (resource) {
                is Resource.Success -> {
                    resource.data?.let { channels ->
                        viewModel.channels.set(channels.items[0].id, channels.items[0])
//                        videosAdapter.differ.submitList(viewModel.videos.toList())
                    }
                }
                is Resource.Error -> {
                    Log.e("Channels", resource.message.toString())
                }
                is Resource.Loading -> {
                    Log.d("Channels", "Waiting")
                }
            }
        })

    }


    private fun hideProgressBar(bar: ProgressBar) {
        bar.visibility = View.INVISIBLE
        isLoading = false
    }

    private fun showProgressBar(bar: ProgressBar){
        bar.visibility = View.VISIBLE
        isLoading = true
    }

    var isScrolling = false
    var isLoading = false

    val scrollListener = object: RecyclerView.OnScrollListener() {
        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
            super.onScrollStateChanged(recyclerView, newState)
            if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
                isScrolling = true
            }
        }

        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)
            val manager = recyclerView.layoutManager as LinearLayoutManager
            val currentItems = manager.childCount
            val totaltItems = manager.itemCount
            val scrolledItems = manager.findFirstVisibleItemPosition()
            if (isScrolling && totaltItems == currentItems + scrolledItems && !isLoading && viewModel.searchedVideos.size <= 1_000_000) {
                viewModel.getNextSearchResults(searchQuery)
                isScrolling = false
            } else {
                videosAdapter.differ.submitList(viewModel.searchedVideos.toList())
                recyclerView.setPadding(0, 0, 0, 0)
                Log.d("Videos", viewModel.searchedVideos.size.toString())
            }
        }
    }

    override fun onPause() {
        super.onPause()
        Log.d("searched", "Search Results Activity paused")
    }

    override fun onStop() {
        super.onStop()
        Log.d("searched", "Search Results Activity stopped")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("searched", "Search Results Activity destroyed")
    }
}

The logcat shows the following error: 2021-09-28 12:51:06.200 1207-2988/? W/ActivityTaskManager: Force finishing activity com.example.mytube/.UI.SearchResultsActivity

For recreating the error, here is the gitHub repo to the app: https://github.com/AkhilrajNambiar/MyTube-1

Thanks in advance!

Aknk
  • 336
  • 3
  • 14
  • 2
    "rather than opening the SearchResultsActivity gets redirected to the MainActivity" - this most probably seems to be a crash in the application . can you check your logcat for crashes – Nitish Sep 28 '21 at 06:26
  • 2021-09-28 12:10:39.074 1207-1327/? E/InputDispatcher: channel 'bfe10e9 com.example.mytube/com.example.mytube.UI.SearchActivity (server)' ~ Channel is unrecoverably broken and will be disposed! 2021-09-28 12:10:39.075 1207-1327/? E/InputDispatcher: channel 'b62c2e com.example.mytube/com.example.mytube.UI.SearchResultsActivity (server)' ~ Channel is unrecoverably broken and will be disposed! 2021-09-28 12:10:39.075 1207-1327/? E/InputDispatcher: channel '604680c com.example.mytube/com.example.mytube.UI.MainActivity (server)' ~ Channel is unrecoverably broken and will be disposed! – Aknk Sep 28 '21 at 06:41
  • I guess this error is the one that causes the crash. Any solution for this? – Aknk Sep 28 '21 at 06:42
  • 1
    Does this help - [channel broken](https://stackoverflow.com/questions/12459719/why-i-am-getting-error-channel-is-unrecoverably-broken-and-will-be-disposed) , it most probably seems you are updating UI element using background thread – Nitish Sep 28 '21 at 06:44
  • I still can't figure out what has to be done. Could you please try running the app on your device @Nitish and figure out what is really going wrong? – Aknk Sep 28 '21 at 07:19
  • @Ankn , I looked at your code , your app is going to `SearchResultsActivity` you api call is not working it's always going to onError , please check that , I think you are pasing it working as api is working – Nitish Sep 30 '21 at 06:58
  • 1
    Also reason for your app is crash is `Expected a string but was BEGIN_OBJECT at path $.items[0].id` , id key in your response sometimes comes as an object instead of String `"id": { "kind": "youtube#video", },` this is causing the JsonDataException and crashes the application – Nitish Sep 30 '21 at 07:10
  • 1
    Thanks a lot @Nitish! It's working really well now. – Aknk Oct 02 '21 at 10:37

0 Answers0