An error occurred while trying to create a variable with audio (Android, Konlin) - android-mediaplayer

I try to assign the value to the variable into viewpage2 adapter. But there is an error into the line: val mediaPlayer = MediaPlayer.create(this, leter.sound) Here is chunk of my code:
class DetailViewPagerAdapter(
private val lettersList: List<Letters>,
private val activity: DetailActivity,
viewPager2: ViewPager2
) : RecyclerView.Adapter<DetailViewPagerAdapter.DetailViewHolder>() {
class DetailViewHolder(
itemView: View
) : RecyclerView.ViewHolder(itemView) {
val toolbar: Toolbar = itemView.findViewById(R.id.detail_toolbar)
private val banner: ImageView = itemView.findViewById(R.id.detail_image_view)
private val title: TextView = itemView.findViewById(R.id.title_detail_text_view)
private val sample_detail: TextView = itemView.findViewById(R.id.sample_detail)
private val description: TextView = itemView.findViewById(R.id.letter_description)
private val playButton: ImageButton = itemView.findViewById(R.id.playButton)
fun bind(
leter: Letters
) {
val mediaPlayer = MediaPlayer.create(this, leter.sound)
banner.setImageResource(leter.banner)
title.text = leter.title
sample_detail.text = leter.sample
description.text = leter.description
toolbar.title = leter.title
playButton.setOnClickListener {
mediaPlayer.start()
}
}
}
Android studio marked the create underline red. And I can not understand how to solve the issue. Please help to understand why this does not work properly.

Related

How do I take data from a firebase database and show it in a recycler view?

Im trying to get data from a firebase database collection and show it in a recycler view.
My collection is called savedDailyPlan. I am super new to kotlin and to android studio and I'm having a hard time finding the solution to this problem.
This is my data class:
data class DoneExercise(
var exerciseName : String = "",
var weight : Int = 0,
var sets : Int = 0,
var reps : Int = 0,
var date : Timestamp = Timestamp.now()
)
This is my adapter:
class DailyWorkoutAdapter(private val items: List<DoneExercise>) : RecyclerView.Adapter<DailyWorkoutAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.plan_list_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = items[position]
holder.property1.text = item.exerciseName
holder.property2.text = item.weight.toString()
//holder.property3.text = item.sets.toString()
//holder.property4.text = item.reps.toString()
}
override fun getItemCount(): Int = items.size
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val property1: TextView = itemView.findViewById(R.id.tv_day_name)
val property2: TextView = itemView.findViewById(R.id.tv_exercise_name)
//val property3: TextView = itemView.findViewById(R.id.property3)
//val property4: TextView = itemView.findViewById(R.id.property4)
}
}
I tried this but nothing shows up after i press the Positive Button:
private fun showPlanDialog() {
val db = FirebaseFirestore.getInstance()
val collectionRef = db.collection("savedDailyPlan")
val showDailyWorkoutHelper = LayoutInflater.from(context).inflate(R.layout.show_daily_exercises, null)
val dialogHelperDaily= ShowDailyWorkoutHelper(showDailyWorkoutHelper)
AlertDialog.Builder(context)
.setView(showDailyWorkoutHelper)
.setTitle("Generate plan")
.setPositiveButton("Choose day") {_, _ ->
recyclerView.layoutManager = LinearLayoutManager(context)
val dailyWorkoutsList: MutableList<DoneExercise> = arrayListOf()
val dailyWorkoutAdapter = DailyWorkoutAdapter(dailyWorkoutsList)
recyclerView.adapter = dailyWorkoutAdapter
}.show()
}
The Choose day part is something im planning on adding later, where I'd want to get only specific data from the collection (exercises that were created only for a certain date).

Expose value from SharedPreferences as Flow

