I have the below code to set up SharedPreferences, but when I try to retrieve a previously saved value, it is always returning the default value.
So I'm saving "Tom" into SharedPreferences with a key of "profileName", but when I go to retrieve this value, I always get the default value returned of "failed".
Where am I going wrong? I have object MySharedPreferences set up so I can access it in various other places in my app without having to always reference the context. Instead it is initialised in the MainActivity.
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import java.util.*
import kotlin.concurrent.schedule
object MySharedPreferences {
lateinit var save: SharedPreferences.Editor
lateinit var retrieve: SharedPreferences
fun init(context: Context) {
save = context.getSharedPreferences("mySharedPreferences", MODE_PRIVATE).edit()
retrieve = context.getSharedPreferences("mySharedPreferences", MODE_PRIVATE)
}
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MySharedPreferences.init(this)
MySharedPreferences.save.putString("Tom", "profileName").commit()
Timer("", false).schedule(5000) {
val retrievalTest: String? =
MySharedPreferences.retrieve.getString("profileName", "failed")
Log.d("retrievalTest", "$retrievalTest")
}
}
}
Related
Trying to add an onclicklistener to the items in my recycler view, that will use an intent to open another activity. I've tried finding examples, but I can only find examples using Java or Kotlin examples that aren't using viewbinding.
package com.truuce.anotherrvtest
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.GridLayoutManager
import com.truuce.anotherrvtest.databinding.ActivityHeroBinding
class HeroActivity : AppCompatActivity() {
var binding: ActivityHeroBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHeroBinding.inflate(layoutInflater)
setContentView(binding?.root)
val adapter = CardAdapter(HeroList.heroList)
binding?.heroRV?.adapter = adapter
binding?.heroRV?.layoutManager = GridLayoutManager(applicationContext, 3)
}
override fun onDestroy() {
super.onDestroy()
binding = null
}
}
package com.truuce.anotherrvtest
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.truuce.anotherrvtest.databinding.HeroCardBinding
class CardAdapter(val heroList: List<Hero>) : RecyclerView.Adapter<CardAdapter.MainViewHolder>() {
inner class MainViewHolder(val heroBinding: HeroCardBinding) :
RecyclerView.ViewHolder(heroBinding.root) {
fun bindHero(hero: Hero){
heroBinding.heroNameTV.text = hero.heroName
heroBinding.heroIV.setImageResource(hero.image)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
return MainViewHolder(HeroCardBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
val hero = heroList[position]
holder.bindHero(hero)
}
override fun getItemCount() = heroList.size
}
tried adding View.OnClickListener to MainViewHolder, then implemented a member. OnClick(p0: View){}, but no idea how to get it working.
You should add a functional property for a click listener in your adapter.
The Activity can set the item click listener behavior.
class CardAdapter(
val heroList: List<Hero>,
val itemClickListener: (Hero)->Unit
) : RecyclerView.Adapter<CardAdapter.MainViewHolder>() {
inner class MainViewHolder(val heroBinding: HeroCardBinding) :
RecyclerView.ViewHolder(heroBinding.root) {
fun bindHero(hero: Hero) = with(heroBinding) {
heroNameTV.text = hero.heroName
heroIV.setImageResource(hero.image)
root.setOnClickListener { itemClickListener(hero) }
}
}
//...
}
// In Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHeroBinding.inflate(layoutInflater)
setContentView(binding?.root)
val adapter = CardAdapter(HeroList.heroList) { hero ->
// do something with hero item when it's clicked
}
binding?.heroRV?.adapter = adapter
binding?.heroRV?.layoutManager = GridLayoutManager(applicationContext, 3)
}
I followed a tutorial on how to do a listview in kotlin and I encountered this problem. I'm new to Kotlin and I don't understand what are the getters and setters for my activity. From what I read it should be put automatically.
private lateinit var listView ListView
Property getter or setter expected
All code:
package com.example.librariasemnelor;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button
import android.widget.ListView;
private lateinit var listView ListView
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val actionBar = supportActionBar
if(actionBar != null){
actionBar.title = "Meniu principal"
actionBar.setDisplayHomeAsUpEnabled(true)
}
listView = findViewById<ListView>(R.id.recipe_list_view)
// 1
val recipeList = Recipe.getRecipesFromFile("recipes.json", this)
// 2
val listItems = arrayOfNulls<String>(recipeList.size)
// 3
for (i in 0 until recipeList.size) {
val recipe = recipeList[i]
listItems[i] = recipe.title
}
// 4
val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, listItems)
listView.adapter = adapter
}
}
private lateinit ... should be placed inside the class block.
And : must be added just after listView.
class SecondActivity : AppCompatActivity() {
private lateinit var listView: ListView
override fun onCreate(savedInstanceState: Bundle?) {
...
}
...
}
I'm trying to play a sound outside of #Copmosable because of my application structure.
I have a validation routine which is in my viewmodel and based on the result I would like to trigger a sound but I cannot seem to get context working outside of #Composable
I get the following error in the MasterViewModel:
None of the following functions can be called with the arguments supplied.
create(Context!, Uri!) defined in android.media.MediaPlayer
create(Context!, Int) defined in android.media.MediaPlayer
Any pointers would be great thanks!!
package com.example.soundtest
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.example.soundtest.ui.theme.SoundTestTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SoundTestTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
Greeting()
}
}
}
}
}
#Composable
fun Greeting(mastVM: MasterViewModel = MasterViewModel()) {
Text("Play a sound...")
mastVM.playSound()
}
and the MasterViewModel
package com.example.soundtest
import android.media.MediaPlayer
class MasterViewModel {
fun playSound() {
val mp: MediaPlayer = MediaPlayer.create(this, R.raw.correct)
}
}
I have correct.mp3 save under res->raw-correct.mp3
MasterViewModel Error
I got it working!!!
I just include
#Composable
fun Greeting(mastVM: MasterViewModel = MasterViewModel()) {
val context = LocalContext.current
Text("Play a sound...")
mastVM.playSound(context: context)
}
and then in the MasterViewModel like this
fun playSound(context: Context) {
val mp: MediaPlayer = MediaPlayer.create(context, R.raw.correct)
}
I am currently building an app with a main activity which hosts a navHostFragment, and 3 fragments that are connected by a bottom navigation bar. My goal is to have each fragment use a Recycler View. I am using a Room database with an Adapter and LiveData for the "data". When I launch the app, I want to go to the Wallet fragment and see a vertical list of textViews. Because I am new and just starting out with this, I just wanted to have a very simple database of just text and then lay it out in a vertical format. Nothing too crazy yet. Any help would be greatly appreciated.
MainActivity
package com.example.android.pointmax
import android.os.Bundle
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import timber.log.Timber
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Plant tree to enable Debugging with Timber
Timber.plant(Timber.DebugTree())
// Find the bottomNavigation bar
val navView: BottomNavigationView = findViewById(R.id.nav_view)
// Find the fragment that will host the different fragments
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_wallet, R.id.navigation_recommended
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
}
WalletFragment
package com.example.android.pointmax.ui.wallet
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.android.pointmax.CardAdapter
import com.example.android.pointmax.R
class WalletFragment : Fragment() {
private lateinit var viewManager: RecyclerView.LayoutManager
private lateinit var viewAdapter: RecyclerView.Adapter<*>
private lateinit var recyclerView: RecyclerView
private lateinit var viewModel: WalletViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.fragment_wallet, container, false)
recyclerView = rootView.findViewById(R.id.wallet_recyclerview)
viewModel = ViewModelProvider(
this
).get(WalletViewModel::class.java)
val linearLayoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
viewManager = linearLayoutManager
// Observe the ViewModel
viewModel.allCards.observe(viewLifecycleOwner, Observer { cards ->
viewAdapter = CardAdapter(cards)
})
return rootView
}
}
WalletViewModel
package com.example.android.pointmax.ui.wallet
import android.app.Application
import androidx.lifecycle.*
import com.example.android.pointmax.database.Card
import com.example.android.pointmax.database.CardRepository
import com.example.android.pointmax.database.CardRoomDatabase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class WalletViewModel(application: Application) : AndroidViewModel(application) {
private val repository: CardRepository
// Using LiveData and caching what getAlphabetizedWords returns has several benefits:
// - We can put an observer on the data (instead of polling for changes) and only update the
// the UI when the data actually changes.
// - Repository is completely separated from the UI through the ViewModel.
val allCards: LiveData<List<Card>>
init {
val cardsDao = CardRoomDatabase.getDatabase(application, viewModelScope).cardDao()
repository = CardRepository(cardsDao)
allCards = repository.allCards
}
/**
* Launching a new coroutine to insert the data in a non-blocking way
*/
fun insert(card: Card) = viewModelScope.launch(Dispatchers.IO) {
repository.insert(card)
}
}
CardAdapter
package com.example.android.pointmax
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.android.pointmax.database.Card
class CardAdapter internal constructor(
private var cards: List<Card>
) : RecyclerView.Adapter<CardAdapter.CardViewHolder>() {
inner class CardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val cardItemView: TextView = itemView.findViewById(R.id.textView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.recyclerview_item, parent, false)
return CardViewHolder(itemView)
}
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
val current = cards[position]
holder.cardItemView.text = current.toString()
}
internal fun setWords(cards: List<Card>) {
this.cards = cards
notifyDataSetChanged()
}
override fun getItemCount() = cards.size
}
CardRepository
package com.example.android.pointmax.database
import androidx.lifecycle.LiveData
// Declares the DAO as a private property in the constructor. Pass in the DAO
// instead of the whole database, because you only need access to the DAO
class CardRepository(private val cardDao: CardDao) {
// Room executes all queries on a separate thread.
// Observed LiveData will notify the observer when the data has changed.
val allCards: LiveData<List<Card>> = cardDao.getCards()
suspend fun insert(card: Card) {
cardDao.insert(card)
}
}
CardRoomDatabase
package com.example.android.pointmax.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
// Annotates class to be a Room Database with a table (entity) of the Word class
#Database(entities = arrayOf(Card::class), version = 1, exportSchema = false)
public abstract class CardRoomDatabase : RoomDatabase() {
abstract fun cardDao(): CardDao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
#Volatile
private var INSTANCE: CardRoomDatabase? = null
fun getDatabase(
context: Context,
scope: CoroutineScope
): CardRoomDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
CardRoomDatabase::class.java,
"card_database"
).addCallback(CardDatabaseCallback(scope)).build()
INSTANCE = instance
return instance
}
}
private class CardDatabaseCallback(
private val scope: CoroutineScope
) : RoomDatabase.Callback() {
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
INSTANCE?.let { database ->
scope.launch {
populateDatabase(database.cardDao())
}
}
}
suspend fun populateDatabase(cardDao: CardDao) {
// Delete all content here.
cardDao.deleteAll()
// Add sample words.
var card = Card("Petal Credit Card")
cardDao.insert(card)
card = Card("Discover IT")
cardDao.insert(card)
}
}
}
}
Card
package com.example.android.pointmax.database
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
#Entity(tableName = "card_table")
data class Card(
#PrimaryKey
#ColumnInfo(name = "cardName")
var card: String
)
CardDao
package com.example.android.pointmax.database
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
#Dao
interface CardDao {
#Query("SELECT * from card_table")
fun getCards(): LiveData<List<Card>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(card: Card)
#Query("DELETE FROM card_table")
suspend fun deleteAll()
}
I can launch the application but as soon as I go to the Wallet fragment, the application crashes with the following:
2020-04-23 19:24:18.676 5048-5048/com.example.android.pointmax E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.pointmax, PID: 5048
java.lang.RuntimeException: Cannot create an instance of class com.example.android.pointmax.ui.wallet.WalletViewModel
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:275)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at com.example.android.pointmax.ui.wallet.WalletFragment.onCreateView(WalletFragment.kt:31)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:267)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at com.example.android.pointmax.ui.wallet.WalletFragment.onCreateView(WalletFragment.kt:31)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: java.lang.RuntimeException: cannot find implementation for com.example.android.pointmax.database.CardRoomDatabase. CardRoomDatabase_Impl does not exist
at androidx.room.Room.getGeneratedImplementation(Room.java:94)
at androidx.room.RoomDatabase$Builder.build(RoomDatabase.java:952)
at com.example.android.pointmax.database.CardRoomDatabase$Companion.getDatabase(CardRoomDatabase.kt:37)
at com.example.android.pointmax.ui.wallet.WalletViewModel.(WalletViewModel.kt:20)
The most essential part of your stack trace is here
`Caused by: java.lang.RuntimeException: cannot find implementation for com.example.android.pointmax.database.CardRoomDatabase. CardRoomDatabase_Impl does not exist at androidx.room.Room.getGeneratedImplementation(Room.java:94) at androidx.room.RoomDatabase$Builder.build(RoomDatabase.java:952) at com.example.android.pointmax.database.CardRoomDatabase$Companion.getDatabase(CardRoomDatabase.kt:37) at com.example.android.pointmax.ui.wallet.WalletViewModel.(WalletViewModel.kt:20)`
So the problem is Room couldn't generate class CardRoomDatabase_Impl (implementation of your abstract class CardRoomDatabase). Since you use Room annotation correctly, the only reason of your problem I could guess - you haven't included annotation processor in your build.gradle (app level). Check if it is in dependencies-section:
kapt "androidx.room:room-compiler:2.2.5"
I'm pulling JSON from a URL and i'm using Gson to convert it to a usable PIN code i need to show to the user.
package example
import android.app.IntentService
import android.content.Intent
import com.google.gson.Gson
import java.net.URL
class SendOrderIntentService : IntentService("SendOrderIntentService") {
override fun onHandleIntent(intent: Intent?) {
val orderNumber = (0..999999999999999).random()
val goodsCode = "1001004"
val orderURLPart1 = "URL"
val orderURLPart2 = "URL"
val orderURLPart3 = "URL"
val orderURL = orderURLPart1 + orderNumber + orderURLPart2 + goodsCode + orderURLPart3
val orderJson = URL(orderURL).readText()
println("Order number $orderNumber")
println(orderJson)
val pinCode = Gson().fromJson(orderJson, OrderData::class.java)
println(pinCode)
}
}
Everything works so far, pinCode has the right data.
package example
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_meal_reserved.*
import example.R
class MealReservedActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_meal_reserved)
setSupportActionBar(my_toolbar)
textViewCode.text = SendOrderIntentService().pinCode
}
}
But i can't get the textViewCode to display the pinCode, it just tells me its an
"Unresolved reference: pinCode"
I've been searching for an answer for hours so i think i'm missing something obvious and simple.
Thank you for reading.