I want to change language programmatically in Jetpack Compose. I've read quite some posts and watch videos but still can't find the way to do it. (The post and video are in Android view system.)
How to change language in kotlin (locale)
https://www.youtube.com/watch?v=xxPzi2h0Vvc
I want my app works like below image. After clicking the language, the whole app will change the language. Below code is the clickable's part. What should I do in this clickable part and MainActivity.kt?
#Composable
fun LanguageScreen(
navController: NavController,
) {
val context = LocalContext.current
val langList = arrayOf("English", "繁體中文", "简体中文", "日本語")
var items by remember {
mutableStateOf(
langList.map {
LanguageItem(
title = it,
isSelected = false
)
}
)
}
LazyColumn(
modifier = Modifier
.fillMaxSize()
) {
items(items.size) { i ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
items = items.mapIndexed { j, item ->
if (i == j) {
item.copy(isSelected = true)
} else item.copy(isSelected = false)
}
if (i == 0) {
setLocaleLang("", context)
} else if (i == 1) {
setLocaleLang("zh-rTW", context)
} else if (i == 2) {
setLocaleLang("zh-rCN", context)
} else {
setLocaleLang("ja", context)
}
}
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(text = items[i].title, fontSize = 20.sp)
if (items[i].isSelected) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = "Selected",
tint = Color.Blue,
modifier = Modifier.size(24.dp)
)
}
}
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(Color.LightGray)
)
}
}
}
fun setLocaleLang(lang: String, context: Context) {
val locale = Locale(lang)
Locale.setDefault(locale)
val resources = context.resources
val configuration = resources.configuration
configuration.setLocale(locale)
resources.updateConfiguration(configuration, resources.displayMetrics)
val editor = context.getSharedPreferences("Settings", Context.MODE_PRIVATE).edit()
editor.putString("My_Lang", lang)
editor.apply()
}
fun loadLocale(context: Context) {
val sharedPreferences = context.getSharedPreferences("Settings", Activity.MODE_PRIVATE)
val language = sharedPreferences.getString("My_Lang", "")
setLocaleLang(language!!, context)
}
MainActivity.kt
class MainActivity : ComponentActivity() {
#ExperimentalFoundationApi
override fun onCreate(savedInstanceState: Bundle?) {
loadLocale(this)
super.onCreate(savedInstanceState)
setContent {
SpanishTravelTheme {
Image: https://i.stack.imgur.com/y5kcO.png
Try this
val context = LocalContext.current
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
val locale = Locale(language) //Here I assume you have access to the language you want
Locale.setDefault(locale)
val resources = context.getResources()
val configuration = resources.getConfiguration()
configuration.locale = locale
resources.updateConfiguration(configuration, resources.getDisplayMetrics())
}
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
)
You can try this
Create a helper object
LocaleUtils.kt
object LocaleUtils {
// [AppPrefs] is sharedpreferences or datastore
fun setLocale(c: Context, pref: AppPrefs) = updateResources(c, pref.language ?: "en") //use locale codes
private fun updateResources(context: Context, language: String) {
context.resources.apply {
val locale = Locale(language)
val config = Configuration(configuration)
context.createConfigurationContext(configuration)
Locale.setDefault(locale)
config.setLocale(locale)
context.resources.updateConfiguration(config, displayMetrics)
}
}
}
Call setLocale inside setContent to change the language at runtime.
setContent {
LocaleUtils.setLocale(LocalContext.current, viewModel.pref)
To change the App language
fun changeAppLanguage(languageISO: String) {
sharedPrefs.edit().putString(LANGUAGE_KEY, languageISO).apply()
}
Please remember to use language ISO 639-1 Code
here's a list of Locale codes
https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/localization
Related
My app has a navigation bar and in one navBarItem I have two tabs, I want to read images and their caption I have stored in my firebase firestore collection into a LazyColumn in one of the tabs.
Here is how my DataException and GalleryRepo class looks like
data class DataOrException<T, E : Exception> (
var data: T? = null,
var e: E? = null
)
#Singleton
class GalleryRepo #Inject constructor(
private val queryImageData: Query
) {
fun getImagesFromFirestore(): DataOrException<List<PQImageGallery>, Exception> {
val dataOrException = DataOrException<List<PQImageGallery>, Exception>()
try {
dataOrException.data = queryImageData.get()
.result.map { document -> document.toObject(PQImageGallery::class.java) }
} catch (e: FirebaseFirestoreException) {
dataOrException.e = e
}
return dataOrException
}
}
My data class
data class PQImageGallery (
var imageUrl: String? =null,
var caption: String? = null
)
ViewModels...
#HiltViewModel
class PQViewModel #Inject constructor(
private val repository: GalleryRepo
): ViewModel() {
var loading = mutableStateOf(false)
val data: MutableState<DataOrException<List<PQImageGallery>, Exception>> = mutableStateOf(
DataOrException(
listOf(),
Exception("")
)
)
init {
getGalleryCollection()
}
private fun getGalleryCollection() {
viewModelScope.launch {
loading.value = true
data.value = repository.getImagesFromFirestore()
loading.value = false
}
}
}
#Module
#InstallIn(SingletonComponent::class)
object RemoteImageModel {
#Provides
#Singleton
fun loadImagesInCollection() = FirebaseFirestore.getInstance()
.collection(IMAGE_COLLECTION)
}
My GalleryCollection composable file
#Composable
fun GalleryCollection(dataOrException: DataOrException<List<PQImageGallery>, Exception>) {
val images = dataOrException.data
images?.let {
LazyColumn {
items(items = images) { product ->
GalleryCard(pqImageGallery = product)
}
}
}
val e = dataOrException.e
e?.let {
Text(
text = e.message!!,
modifier = Modifier.padding(16.dp)
)
}
}
#Composable
fun GalleryCard(
pqImageGallery: PQImageGallery,
) {
Card(...) {
Box(modifier = Modifier
.fillMaxSize()
) {
pqImageGallery.imageUrl?.let { imageUrl ->
CoilImage(
data = imageUrl,
....
)
}
pqImageGallery.caption?.let { caption ->
Text(
text = caption,
...
)
}
}
}
}
My navBarItem that contains the two tabs called Home
class Home : ComponentActivity() {
private val viewModel: PQViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
HomeScreen(viewModel)
}
}
}
#Composable
fun HomeScreen(
viewModel: PQViewModel
) {
val dataOrException = viewModel.data.value
val navController = rememberNavController()
val currentBackStack by navController.currentBackStackEntryAsState()
val currentDestination = currentBackStack?.destination
val currentScreen =
HomeTabRowScreens.find { it.route == currentDestination?.route } ?: GalleryView
Scaffold(
topBar = {
HomeScreenTabRow(
allScreens = HomeTabRowScreens,
onTabSelected = { newScreen ->
navController
.navigateSingleTopTo(newScreen.route)
},
currentScreen = currentScreen,
)
}
) { innerPadding ->
NavHost(
navController = navController,
startDestination = GalleryView.route,
modifier = Modifier.padding(innerPadding)
) {
composable(route = GalleryView.route) {
// GalleryTabScreen()
GalleryCollection(dataOrException = dataOrException)
}
composable(route = VirtualTourView.route) {
VirtualTourScreen()
}
}
}
}
fun NavHostController.navigateSingleTopTo(route: String) =
this.navigate(route) {
popUpTo(
this#navigateSingleTopTo.graph.findStartDestination().id
) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
Finally calling the Home class in my main screen(MainActivity)
NavHost(
..
) {
composable(NAV_HOME) {
Home()
}
composable(..) { ...}
}
The issue is when I run the app the tabs in the Home navBar doesn't show at all talk of loading the images from the firestore collection.
Also, is there another way to instantiate a viewModel without having to create a class that extends ComponentActiviy?
If there's another way to achieve this kindly share.
I am writing a small application in Kotlin with JetpackCompose and my application freezes (CountDownLatch) when a request is called, is there any way to avoid this?
Without CountDownLatch, okhttp does not have time to return the result before the method completes.
MainActivity.kt
fun infoServer() {
val viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
var txt = remember {
mutableStateOf(0)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.padding(10.dp)
) {
Text(
text = "Player Count ",
style = MaterialTheme.typography.body1
)
Text(
text = txt.value.toString(),
style = MaterialTheme.typography.body1,
fontWeight = FontWeight.Bold
)
}
Row(horizontalArrangement = Arrangement.Center) {
eveButton(
text = "Update",
onClick = ({
viewModel.updatePlayerCount()
txt.value = viewModel.res
}),
modifier = Modifier
.padding(10.dp)
)
eveButton(
text = "Clear",
onClick = ({
txt.value = 0
}),
modifier = Modifier
.padding(10.dp)
)
}
}
}
MainActivityViewModel.kt
class MainActivityViewModel : ViewModel() {
var res = 0
fun updatePlayerCount() {
res = MainActivityModel().updateServerPlayers()
}
}
MainACtivityModel.kt
class MainActivityModel {
var result = 0
fun updateServerPlayers(): Int {
val client = OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.callTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
val request = Request.Builder()
.url("https://esi.evetech.net/latest/status/?datasource=tranquility")
.method("GET", null)
.build()
var countDownLatch = CountDownLatch(1)
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.d("Error Connection", e.toString())
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Error $response")
try {
var json =JSONObject(response.body!!.string())
countDownLatch.countDown()
result = json.getInt("players")
}
catch (e: Exception) {
Log.d("OKHTTP", e.toString())
}
}
}
})
countDownLatch.await()
Log.d("OKHTTP2", result.toString())
return result
}
}
I have 3 composable in Column and I want to align 2nd composable to bottom of 1st composable like this:
But currently i'm getting output like this:
Here is my current code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposePlaygroundTheme {
Column(modifier = Modifier
.fillMaxSize()) {
topControls("Game 1",34,Modifier.weight(1f))
centerControls()
bottomControls()
}
}
}
}
}
#Composable
fun topControls(gamename: String, totalTime: Int, modifier: Modifier) {
Column(modifier = modifier
.background(Color.LightGray)
.padding(start = 24.dp, end = 24.dp, top = 24.dp)) {
Row(modifier = Modifier.padding(end = 32.dp)) {
Image(
painter = painterResource(R.drawable.ic_launcher_foreground),
contentDescription = "",
Modifier
.height(32.dp)
.width(32.dp)
)
Text(
text = gamename,
modifier = Modifier
.weight(1f)
.align(Alignment.CenterVertically),
textAlign = TextAlign.Center
)
}
Text(text = totalTime.toString(),modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center,fontSize = 53.sp)
Image(
painter = painterResource(R.drawable.ic_launcher_background),
contentDescription = "",
Modifier.fillMaxSize(),
contentScale = ContentScale.FillBounds
)
}
}
#Composable
fun centerControls() {
Box(modifier = Modifier
.fillMaxWidth()) {
Divider(color = Color.Blue, thickness = 1.dp, modifier = Modifier.align(Alignment.Center))
Row(modifier = Modifier.padding(start = 20.dp, end = 20.dp)) {
Button(onClick = { },colors = ButtonDefaults.buttonColors(backgroundColor = Color.Black,contentColor = Color.White), modifier = Modifier
.weight(1f)
.padding(end = 20.dp)
.fillMaxWidth()) {
Text(text = "End Game",modifier = Modifier
.wrapContentWidth()
.wrapContentHeight(), textAlign = TextAlign.Center,fontSize = 20.sp)
}
Image(painter = ColorPainter(Color.Gray), contentDescription = "", modifier = Modifier
.padding(horizontal = 25.dp)
.align(Alignment.CenterVertically)
.height(32.dp)
.width(32.dp))
}
}
}
#Composable
fun bottomControls() {
Spacer(modifier = Modifier.height(170.dp))
}
Is there anyway I can achieve this in jetpack compose without using Constraint layout?
As you need to place one view under an other one, you should use Box.
Using Modifier.align, you can specify position of your centerControls, in this case Alignment.BottomCenter.
Using Modifier.layout you can add offset, equal to half view size.
Column(modifier = Modifier
.fillMaxSize()
) {
Box(Modifier.weight(1f)) {
topControls("Game 1", 34, Modifier.fillMaxHeight())
centerControls(
Modifier
.align(Alignment.BottomCenter)
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
placeable.place(0, placeable.height / 2)
}
}
)
}
bottomControls()
}
Result:
Something like this should work
ComposePlaygroundTheme {
Column(modifier = Modifier
.fillMaxSize()) {
topControls("Game 1",34)
Box(modifier = Modifier.weight(1f)) { // FrameLayout, weight will occupy rest of the content
centerControls()
bottomControls() // will be stacked up on top of topControls()
}
}
}
I wrote an App, in Kotlin with Android Studio that write some strings to a file.
All work, I can write and read inside the App, but I can't see the file looking in Documents folder.
How can I use the folder Documents as a storage space?
Thank you
These are the function I use:
fun saveTextFile(view: View, nomeFile: String, testo: String, contesto: Context){
var fileStream: FileOutputStream
try {
fileStream = contesto.openFileOutput(nomeFile, MODE_APPEND) // OK esegue append
fileStream.write(testo.toByteArray())
fileStream.close()
}catch (e: Exception){
e.printStackTrace()
}
}
fun readTextFile(view: View, nomeFile: String, contesto: Context): String{
var fileInputStream: FileInputStream? = null
fileInputStream = contesto.openFileInput(nomeFile)
var inputStreamReader: InputStreamReader = InputStreamReader(fileInputStream)
val bufferedReader: BufferedReader = BufferedReader(inputStreamReader)
val stringBuilder: StringBuilder = StringBuilder()
var text: String? = null
while ({ text = bufferedReader.readLine(); text }() != null) {
stringBuilder.append(text)
}
inputStreamReader.close();
return(stringBuilder.toString())
}
Thank you, Livio
For writing in Documents folder of your device , you just need to make use of MediaStore for the same. You can take input for this function anything that you want like String , bitmap , PdfDocument and other's too .
For Your UseCase you can do the following ,
Global Variable :
private var imageUri: Uri? = null
override suspend fun saveDocument(context : Context, text : String) {
withContext(Dispatchers.IO) {
try {
val collection =
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val dirDest = File(
Environment.DIRECTORY_DOCUMENTS,
context.getString(R.string.app_name)
)
val date = System.currentTimeMillis()
val fileName = "$date.txt"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.RELATIVE_PATH,
"$dirDest${File.separator}")
put(MediaStore.Files.FileColumns.IS_PENDING, 1)
}
}
val imageUri = context.contentResolver.insert(collection, contentValues)
withContext(Dispatchers.IO) {
imageUri?.let { uri ->
context.contentResolver.openOutputStream(uri, "w").use { out -> out?.write(text.toByteArray())
}
contentValues.clear()
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0)
context.contentResolver.update(uri, contentValues, null, null)
}
}
} catch (e: FileNotFoundException) {
null
}
}
}
For Updating the already existing file , do the following . After creating file for the first time I have saved the imageUri in a global variable (If you want to store it permanently / or for a while you can use Jetpack Datastore / Shared Preference to save the same ):
suspend fun updateData(context: Context,text : String){
withContext(Dispatchers.IO) {
try {
val collection =
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val dirDest = File(
Environment.DIRECTORY_DOCUMENTS,
context.getString(R.string.app_name)
)
val fileName = "test.txt"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(
MediaStore.MediaColumns.RELATIVE_PATH,
"$dirDest${File.separator}"
)
put(MediaStore.Files.FileColumns.IS_PENDING, 1)
}
withContext(Dispatchers.IO) {
imageUri?.let { uri ->
context.contentResolver.openOutputStream(uri, "wa").use { out ->
out?.write(text.toByteArray())
}
contentValues.clear()
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0)
context.contentResolver.update(uri, contentValues, null, null)
}
}
} catch (e: FileNotFoundException) {
null
}
}
}
For Reading the File , Do the following :
suspend fun read(context: Context, source: Uri): String = withContext(Dispatchers.IO) {
val resolver: ContentResolver = context.contentResolver
resolver.openInputStream(source)?.use { stream -> stream.readText() }
?: throw IllegalStateException("could not open $source")
}
private fun InputStream.readText(charset: Charset = Charsets.UTF_8): String =
readBytes().toString(charset)
This is how the final code looks like :
class MainActivity : AppCompatActivity() {
private lateinit var btn: Button
private var imageUri: Uri? = null
private lateinit var btn2: Button
private lateinit var btn3 : Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn = findViewById(R.id.btnAdd)
btn2 = findViewById(R.id.getText)
btn3 = findViewById(R.id.updateText)
btn.setOnClickListener {
lifecycleScope.launch {
saveDocument(applicationContext, "Original ")
}
}
btn3.setOnClickListener {
lifecycleScope.launch {
updateData(applicationContext,"Appended")
}
}
btn2.setOnClickListener {
lifecycleScope.launch {
imageUri?.let { it1 ->
val data = read(applicationContext, it1)
Toast.makeText(applicationContext, "The data is $data ", Toast.LENGTH_LONG)
.show()
}
}
}
}
suspend fun saveDocument(context: Context, text: String) {
withContext(Dispatchers.IO) {
try {
val collection =
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val dirDest = File(
Environment.DIRECTORY_DOCUMENTS,
context.getString(R.string.app_name)
)
val date = System.currentTimeMillis()
val fileName = "test.txt"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(
MediaStore.MediaColumns.RELATIVE_PATH,
"$dirDest${File.separator}"
)
put(MediaStore.Files.FileColumns.IS_PENDING, 1)
}
imageUri = context.contentResolver.insert(collection, contentValues)
withContext(Dispatchers.IO) {
imageUri?.let { uri ->
context.contentResolver.openOutputStream(uri, "w").use { out ->
out?.write(text.toByteArray())
}
contentValues.clear()
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0)
context.contentResolver.update(uri, contentValues, null, null)
}
}
} catch (e: FileNotFoundException) {
null
}
}
}
suspend fun updateData(context: Context, text: String) {
withContext(Dispatchers.IO) {
try {
val collection =
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val dirDest = File(
Environment.DIRECTORY_DOCUMENTS,
context.getString(R.string.app_name)
)
val fileName = "test.txt"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(
MediaStore.MediaColumns.RELATIVE_PATH,
"$dirDest${File.separator}"
)
put(MediaStore.Files.FileColumns.IS_PENDING, 1)
}
withContext(Dispatchers.IO) {
imageUri?.let { uri ->
context.contentResolver.openOutputStream(uri, "wa").use { out ->
out?.write(text.toByteArray())
}
contentValues.clear()
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0)
context.contentResolver.update(uri, contentValues, null, null)
}
}
} catch (e: FileNotFoundException) {
null
}
}
}
suspend fun read(context: Context, source: Uri): String = withContext(Dispatchers.IO) {
val resolver: ContentResolver = context.contentResolver
resolver.openInputStream(source)?.use { stream -> stream.readText() }
?: throw IllegalStateException("could not open $source")
}
private fun InputStream.readText(charset: Charset = Charsets.UTF_8): String =
readBytes().toString(charset)
I have three buttons . With the first I create a file , then the uri gets stored in the global variable . Then onClick of second button I add to the already existing file and then read the file using the third button using the same imageUri stored in the global variable
This is the demo for the same . Check when the buttons are being pressed and the output in the form of Toast at the bottom .
I am a student, new to kotlin, so I am converting java codes to kotlin to learn and see how it works, but I didnt understand what the error says.
private val _songs = ArrayList<SongInfo>()
internal lateinit var recyclerView: RecyclerView
internal lateinit var seekBar: SeekBar
internal lateinit var songAdapter: SongAdapter
internal var mediaPlayer: MediaPlayer? = null
private val myHandler = Handler()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recyclerView) as RecyclerView
seekBar = findViewById(R.id.seekBar) as SeekBar
songAdapter = SongAdapter(this, _songs)
recyclerView.adapter = songAdapter
val linearLayoutManager = LinearLayoutManager(this)
val dividerItemDecoration = DividerItemDecoration(recyclerView.context,
linearLayoutManager.orientation)
recyclerView.layoutManager = linearLayoutManager
recyclerView.addItemDecoration(dividerItemDecoration)
songAdapter.setOnItemClickListener { b, view, obj, position ->
if (b.text == "Stop") {
mediaPlayer!!.stop()
mediaPlayer!!.reset()
mediaPlayer!!.release()
mediaPlayer = null
b.text = "Play"
} else {
val runnable = Runnable {
try {
mediaPlayer = MediaPlayer()
mediaPlayer!!.setDataSource(obj.songUrl)
mediaPlayer!!.prepareAsync()
mediaPlayer!!.setOnPreparedListener { mp ->
mp.start()
seekBar.progress = 0
seekBar.max = mediaPlayer!!.duration
Log.d("Prog", "run: " + mediaPlayer!!.duration)
}
b.text = "Stop"
} catch (e: Exception) {
}
}
myHandler.postDelayed(runnable, 100)
}
}
checkUserPermission()
val t = runThread()
t.start()
}
inner class runThread : Thread() {
override fun run() {
while (true) {
try {
Thread.sleep(1000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
Log.d("Runwa", "run: " + 1)
if (mediaPlayer != null) {
seekBar.post { seekBar.progress = mediaPlayer!!.currentPosition }
Log.d("Runwa", "run: " + mediaPlayer!!.currentPosition)
}
}
}
}
private fun checkUserPermission() {
if (Build.VERSION.SDK_INT >= 23) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 123)
return
}
}
loadSongs()
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
123 -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
loadSongs()
} else {
Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show()
checkUserPermission()
}
else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
private fun loadSongs() {
val uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val selection = MediaStore.Audio.Media.IS_MUSIC + "!=0"
val cursor = contentResolver.query(uri, null, selection, null, null)
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
val name = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME))
val artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST))
val url = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA))
val s = SongInfo(name, artist, url)
_songs.add(s)
} while (cursor.moveToNext())
}
cursor.close()
songAdapter = SongAdapter(this#MainActivity, _songs)
}
}
}
This is the error:
"Error:(46, 44) Type mismatch: inferred type is (???, ???, ???, ???)
-> Any but SongAdapter.OnItemClickListener was expected Error:(46, 46) Cannot infer a type for this parameter. Please specify it explicitly."
Batch conversion to Kotlin is not the best way to learn the language. I suggest you to re-implement your Android component in Kotlin manually, to get the feeling of language.
The error you see says: "I can not understand how this lambda with 4 parameters can be an instance of SongAdapter.OnItemClickListener, please help". You can try anonymous class in this place.