I'm trying to get a display scaling feature to work with JetPack Compose. I have a ViewModel that exposes a shared preferences value as a flow, but it's definitely incorrect, as you can see below:
#HiltViewModel
class MyViewModel #Inject constructor(
#ApplicationContext private val context: Context
) : ViewModel() {
private val _densityFactor: MutableStateFlow<Float> = MutableStateFlow(1.0f)
val densityFactor: StateFlow<Float>
get() = _densityFactor.asStateFlow()
private fun getDensityFactorFromSharedPrefs(): Float {
val sharedPreference = context.getSharedPreferences(
"MY_PREFS",
Context.MODE_PRIVATE
)
return sharedPreference.getFloat("density", 1.0f)
}
// This is what I look at and go, "this is really bad."
private fun densityFactorFlow(): Flow<Float> = flow {
while (true) {
emit(getDensityFactorFromSharedPrefs())
}
}
init {
viewModelScope.launch(Dispatchers.IO) {
densityFactorFlow().collectLatest {
_densityFactor.emit(it)
}
}
}
}
Here's my Composable:
#Composable
fun MyPageRoot(
modifier: Modifier = Modifier,
viewModel: MyViewModel = hiltViewModel()
) {
val densityFactor by viewModel.densityFactor.collectAsState(initial = 1.0f)
CompositionLocalProvider(
LocalDensity provides Density(
density = LocalDensity.current.density * densityFactor
)
) {
// Content
}
}
And here's a slider that I want to slide with my finger to set the display scaling (the slider is outside the content from the MyPageRoot and will not change size on screen while the user is using the slider).
#Composable
fun ScreenDensitySetting(
modifier: Modifier = Modifier,
viewModel: SliderViewModel = hiltViewModel()
) {
var sliderValue by remember { mutableStateOf(viewModel.getDensityFactorFromSharedPrefs()) }
Text(
text = "Zoom"
)
Slider(
value = sliderValue,
onValueChange = { sliderValue = it },
onValueChangeFinished = { viewModel.setDisplayDensity(sliderValue) },
enabled = true,
valueRange = 0.5f..2.0f,
steps = 5,
colors = SliderDefaults.colors(
thumbColor = MaterialTheme.colors.secondary,
activeTrackColor = MaterialTheme.colors.secondary
)
)
}
The slider composable has its own viewmodel
#HiltViewModel
class PersonalizationMenuViewModel #Inject constructor(
#ApplicationContext private val context: Context
) : ViewModel() {
fun getDensityFactorFromSharedPrefs(): Float {
val sharedPreference = context.getSharedPreferences(
"MY_PREFS",
Context.MODE_PRIVATE
)
return sharedPreference.getFloat("density", 1.0f)
}
fun setDisplayDensity(density: Float) {
viewModelScope.launch {
val sharedPreference = context.getSharedPreferences(
"MEAL_ASSEMBLY_PREFS",
Context.MODE_PRIVATE
)
val editor = sharedPreference.edit()
editor.putFloat("density", density)
editor.apply()
}
}
}
I know that I need to move all the shared prefs code into a single class. But how would I write the flow such that it pulled from shared prefs when the value changed? I feel like I need a listener of some sort, but very new to Android development.
Your comment is right, that's really bad. :) You should create a OnSharedPreferenceChangeListener so it reacts to changes instead of locking up the CPU to constantly check it preemptively.
There's callbackFlow for converting listeners into Flows. You can use it like this:
fun SharedPreferences.getFloatFlowForKey(keyForFloat: String) = callbackFlow<Float> {
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
if (keyForFloat == key) {
trySend(getFloat(key, 0f))
}
}
registerOnSharedPreferenceChangeListener(listener)
if (contains(key)) {
send(getFloat(key, 0f)) // if you want to emit an initial pre-existing value
}
awaitClose { unregisterOnSharedPreferenceChangeListener(listener) }
}.buffer(Channel.UNLIMITED) // so trySend never fails
Then your ViewModel becomes:
#HiltViewModel
class MyViewModel #Inject constructor(
#ApplicationContext private val context: Context
) : ViewModel() {
private val sharedPreference = context.getSharedPreferences(
"MY_PREFS",
Context.MODE_PRIVATE
)
val densityFactor: StateFlow<Float> = sharedPreferences
.getFloatFlowForKey("density")
.stateIn(viewModelScope, SharingStarted.Eagerly, 1.0f)
}

How to pass intent with Adapter in Kotlin

