Here's the situation - I've started studying kotlin and android studio, and now I'm stuck with this.
I have a button (ImageView) that when pressed starts to play an audio file.
class MainActivity : AppCompatActivity() {
private var mp: MediaPlayer? = null
private var bruhSound: MutableList<Int> = mutableListOf(R.raw.bruh)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
supportActionBar?.hide()
bruhBtn.setOnClickListener {
if (mp == null) {
controlSound(bruhSound[0])
bruhBtn.setImageResource(R.drawable.btnpressed)
} else if (mp !== null) {
bruhBtn.setImageResource(R.drawable.btn)
}
}
}
private fun controlSound(id: Int) {
if (mp == null) {
mp = MediaPlayer.create(this, id)
Log.d("MainActivity", "ID: ${mp!!.audioSessionId}")
}
mp?.start()
Log.d("MainActivity", "Duration: ${mp!!.duration / 1000} seconds")
}
Currently when I press "bruhBtn", the picture is changing to "btnpressed" and back again correctly, but it wont change after audio is ended. I want it to reset on the audio finishing. I realize that problem is with my code, I need to change the image when the audio is finished. How would I do this?
Before your line
mp?.start
add a listener
mp?.setOnCompletionListener { //change your button state here }
Related
I try to load rewarded interestitial ad in Mainactivity and then show it in one composable screen in jetpack compose. I loaded it successfully but in my RewardedShow compose screen rewardedInterstitialAd is null? I used my code from https://developers.google.com/admob/android/rewarded-interstitial
This is my code in MainActivity
class MainActivity : ComponentActivity(){
var rewardedInterstitialAd: RewardedInterstitialAd? = null
private var TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyBeautyAppInJetpackComposeTheme {
MyNavGraph()
MobileAds.initialize(this) {
loadAd()
}
}
}
}
private fun loadAd() {
RewardedInterstitialAd.load(this, "ca-app-pub-
3940256099942544/5354046379",
AdRequest.Builder().build(), object :
RewardedInterstitialAdLoadCallback() {
override fun onAdLoaded(ad: RewardedInterstitialAd) {
Log.d(TAG, "Ad was loaded.")
rewardedInterstitialAd = ad
}
override fun onAdFailedToLoad(adError: LoadAdError) {
Log.d(TAG, adError?.toString())
rewardedInterstitialAd = null
}
})
}
}
and this the RewardedShowCompose screen
#SuppressLint("UnrememberedMutableState")
#Composable
fun RewardedShowCompose( actions: MainActions, maskArg: String) {
val context = LocalContext.current
val loading = mutableStateOf(true)
Surface(modifier = Modifier.fillMaxSize())
{
if (MainActivity.rewardedInterstitialAd != null){
MainActivity.rewardedInterstitialAd?.show(context as Activity, OnUserEarnedRewardListener { actions.gotoFinalShow.invoke(maskArg) })
}
}
}
I made rewardedInterstitialAd Companion and now it works well. like this
companion object {
var rewardedInterstitialAd: RewardedInterstitialAd? = null
}
I've got an activity which uses camera intent. If I take a picture vertically and accept to display it in an imageview vertically there is no problem. If i take a picture horizontally and accept it while having the mobile horizontally it crashes, but if I turn it to vertical it works. Does anybody know why this might be happening?
It does not have much sense cause when the app crashes the only thing it says is that the photoFile is null, it seems as if there was no picture but in fact there is a picture.
Here is the code from the activity:
private const val FILE_NAME = "photo.jpg"
class TextCameraActivity : AppCompatActivity(), TextToSpeech.OnInitListener {
private lateinit var binding: ActivityTextCameraBinding
private lateinit var bitmap : Bitmap
private lateinit var photoFile: File
private var tts: TextToSpeech? = null
private var locale : Locale = Locale("es", "ES")
private var progressBar : ProgressBar? = null
private var i = 0
private val handler = Handler()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTextCameraBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btn7.setOnClickListener {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
photoFile = getPhotoFile(FILE_NAME)
val fileProvider = FileProvider.getUriForFile(this, "com.example.fileprovider", photoFile)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileProvider)
if(takePictureIntent.resolveActivity(this.packageManager) != null) {
startActivityForResult(takePictureIntent, 42)
} else {
Toast.makeText(this, "Unable to open camera", Toast.LENGTH_SHORT).show()
Log.d("mensaje", "?????Unable to open camera")
}
}
tts = TextToSpeech(this, this)
progressBar = binding.progressBar
binding.btnReconocerImagen.setOnClickListener {
progressBar!!.visibility = View.VISIBLE
i = progressBar!!.progress
Thread(Runnable {
while (i < 10) {
i += 1
handler.post(Runnable {
progressBar!!.progress = i
})
try {
Thread.sleep(100)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
progressBar!!.visibility = View.INVISIBLE
}).start()
recognizeText()
}
val actionBar = supportActionBar
actionBar?.setDisplayHomeAsUpEnabled(true)
}
private fun getPhotoFile(fileName:String):File {
val storageDirectory = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(fileName, ".jpg", storageDirectory)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.getItemId()) {
android.R.id.home -> {
finish()
return true
}
}
return super.onOptionsItemSelected(item)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
return true
}
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
// set US English as language for tts
val result = tts!!.setLanguage(locale)
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.e("TTS","The Language specified is not supported!")
} else {
binding.btnReconocerImagen.isEnabled = true
}
} else {
Log.e("TTS", "Initilization Failed!")
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if(requestCode == 42 && resultCode == Activity.RESULT_OK) {
bitmap = BitmapFactory.decodeFile(photoFile.absolutePath)
binding.imageView.setImageBitmap(bitmap)
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
private fun recognizeText() {
try {
val image = InputImage.fromBitmap(bitmap, 0)
val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
val result = recognizer.process(image)
.addOnSuccessListener { visionText ->
for (block in visionText.textBlocks) {
val boundingBox = block.boundingBox
val cornerPoints = block.cornerPoints
val text = block.text
Log.d("mensaje", "he encontrado $text")
binding.tvTextoReconocido.text = "El texto reconocido es: $text"
tts!!.speak("El texto reconocido es $text", TextToSpeech.QUEUE_FLUSH, null, "")
for (line in block.lines) {
// ...
for (element in line.elements) {
// ...
}
}
}
}
.addOnFailureListener { e ->
}
} catch (e : Exception) {
Log.d("mensaje", "NO HAY IMAGEN SELECCIONADA")
Toast.makeText(this,"NO HAS SELECCIONADO NINGUNA IMAGEN PARA RECONOCER", Toast.LENGTH_SHORT).show()
}
}
}
Error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.appdigitalinkrecognition, PID: 30407
java.lang.RuntimeException: Unable to resume activity {com.example.appdigitalinkrecognition/com.example.appdigitalinkrecognition.TextCameraActivity}: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=42, result=-1, data=null} to activity {com.example.appdigitalinkrecognition/com.example.appdigitalinkrecognition.TextCameraActivity}: kotlin.UninitializedPropertyAccessException: lateinit property photoFile has not been initialized
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4918)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4955)
at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8653)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=42, result=-1, data=null} to activity {com.example.appdigitalinkrecognition/com.example.appdigitalinkrecognition.TextCameraActivity}: kotlin.UninitializedPropertyAccessException: lateinit property photoFile has not been initialized
at android.app.ActivityThread.deliverResults(ActivityThread.java:5590)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4905)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4955)
at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8653)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property photoFile has not been initialized
at com.example.appdigitalinkrecognition.TextCameraActivity.onActivityResult(TextCameraActivity.kt:143)
at android.app.Activity.dispatchActivityResult(Activity.java:8550)
at android.app.ActivityThread.deliverResults(ActivityThread.java:5583)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4905)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4955)
at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8653)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
````
If I had to guess, you're getting different results because changing the orientation destroys an Activity, so a new one can be created to match the new orientation. So maybe that's happening in the background - but if you take a photo vertically, there's no orientation change so the Activity isn't destroyed
That's an issue because like the docs say:
Note: Since your process and activity can be destroyed between when you call launch() and when the onActivityResult() callback is triggered, any additional state needed to handle the result must be saved and restored separately from these APIs.
This is the closest thing I can find to them explicitly saying "onActivityResult can run before onCreate", but if that's what's happening to you, then that's why you're getting that lateinit property photoFile has not been initialized error.
It might also be because you're not actually initialising it in onCreate, you're doing it in a click listener - so even if onCreate does run first, the button would need to be clicked again, and onActivityResult definitely runs before that. But just moving it into onCreate might not be enough
I think to handle this properly, you'll need to think about your "show the photo" task having two parts - creating the path (in onCreate) and getting a result (through onActivityResult). You can't guarantee the order of those two things, so when you do one, you'll have to check if the other part has already been completed - if it has, you can show the image.
You could do something like this:
private var pathInitialised = false
private var photoTaken = false
override fun onCreate(savedInstanceState: Bundle?) {
...
// don't do this in the click listener - it needs to be available when the activity starts
photoFile = getPhotoFile(FILE_NAME)
// set the flag, and try to show the pic if ready
pathInitialised = true
tryDisplayPhoto()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if(requestCode == 42 && resultCode == Activity.RESULT_OK) {
// set the flag, and try to show the pic if ready
photoTaken = true
tryDisplayPhoto()
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
private fun tryDisplayPhoto() {
if (pathInitialised && photoTaken) {
// now you know that the path is available and a photo needs to be shown
bitmap = BitmapFactory.decodeFile(photoFile.absolutePath)
binding.imageView.setImageBitmap(bitmap)
}
}
So now, no matter what order those two methods are called, they both try to display a pic - and that will only happen when the last of those methods is called, and all the pieces are in place
Declaring photoFile as optional should solve the problem:
private var photoFile: File? = null
You will need to add a null-check in your onActivityResult:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if(requestCode == 42 && resultCode == Activity.RESULT_OK) {
photoFile?.let { photo ->
bitmap = BitmapFactory.decodeFile(photo.absolutePath)
binding.imageView.setImageBitmap(bitmap)
} ?: run {
// Photo is not defined...
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
As I have understood, when I exit the player view while watching a video by pressing or not the pause button, the onPause() and onStop() methods come into play and if we pressing back button the onDestroy() method comes into play and destroy the view.
The following code achieves that when I leave the view of the player pressing or not to the pause button, for example to increase the brightness of the screen and I return to the video, the reproduction continues for where it was left. That's good for me. But when I press the back button and play the video again, it starts from the beginning.
I could control it in the onCreate() method, but I can't find the right key.
Any idea? Thank you
private lateinit var binding: ActivityExoPlayerPlayPeliBinding
private var player: ExoPlayer? = null
private var playWhenReady = true
private var currentItem = 0
private var playbackPosition: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityExoPlayerPlayPeliBinding.inflate(layoutInflater)
setContentView(binding.root)
}
override fun onStart() {
super.onStart()
initializePlayer()
}
override fun onPause() {
super.onPause()
savePos()
}
override fun onStop() {
super.onStop()
savePos()
}
override fun onResume() {
super.onResume()
hideSystemUi()
continuePlayer(savePos())
}
override fun onDestroy() {
super.onDestroy()
savePos()
}
private fun savePos(): Long {
if (player != null){
playbackPosition = player!!.currentPosition
player!!.removeListener(this)
player!!.release()
player = null
}
return playbackPosition
}
private fun continuePlayer(playPosition: Long) {
player = ExoPlayer.Builder(applicationContext).build()
player!!.addListener(this)
binding.videoView.player = player
val url = intent.getStringExtra("url")
val mediaItem = item(url!!)
player!!.setMediaItem(mediaItem)
player!!.isCurrentMediaItemLive
playWhenReady
player!!.seekTo(currentItem, playPosition)
player!!.prepare()
player!!.play()
}
private fun initializePlayer() {
player = ExoPlayer.Builder(applicationContext).build()
player!!.addListener(this)
binding.videoView.player = player
val url = intent.getStringExtra("url")
val mediaItem = item(url!!)
player!!.setMediaItem(mediaItem)
player!!.isCurrentMediaItemLive
playWhenReady
player!!.seekTo(currentItem, playbackPosition)
player!!.prepare()
player!!.play()
}
you have to save the position elsewhere and then retrieve it in your Activity or Fragment. The easiest way to do this is through SharedPreferences
i'm a beginner in android & kotlin and i'm having an issue i been trying to figure out all day...
I have an app that fetches data from NewsApi and displays it in a recycler view , i am using Retrofit library and Room (to save favorite articles) with MVVM architecture. I want to add an option so that the user can select the country of the news from a dialog that pops up by clicking on a icon on the toolbar menu.
I have created a custom DialogFragment and have it show up, the dialog contains a spinner with a list of countries and i'm using FragmentResult and FragmentResultListener to pass the country value between dialog fragment and news fragment.
DialogFragment
class CountrySelectDialog : DialogFragment(R.layout.country_selection_dialog) {
private lateinit var binding: CountrySelectionDialogBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = CountrySelectionDialogBinding.bind(view)
binding.spCountrySelection.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
Toast.makeText(
context,
"you selected ${adapterView?.getItemAtPosition(position).toString()}",
Toast.LENGTH_SHORT
).show()
}
override fun onNothingSelected(adapterView: AdapterView<*>?) {
}
}
binding.btnCancel.setOnClickListener {
this.dismiss()
}
binding.btnConfirm.setOnClickListener {
val result = binding.spCountrySelection.selectedItem.toString()
setFragmentResult("countryCode", bundleOf("bundleKey" to result))
this.dismiss()
}
}
}
The news Fragment is observing data from the View Model
class BreakingNewsFragment : Fragment(R.layout.fragment_breaking_news) {
lateinit var viewModel: NewsViewModel
lateinit var newsAdapter: NewsAdapter
private lateinit var binding: FragmentBreakingNewsBinding
val TAG = "BreakingNewsFragment"
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentBreakingNewsBinding.bind(view)
viewModel = (activity as NewsActivity).viewModel
setUpRecyclerView()
setFragmentResultListener("countryCode") { countryCode, bundle ->
val result = bundle.getString("countryCode")
viewModel.countryCode = result!!}
viewModel.breakingNews.observe(viewLifecycleOwner, Observer {
when (it) {
is Resource.Success -> {
hideProgressBar()
it.data?.let {
newsAdapter.differ.submitList(it.articles.toList())
val totalPages = it.totalResults / QUERY_PAGE_SIZE + 2
isLastPage = viewModel.breakingNewsPage == totalPages
}
}
is Resource.Error -> {
hideProgressBar()
it.message?.let {
Log.e(TAG, "An error occurred: $it")
}
}
is Resource.Loading -> {
showProgressBar()
}
}
})
newsAdapter.setOnItemClickListener {
val bundle = Bundle().apply {
putSerializable("article", it)
}
findNavController().navigate(
R.id.action_breakingNewsFragment_to_articleFragment, bundle
)
}
}
ViewModel:
class NewsViewModel(val newsRepository: NewsRepository, val app: Application) : AndroidViewModel(app) {
val breakingNews: MutableLiveData<Resource<NewsResponse>> = MutableLiveData()
var breakingNewsPage = 1
var breakingNewsResponse: NewsResponse? = null
val searchNews: MutableLiveData<Resource<NewsResponse>> = MutableLiveData()
var searchNewsPage = 1
var searchNewsResponse: NewsResponse? = null
var countryCode :String = "it"
init {
getBreakingNews(countryCode)
}
fun getBreakingNews(countryCode: String) {
viewModelScope.launch {
breakingNews.postValue(Resource.Loading())
val response = newsRepository.getBreakingNews(countryCode, breakingNewsPage)
breakingNews.postValue(handleBreakingNewsResponse(response))
}
}
fun handleBreakingNewsResponse(response: Response<NewsResponse>): Resource<NewsResponse> {
if (response.isSuccessful) {
response.body()?.let { resultResponse ->
breakingNewsPage++
if (breakingNewsResponse == null) {
breakingNewsResponse = resultResponse
} else {
val oldArticles = breakingNewsResponse?.articles
val newArticles = resultResponse.articles
oldArticles?.addAll(newArticles)
}
return Resource.Success(breakingNewsResponse ?: resultResponse)
}
}
return Resource.Error(response.message())
}
fun searchNews(searchQuery: String) {
viewModelScope.launch {
searchNews.postValue(Resource.Loading())
val response = newsRepository.searchNews(searchQuery, searchNewsPage)
searchNews.postValue((handleSearchNewsResponse(response)))
}
}
fun handleSearchNewsResponse(response: Response<NewsResponse>): Resource<NewsResponse> {
if (response.isSuccessful) {
response.body()?.let { resultResponse ->
searchNewsPage++
if (searchNewsResponse == null) {
searchNewsResponse = resultResponse
} else {
val oldArticles = searchNewsResponse?.articles
val newArticles = resultResponse.articles
oldArticles?.addAll(newArticles)
}
return Resource.Success(searchNewsResponse ?: resultResponse)
}
}
return Resource.Error(response.message())
}
}
When i click on the icon on the toolbar menu the dialog appears and works fine but i can't seem to find a way to have the recycler view update with new data using given value for country
I searched everywhere and couldn't find a solution (or probably didn't understand it :S) can someone guide me into the right direction? I'm so lost...
When I click on the icon on the toolbar menu the dialog appears and works fine but I can't seem to find a way to have the recycler view update with new data using given value for country.
im trying to navigate from webview to fragment using navController, but it seems not working.
here is my code
class LandingPageCreditCardFragment : Fragment(R.layout.fragment_landing_page_credit_card) {
private val binding by viewBinding(FragmentLandingPageCreditCardBinding::bind)
lateinit var navController: NavController
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = view.findNavController()
Log.i("link url", arguments?.getString("url").toString())
showWebViewContent(requireArguments().getString("url")!!)
}
private fun showWebViewContent(url: String) {
binding.webViewCreditCard.settings.javaScriptEnabled = true
binding.webViewCreditCard.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
if (url != null) {
view?.loadUrl(url)
}
return true
}
}
binding.webViewCreditCard.addJavascriptInterface(object : Any() {
#JavascriptInterface
fun valid() {
Toast.makeText(requireContext(), "test", Toast.LENGTH_SHORT).show()
val builder = AlertDialog.Builder(requireContext())
builder.setTitle("Konfirmasi")
builder.setMessage("navigasi ke pesanan")
builder.setPositiveButton("Ya") { dialog, which ->
navController.navigate(R.id.action_landingPageCreditCardFragment_to_pesananFragment)
dialog.dismiss()
}
builder.setNegativeButton("Tidak") { dialog, which ->
dialog.dismiss()
}
builder.show() }
}, "btn")
binding.webViewCreditCard.loadUrl(url)
} }
im referrring to this answer to get my function working
i try to show toast, its working
then im try to show alertDialog contain button to navigate from webview to next fragment, when i click it, my app crash.
any help appreciated
after much debugging, i found the answer.
call the navController in runOnUiThread wrapped on Thread and Handler
binding.webViewCreditCard.addJavascriptInterface(object : Any() {
#JavascriptInterface
fun valid() {
val handler = Handler(Looper.getMainLooper())
handler.post(Thread {
(activity as MainActivity).runOnUiThread {
navController.navigate(R.id.action_landingPageCreditCardFragment_to_pesananFragment)
}
})
}
}, "btn")