I would like to pass intent to another activity class with Adapter via OnClick function in Kotlin. However, when I am using the debug function, I noticed that the intent has not passed successfully. May I know how can I solve this? From various sources online, I realized that I may be required to called the gList inside the "OnClick" function, but I cant seem to work it out.
class GoalAdapter(
private var gList: ArrayList<GoalList>
) : RecyclerView.Adapter<GoalAdapter.MyViewHolder>(), View.OnClickListener{
private var connection : Connection? = null
private var statement : Statement? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val v: View = LayoutInflater.from(parent.context).inflate(R.layout.activity_goal_list, parent, false)
return MyViewHolder(v)
}
override fun getItemCount(): Int {
return gList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val list = gList[position]
holder.goal.text = list.gName
holder.tAmount.text = list.tAmount.toString()
holder.sAmount.text = list.sAmount.toString()
holder.gnote.text = list.Note
holder.gdate.text = list.dDate
val sqlCon = SQLCon()
connection = sqlCon.connectionClass()!!
holder.delete.setOnClickListener {
try
{
val sql : String= "DELETE FROM Goals where gName = '${list.gName}' "
statement = connection!!.createStatement()
statement!!.executeQuery(sql)
}
catch (e : Exception)
{ }
}
holder.update.setOnClickListener(this)
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var goal: TextView = itemView.findViewById(R.id.txtGoal)
var tAmount : TextView = itemView.findViewById(R.id.txtTargetAmount)
var sAmount : TextView = itemView.findViewById(R.id.txtSavedAmount)
var gnote : TextView = itemView.findViewById(R.id.txtNote)
var gdate : TextView = itemView.findViewById(R.id.txtDate)
var delete : Button = itemView.findViewById(R.id.btnDeleteGoal)
var update : Button = itemView.findViewById(R.id.btnUpdateGoal)
}
override fun onClick(view: View?) {
when(view?.id)
{
R.id.btnUpdateGoal ->
{
val intent = Intent(view.context, EditGoalActivity::class.java)
intent.putExtra("gName", R.id.txtGoal)
intent.putExtra("tAmount", R.id.txtTargetAmount )
intent.putExtra("sAmount", R.id.txtSavedAmount )
intent.putExtra("Note", R.id.txtNote )
intent.putExtra("dDate", R.id.txtDate )
view.context.startActivity(intent)
}
}
}
}

Observer pattern is not working in Android MVVM

I am trying to update my view according to my data in my ViewModel, using MVVM
I need in the method onCacheReception to update my map whenever zones is changing
ViewModel
class MainViewModel constructor(application: Application) : AndroidViewModel(application),
CacheListener {
private val instance = Initializer.getInstance(application.applicationContext)
private val _zones = MutableLiveData<List<Zone>>()
val zones: LiveData<List<Zone>>
get() = _zones
init {
CacheDispatcher.addCacheListener(this)
}
override fun onCacheReception() {
val zonesFromDB: List<Zone>? = instance.fetchZonesInDatabase()
_zones.value = zonesFromDB
}
}
MainActivity
class MainActivity : AppCompatActivity(), EasyPermissions.PermissionCallbacks, OnMapReadyCallback {
private val mainViewModel: MainViewModel = ViewModelProvider(this).get(MainViewModel(application)::class.java)
private lateinit var initializer: Initializer
private lateinit var map: GoogleMap
private val REQUEST_CODE_LOCATIONS: Int = 100
private val permissionLocationsRationale: String = "Permissions for Fine & Coarse Locations"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (checkForLocationsPermission()) {
setUp()
mapSetUp()
}
mainViewModel.zones.observe(this, Observer { zones ->
zones.forEach {
Log.i("YES DATA", "Data has been updated")
val latLng = it.lat?.let { it1 -> it.lng?.let { it2 -> LatLng(it1, it2) } }
val markerOptions = latLng?.let { it1 -> MarkerOptions().position(it1) }
map.addMarker(markerOptions)
}
})
}
My Log is never displaying and it doesn't seem while debugging that mainView.zones.observe { } is called when I receive some new data in my ViewModel
In the onCacheReception(), replace:
_zones.value = zonesFromDB
by:
_zones.postValue(zonesFromDB)
in case your onCacheReception() function is called from a worker thread.

Why is createView never called?

My ultimate goal is to be able move my content widgets into a custom view and instantiate that view in an Anko layout in my MainView. I thought I had this working at one point, but I can't reproduce it.
When I run with the following code, the content in the createView of the MainContextView is never displayed and I never see the message "creating main context view", but I do see the message "main content view".
I start by creating a MainContextView
class MainContextView(context: Context) : ViewGroup(context), AnkoComponent<Context> {
lateinit var textBox: EditText
lateinit var button: Button
lateinit var clickCount: TextView
override fun createView(ui: AnkoContext<Context>) = with(ui) {
println("creating main context view")
verticalLayout {
themedEditText {
hint = "hi from main context"
}
button = themedButton {
text = "ok"
}
textBox = themedEditText {
hint = "hi"
}
clickCount = themedTextView {
text = "0"
}
}
}
override fun onLayout(p0: Boolean, p1: Int, p2: Int, p3: Int, p4: Int) {
println("onLayout called")
}
}
and call it from my main view
class MainView : AnkoComponent<MainActivity> {
lateinit var mainCtx: MainContextView
lateinit var textBox: EditText
lateinit var button: Button
lateinit var clickCount: TextView
lateinit var mainMenu: Menu
lateinit var settingItem: MenuItem
lateinit var otherItem: MenuItem
lateinit var floatingActionButton: FloatingActionButton
override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
coordinatorLayout {
verticalLayout {
themedAppBarLayout {
themedToolbar(theme = R.style.Base_ThemeOverlay_AppCompat_Dark_ActionBar) {
title = resources.getString(R.string.app_name)
popupTheme = R.style.AppTheme
mainMenu = menu
settingItem = mainMenu.add("My Settings")
otherItem = mainMenu.add("My Other")
}
}.lparams(width = matchParent, height = wrapContent)
// ************************************
// HERE IS THE CALL TO THE CONTEXT VIEW
mainCtx = mainContextView { println("main content view ") }
// *************************************
}.lparams(width = matchParent, height = wrapContent) {
}
floatingActionButton = floatingActionButton {
imageResource = android.R.drawable.ic_dialog_email
}.lparams {
margin = dip(10)
gravity = Gravity.BOTTOM or Gravity.END
}
}
}
}
The MainView is called set as the content view from the MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var presenter: MainPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val mainView = MainView()
mainView.setContentView(this)
presenter = MainPresenter(mainView)
}
}
And finally the ViewManger extensions
inline fun ViewManager.mainContextView(theme: Int = 0) = mainContextView(theme) {}
inline fun ViewManager.mainContextView(theme: Int = 0, init: MainContextView.() -> Unit): MainContextView {
return ankoView({ MainContextView(it) }, theme, init)
}
I found my problem, it had to do with my subView being defined as a subclass of View/ViewGroup, but not really implementing any of the methods.
As it turns out that was not really the right direction. The solution is based on a comment on this issue
Here are some of the key parts of the solution, I also created a Gist with the full code.
The extension function, creates an instance of the "SubView" class and then calls the createView() method in the ankoView() method. It also passes the created instance into the closure that is passed to the extension function, which is key as it allows access to the widgets contained within the view.
inline fun ViewManager.mainContentView(theme: Int = 0) = mainContentView(theme) {}
inline fun ViewManager.mainContentView(theme: Int = 0, init: View.(mainContentView: MainContentView) -> Unit): View {
val mainContentView = MainContentView()
return ankoView({ mainContentView.createView(AnkoContext.create(it)) }, theme, { init(mainContentView)} )
}
The "content view" creates the layout and holds a reference to the widgets.
class MainContentView : AnkoComponent<Context> {
lateinit var textBox: EditText
lateinit var button: Button
lateinit var clickCount: TextView
override fun createView(ui: AnkoContext<Context>) = with(ui) {
verticalLayout {
button = themedButton {
text = "ok"
}
textBox = themedEditText {
hint = "hi"
}
clickCount = themedTextView {
text = "0"
}
}
}
}
In the main view I have fields to refer to the widgets in the "subView" which I then initialize in the closure passed to the mainContentView instance.
lateinit var textBox: EditText
lateinit var button: Button
lateinit var clickCount: TextView
private lateinit var mainMenu: Menu
lateinit var settingItem: MenuItem
lateinit var otherItem: MenuItem
private lateinit var floatingActionButton: FloatingActionButton
override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
coordinatorLayout {
verticalLayout {
themedAppBarLayout {
themedToolbar(theme = R.style.Base_ThemeOverlay_AppCompat_Dark_ActionBar) {
title = resources.getString(R.string.app_name)
popupTheme = R.style.AppTheme
mainMenu = menu
settingItem = mainMenu.add("My Settings")
otherItem = mainMenu.add("My Other")
}
}.lparams(width = matchParent, height = wrapContent)
mainContentView {
button = it.button
textBox = it.textBox
clickCount = it.clickCount
}.lparams(width = matchParent, height = wrapContent)
}.lparams(width = matchParent, height = wrapContent)
floatingActionButton = floatingActionButton {
imageResource = android.R.drawable.ic_dialog_email
}.lparams {
margin = dip(10)
gravity = Gravity.BOTTOM or Gravity.END
}
}
}