How to get a FragmentManager for DialogFragment in Kotlin using Androidx? - kotlin

I am following a tutorial where the instructor is using the Android support library v7. In my app I am using the androidx version (as suggested on the Android Developer website). When I type the lines of code as instructed, Android Studio puts a strikethrough over part of the code where I try to obtain a FragmentManager and says: "getter for fragmentManager: FragmentManager! is deprecated. Deprecated in Java." I have searched so many posts where people were having similar issues but the solutions provided don't apply to my case. Some users were having mismatched support library versions and others didn't have the proper dependencies in the gradle file. As far as I can tell those issues don't apply here.
According to the androidx documentation for FragmentManager, it states, "Your activity must derive from FragmentActivity to use this. From such an activity, you can acquire the FragmentManager by calling FragmentActivity#getSupportFragmentManager." However, I am not using an Activity, the code is inside an inner class that extends the RecylcerView.ViewHolder class which is nested inside a class extending RecyclerView.Adapter. Is my only choice to use the android support library v7?
RecyclerView Adapter Class:
import android.app.Activity
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.e_product_row.view.*
import androidx.fragment.app.DialogFragment // greyed out as "unused import directive"
import androidx.fragment.app.FragmentManager // greyed out as "unused import directive"
class EProductAdapter(var context: Context, var arrayList : ArrayList<EProduct>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val productView = LayoutInflater.from(context).inflate(R.layout.e_product_row, parent, false)
return ProductViewHolder(productView)
}
override fun getItemCount(): Int {
return arrayList.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
// downcast holder to ProductViewHolder
(holder as ProductViewHolder).initializeRowComponents(arrayList.get(position).id,
arrayList[position].name,
arrayList[position].price,
arrayList[position].picture)
}
inner class ProductViewHolder(productView: View) : RecyclerView.ViewHolder(productView) {
fun initializeRowComponents(id: Int, name: String, price: Int, pictureName: String) {
itemView.txt_id.text = id.toString()
itemView.txt_name.text = name
itemView.txt_price.text = price.toString()
var pictureUrl = "http://192.168.0.21/OnlineStoreApp/osimages/$pictureName"
pictureUrl = pictureUrl.replace(" ", "%20")
Picasso.get().load(pictureUrl).into(itemView.img_product)
// initialize add item imageView
itemView.img_add_item.setOnClickListener {
var amountFragment = AmountFragment()
var fragmentManager = (itemView.context as Activity).fragmentManager // fragmentManager strikethrough text
amountFragment.show(fragmentManager, "TAG") // show function cannot be called with arguments supplied and won't compile
}
}
}
}
DialogFragment class:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
class AmountFragment : DialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? ): View? {
return inflater.inflate(R.layout.fragment_amount, container, false)
}
}
build.gradle(Module: app)
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.bryontaylor.onlinestoreapp"
minSdkVersion 24
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.android.volley:volley:1.1.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.fragment:fragment:1.2.4'
implementation 'androidx.fragment:fragment-ktx:1.2.4'
}

According to the androidx documentation for FragmentManager, it
states, "Your activity must derive from FragmentActivity to use this.
From such an activity, you can acquire the FragmentManager by calling
FragmentActivity#getSupportFragmentManager." However, I am not using
an Activity
Yes you are:
var fragmentManager = (itemView.context as Activity).fragmentManager
The only problem is that you're casting to the root Activity type and using the deprecated getFragmentManager(). You need to do exactly as the docs state: use FragmentActivity and getSupportFragmentManager():
var fragmentManager = (itemView.context as FragmentActivity).supportFragmentManager
Your show() call will then work because you're passing in the correct fragment manager type.
Hope that helps!

Related

Abstract room database implementation not found dagger hilt

I am new to Android dev and need to build my first project for university.
The university was using the old XML version with Java, but I wanted to learn Compose so I learnt Kotlin.
Now after getting everything setup, I am trying to use hiltViewModel() method to inject the view model inside the composable function and I am getting an error.
I watched this tutorial: https://www.youtube.com/watch?v=A7CGcFjQQtQ&t=10s and a few other stack overflow questions which suggest doing the same thing but I am not sure what is going on.
After getting this to work. Now it says a database class implementation is not found, but I expect Dagger Hilt to produce this? for Room database
Here is the basic code:
Dependencies:
build.gradle:
buildscript {
ext {
compose_version = '1.0.0'
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10"
//->Adding the Dagger Hilt class path here as suggested:
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
app/build.gradle:
plugins {
id 'com.android.application'
id 'kotlin-android'
//->Adding Kotlin annotation processing plugin and the dagger hilt plugin:
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdk 31
defaultConfig {
applicationId "ac.gre.mxpenseresit"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
useIR = true
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion '1.5.10'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
implementation 'androidx.activity:activity-compose:1.4.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
//This version for navigation is hard coded, probably will need to update later, but should be fine for assignment
implementation("androidx.navigation:navigation-compose:2.4.2")
//Room Dependencies:
def room_version = "2.4.2"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
//Coroutine dependency:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
//->Dagger Hilt Dependency for DI copied from official docs:
implementation "com.google.dagger:hilt-android:2.38.1"
kapt "com.google.dagger:hilt-compiler:2.38.1"
}
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
/**
* Adding the Hilt Android App dependency for the Application class
*/
#HiltAndroidApp
class MExpenseApp : Application() {
}
Main & only activity:
#AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MXPenseResitTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
App(modifier = Modifier)
}
}
}
}
}
Main Module:
**
* Setup dependencies:
*/
#Module
#InstallIn(ActivityComponent::class)
object MainModule {
#Provides
#Singleton
fun provideDb(application: Application): MExpenseDb {
return Room
.databaseBuilder(
application,
MExpenseDb::class.java,
"MExpenseDb"
).build()
}
/**
* We are type hinting to the Interface so we can change implementations
*/
#Provides
#Singleton
fun provideTripRepository(mExpenseDb: MExpenseDb): TripRepositoryContract {
return TripRepository(mExpenseDb.tripDao)
}
}
View Model:
#HiltViewModel
class TripListVm #Inject constructor(
private val tripRepository: TripRepository
) : ViewModel() {
/**
* #var trips
* Get all trips
*/
val trips = tripRepository.getTrips()
val testStr: String = "Test String!"
}
Composable:
#Composable
fun TripList(
navController: NavHostController,
modifier: Modifier = Modifier,
tripListVm: TripListVm = hiltViewModel()
) {
Text(text = "Trip List")
TripListItem(
name = "Trip Name",
date = "16/04/1885",
amount = 46.66,
modifier = modifier
)
}
Result:
I am also getting an error: [Dagger/MissingBinding] exception.
ac.gre.mxpenseresit.data.repository.TripRepository cannot be provided without an #Inject constructor or an #Provides-annotated method
Here is the code for the data layer:
Database class:
#Database(
entities = [
Trip::class,
Expense::class,
],
version = 1
)
abstract class MExpenseDb : RoomDatabase() {
abstract val tripDao: TripDao
}
Dao:
#Dao
interface TripDao {
/**
* Gets all trips
*/
#Query("SELECT * FROM Trip")
fun getTrips(): Flow<List<Trip>>
/**
* Gets trips based on a named search,
* This functionality can be extended later
*/
#Query("SELECT * FROM Trip t WHERE t.name LIKE :name")
suspend fun getTripsBySearchName(name: String)
/**
* Gets trip by Id
*/
#Query("SELECT * FROM Trip WHERE Trip.id = :id")
suspend fun getTripById(id: Long)
/**
*
*/
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun saveTrip(trip: Trip): Long
#Delete
suspend fun deleteTrip(trip: Trip)
}
Trip Repository Contract (Interface):
interface TripRepositoryContract {
fun getTrips(): Flow<List<Trip>>
suspend fun getTripById(id: Long): Trip?
suspend fun getTripsBySearchName(keyword: String): List<Trip>
suspend fun saveTripLocal(trip: Trip)
suspend fun saveTripRemote(trip: Trip)
suspend fun deleteTrip(trip: Trip)
}
Trip Repository implementation:
class TripRepository (val tripDao: TripDao) : TripRepositoryContract {
override fun getTrips(): Flow<List<Trip>> {
return tripDao.getTrips();
}
override suspend fun getTripById(id: Long): Trip? {
TODO("Not yet implemented")
}
override suspend fun getTripsBySearchName(keyword: String): List<Trip> {
TODO("Not yet implemented")
}
override suspend fun saveTripLocal(trip: Trip) {
TODO("Not yet implemented")
}
override suspend fun saveTripRemote(trip: Trip) {
TODO("Not yet implemented")
}
override suspend fun deleteTrip(trip: Trip) {
TODO("Not yet implemented")
}
}
Now the hiltViewModel() method works correctly, but I am getting a MExpenseDb_impl class not found
I looked at this stack overflow question: Android room persistent: AppDatabase_Impl does not exist
And it says to use the kept dependency, I already have the same thing with annotationProcessing so I'm not sure if this is an issue
All the tutorials online are either too long & irrelevant or too vague and I am trying to gain a deeper understanding about how this works.
Any advice would be appreciated
To provide the TripRepository you need to create a class with Hilt's Bind functions. Example:
#Module
#InstallIn(SingletonComponent::class)
interface RepositoryModule {
#Binds
#Singleton
fun bindTripRepository(
repository: TripRepository // here the class
): TripRepositoryContract // here the interface that the class implements
}
It is also necessary to modify the TripRepository. You must add the #Inject constructor() (even if your class doesn't use any dependencies) so that Hilt can create the class.
In your case it will look like this:
class TripRepository #Inject constructor(
val tripDao: TripDao
) : TripRepositoryContract {
// ...
}
The last change is in your MainModule:
#Module
#InstallIn(SingletonComponent::class)
object MainModule {
// providing the db implementation normally
#Provides
#Singleton
fun provideDb(application: Application): MExpenseDb {
return Room.databaseBuilder(
application,
MExpenseDb::class.java,
"MExpenseDb"
).build()
}
// providing your dao interface to be injected into TripRepository
#Provides
#Singleton
fun provideTripDao(
mExpenseDb: MExpenseDb
): TripDao = mExpenseDb.tripDao
}
Note that I changed from ActivityComponent to SingletonComponent in the hilt's modules, in both cases we want them to be singleton throughout the project, not just a component created for activity (which can also be singleton).
See components life cycles here.
I also recommend upgrading the Hilt version in your project, because you are using a very old one. The newest version is 2.42.
I think this video can help you better understand some things about Hilt. And there is also this repository of a project that uses Hilt together with Room that can be useful for you to consult.
Important edit:
In the TripListVm you are using the private val tripRepository: TripRepository (your class that implements the TripRepositoryContract), it is not recommended to directly inject the implementation class, instead you should inject the interface (TripRepositoryContract) and let Hilt take care of providing the implementation of it. Because that's what we taught Hilt to do in the RepositoryModule.
So to make it ideal, the TripListVm would look like this:
#HiltViewModel
class TripListVm #Inject constructor(
private val tripRepositoryContract: TripRepositoryContract
) : ViewModel() {
// ...
}

kotlinx-serialization not generating serializer in companion of classes annotated with #Serializable

I'm currently trying to create a parent class that can be serialized but has no own attributes. (They will be added in the inheriting classes)
For some reason there is no serializer()-method being generated.
I checked the following things:
The companion-object-class is available in the jar but is empty. No class header or anything. Just an empty file.
I have the serialization-plugin from JetBrains added to my build.gradle-file.
My class is annotated with the kotlinx.serialization.serializable-annotation.
Thank you for reading my question.
This is the exception:
java.lang.NullPointerException: Cannot invoke "me.trqhxrd.grapesrpg.impl.world.predefined.Void$Companion.serializer()" because "me.trqhxrd.grapesrpg.impl.world.predefined.Void.Companion" is null
at me.trqhxrd.grapesrpg.impl.world.BlockData.<clinit>(BlockData.kt:24) ~[GrapesRPG-1.0-SNAPSHOT-all.jar:?]
... 37 more
These code-snippets might be relevant:
build.gradle
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.6.21'
id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.21'
id 'com.github.johnrengelman.shadow' version '7.1.2'
id 'java'
}
group = 'me.trqhxrd'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
maven {
url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/'
}
maven {
url 'https://repo.codemc.org/repository/maven-public/'
}
maven {
url 'https://maven.wolfyscript.com/repository/public/'
}
maven {
url 'https://mvn.trqhxrd.de/releases'
}
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'com.github.seeseemelk:MockBukkit-v1.18:1.24.1'
compileOnly 'org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT'
compileOnly 'org.eclipse.aether:aether-api:1.1.0'
compileOnly 'org.eclipse.aether:aether-spi:1.1.0'
compileOnly 'org.eclipse.aether:aether-connector-basic:1.1.0'
compileOnly 'org.eclipse.aether:aether-transport-file:1.1.0'
compileOnly 'org.eclipse.aether:aether-transport-http:1.1.0'
compileOnly 'org.apache.maven:maven-aether-provider:3.3.9'
compileOnly 'org.apache.maven.resolver:maven-resolver-connector-basic:1.8.0'
compileOnly 'org.jetbrains.exposed:exposed-core:0.38.2'
compileOnly 'org.jetbrains.exposed:exposed-jdbc:0.38.2'
compileOnly 'org.jetbrains.exposed:exposed-dao:0.38.2'
compileOnly 'org.xerial:sqlite-jdbc:3.36.0.3'
compileOnly 'com.zaxxer:HikariCP:5.0.1'
compileOnly 'org.slf4j:slf4j-nop:2.0.0-alpha7'
compileOnly 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21'
compileOnly 'org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.3.2'
compileOnly 'org.jetbrains.kotlin:kotlin-reflect:1.6.21'
compileOnly 'com.google.guava:guava:31.1-jre'
shadow 'org.bstats:bstats-bukkit:3.0.0'
shadow 'me.trqhxrd:menus:1.2.1'
shadow 'com.wolfyscript.wolfyutils.spigot:wolfyutils-spigot:4.16.1.0'
shadow 'de.tr7zw:item-nbt-api:2.9.2'
shadow 'de.tr7zw:nbt-injector:2.9.2'
}
test {
useJUnitPlatform()
}
compileKotlin {
kotlinOptions.jvmTarget = '1.8'
}
compileTestKotlin {
kotlinOptions.jvmTarget = '1.8'
}
tasks.register('installPlugin', Copy) {
dependsOn 'shadowJar'
from layout.buildDirectory.dir("$buildDir/libs/GrapesRPG-1.0-SNAPSHOT-all.jar")
into layout.buildDirectory.dir("$projectDir/Server/plugins")
}
configurations {
shadow
compile.extendsFrom provided
provided.extendsFrom shadow
}
shadowJar {
relocate 'org.bstats', 'me.trqhxrd.shade.bstats'
configurations = [project.configurations.shadow]
from(project.sourceSets.main.output)
}
The parent class
package me.trqhxrd.grapesrpg.impl.world
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.jsonObject
import me.trqhxrd.grapesrpg.impl.world.predefined.Void
import me.trqhxrd.grapesrpg.util.ModuleKey
import me.trqhxrd.grapesrpg.api.world.BlockData as BlockDataAPI
#Serializable
abstract class BlockData(override val id: ModuleKey) : BlockDataAPI {
companion object {
val registry = mutableMapOf<ModuleKey, Pair<Class<out BlockData>, KSerializer<out BlockData>>>()
val KEY_VOID = ModuleKey("grapes", "void")
init {
registry[KEY_VOID] = Void::class.java to Void.serializer()
}
}
override fun save() = Json.encodeToString(this)
#Suppress("UNCHECKED_CAST")
override fun load(serialized: String) {
val key = Json.decodeFromJsonElement<ModuleKey>(
Json.decodeFromString<JsonObject>(serialized)["id"]!!.jsonObject
)
val serializer = registry[key]!!.second
val d = serializer.deserialize(Json.decodeFromString(serialized))
if (this::class.java.isAssignableFrom(d::class.java)) this.apply(d)
}
override fun apply(data: BlockDataAPI) {
if (!this::class.java.isAssignableFrom(data::class.java))
throw IllegalArgumentException(
"Can't load data from a class isn't assignable from the loading class. " +
"(Required: ${this::class.qualifiedName}, Received: ${data::class.qualifiedName})"
)
}
}
The child class
package me.trqhxrd.grapesrpg.impl.world.predefined
import kotlinx.serialization.Serializable
import me.trqhxrd.grapesrpg.impl.world.BlockData
import org.bukkit.event.block.BlockBreakEvent
import org.bukkit.event.block.BlockPlaceEvent
import org.bukkit.event.player.PlayerInteractEvent
#Serializable
class Void : BlockData(KEY_VOID) {
override fun onClick(event: PlayerInteractEvent) {}
override fun onBreak(event: BlockBreakEvent) {}
override fun onPlace(event: BlockPlaceEvent) {}
}
EDIT 1:
I try to create a new object of the Void-class via the empty constructor.
EDIT 2:
All the dependencies are compileOnly because this is a Minecraft-Plugin and most of the dependencies will be downloaded automatically by the Minecraft-Server. That way my Uber-Jar is smaller...
Well I just gave up and used Gson. If anyone finds an answer that doesn't just avoid the problem by removing the library feel free to post it here and I'll mark it as correct.
Thank you for reading...

Cannot create instance of MainViewModel - has no zero argument constructor with Compose Hilt

Using Jetpack Compose to build small Room database app. I keep getting error:
Cannot create an instance of class com.learning.kotlinreadexstingroomdb.MainViewModel
...
Caused by: java.lang.InstantiationException: java.lang.Class<com.learning.kotlinreadexstingroomdb.MainViewModel> has no zero argument constructor
at java.lang.Class.newInstance(Native Method)
MainActivity.kt
package com.learning.kotlinreadexstingroomdb
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import com.learning.kotlinreadexstingroomdb.ui.theme.KotlinReadExstingRoomDBTheme
class MainActivity : ComponentActivity() {
private val mainViewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
KotlinReadExstingRoomDBTheme {
MyApp()
}
}
}
#Composable
fun MyApp() {
val result by mainViewModel.readAll.collectAsState(initial= emptyList())
Column(
modifier = androidx.compose.ui.Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
if (result.isNotEmpty()) {
Text(
//text = person.name,
text = "There is Data!",
fontSize = MaterialTheme.typography.h4.fontSize
)
} else {
Text(
text = "Empty Database",
fontSize = MaterialTheme.typography.h4.fontSize
)
}
}
}
}
MainViewModel.kt
package com.learning.kotlinreadexstingroomdb
import androidx.lifecycle.ViewModel
import com.learning.kotlinreadexstingroomdb.data.PersonRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
#HiltViewModel
class MainViewModel
#Inject
constructor(personRepository: PersonRepository): ViewModel(){
val readAll = personRepository.readAll
}
build.gradle (:app)
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
...
dependencies {
def room_version = "2.4.1"
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
def dagger_hilt_version = "2.40.5"
implementation "com.google.dagger:hilt-android:$dagger_hilt_version"
kapt "com.google.dagger:hilt-compiler:$dagger_hilt_version"
def compose_version = "1.0.5"
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
implementation 'androidx.activity:activity-compose:1.4.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
}
I feel like I've tried everything I could find on the web, but nothing fixes it.
Note: Code is based on: Pre-Populate ROOM Database with Already Loaded Data | Android Studio Tutorial
Make sure you annotate your Application class to initialise Hilt
#HiltAndroidApp
class App : Application()
Your Application class needs to be registered in the manifest if it isn't already.
<application
android:name=".App"
... />
You also need to annotate your activities and fragments so that Hilt can inject into them.
#AndroidEntryPoint
class MainActivity
Add #AndroidEntryPoint annotation in main activity
#AndroidEntryPoint // this line
class MainActivity : ComponentActivity() {
private val mainViewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
KotlinReadExstingRoomDBTheme {
MyApp()
}
}
}

kotlin:Internal Error occurred while analyzing this expression

I am using navigation safeargs for passing arguments from one Fragment to another .
However , after I rebuild the project ,I could not parse the augument I had sent .I got an error below:
Internal Error occurred while analyzing this expression:
org.jetbrains.kotlin.descriptors.InvalidModuleException: Accessing invalid module descriptor <production sources for module FavDishKT.app> is a module[ModuleDescriptorImpl#10578057]
at org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl.assertValid(ModuleDescriptorImpl.kt:62)
at org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl.getPackage(ModuleDescriptorImpl.kt:84)
at org.jetbrains.kotlin.resolve.lazy.FileScopeFactory.createScopesForFile(FileScopeFactory.kt:62)
at org.jetbrains.kotlin.resolve.lazy.FileScopeFactory.createScopesForFile$default(FileScopeFactory.kt:61)
at org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl$cache$1.invoke(FileScopeProvider.kt:48)
at org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl$cache$1.invoke(FileScopeProvider.kt:46)
at org.jetbrains.kotlin.storage.LockBasedStorageManager$MapBasedMemoizedFunction.invoke(LockBasedStorageManager.java:578)
at org.jetbrains.kotlin.storage.LockBasedStorageManager$MapBasedMemoizedFunctionToNotNull.invoke(LockBasedStorageManager.java:651)
at org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl.getFileScopes(FileScopeProvider.kt:53)
at org.jetbrains.kotlin.resolve.lazy.FileScopeProvider$DefaultImpls.getFileResolutionScope(FileScopeProvider.kt:30)
at org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl.getFileResolutionScope(FileScopeProvider.kt:40)
at org.jetbrains.kotlin.resolve.lazy.DeclarationScopeProviderImpl.getResolutionScopeForDeclaration(DeclarationScopeProviderImpl.java:60)
at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.getOuterScope(LazyClassDescriptor.java:353)
at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$scopeForClassHeaderResolution$1.invoke(ClassResolutionScopesSupport.kt:44)
at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$scopeForClassHeaderResolution$1.invoke(ClassResolutionScopesSupport.kt:43)
at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageM...
My configures:
build.gradle(project:FavDishKT)
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.5.0"
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
def nav_version = "2.3.2"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
jcenter() // Warning: this repository is going to shut down soon
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
**build.gradle(Module:FavDishKT.app)**
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
// For more details visit https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args
id 'androidx.navigation.safeargs.kotlin'
id 'kotlin-parcelize'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.gearsrun.favdishkt"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
exclude 'META-INF/atomicfu.kotlin_module'
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation("androidx.recyclerview:recyclerview:1.2.1")
// For control over item selection of both touch and mouse driven selection
implementation("androidx.recyclerview:recyclerview-selection:1.1.0")
implementation 'com.airbnb.android:lottie:3.4.1'
implementation 'de.hdodenhof:circleimageview:3.1.0'
implementation 'com.makeramen:roundedimageview:2.3.0'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation("com.squareup.okhttp3:okhttp:4.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
implementation 'com.google.code.gson:gson:2.8.7'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.github.bumptech.glide:glide:4.12.0'
kapt 'com.github.bumptech.glide:compiler:4.12.0'
}
dependencies {
//room
def room_version = "2.3.0"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
}
My idea is to pass the dish item from the homepage to another details page ,as follow ,Therefor ,I will need to pass a entity which names :FavDish
FavDish entity:
#Parcelize
#Entity(tableName = "fav_dishes_table")
data class FavDish(
#PrimaryKey(autoGenerate = true) val id:Int = 0,
#ColumnInfo val image:String,
#ColumnInfo(name = "image_source")val imageSource:String,
#ColumnInfo val title:String,
#ColumnInfo val type:String,
#ColumnInfo val category:String,
#ColumnInfo val ingredients:String,
#ColumnInfo(name = "cooking_time") val cookingTime:String,
#ColumnInfo(name = "instructions") val directionToCook:String,
#ColumnInfo(name = "favorite_dish") var favoriteDish:Boolean = false,
):Parcelable
And then , I have modified the navigation.xml ,from the AllDishes page to the DishDetailsFragment , and pass the entity reference as aguments :
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mobile_navigation"
app:startDestination="#+id/navigation_all_dishes">
<fragment
android:id="#+id/navigation_all_dishes"
android:name="com.gearsrun.favdishkt.view.fragments.AllDishesFragment"
android:label="Home"
tools:layout="#layout/fragment_all_dishes" >
<action
android:id="#+id/action_navigation_all_dishes_to_dishDetailsFragment"
app:destination="#id/dishDetailsFragment" />
</fragment>
<fragment
android:id="#+id/navigation_favorite_dishes"
android:name="com.gearsrun.favdishkt.view.fragments.FavoriteFragment"
android:label="Favorite"
tools:layout="#layout/fragment_favorite_dishes" >
<action
android:id="#+id/action_navigation_favorite_dishes_to_dishDetailsFragment"
app:destination="#id/dishDetailsFragment" />
</fragment>
<fragment
android:id="#+id/navigation_random_dish"
android:name="com.gearsrun.favdishkt.view.fragments.RandomFragment"
android:label="Random"
tools:layout="#layout/fragment_random_dish" />
<fragment
android:id="#+id/dishDetailsFragment"
android:name="com.gearsrun.favdishkt.view.fragments.DishDetailsFragment"
android:label="fragment_dish_details"
tools:layout="#layout/fragment_dish_details" >
<argument
android:name="dishDetails"
app:argType="com.gearsrun.favdishkt.model.entities.FavDish"/>
</fragment>
</navigation>
From my AllDishsFragment (home page ) ,I use findNavController,and navigate to the detailsDishFragment ,and pass the entity to it :
class AllDishesFragment : Fragment(){
private var _binding: FragmentAllDishesBinding? = null
private val binding get() = _binding!!
private val mFavDishViewModel :FavDishViewModel by viewModels{
FavDishViewModelFactory((requireActivity().application as FavDishApplication).repository)
}
private lateinit var mFavDishAdapter: FavDishAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentAllDishesBinding.inflate(inflater, container, false)
val root: View = binding.root
binding.imgAddHome.setOnClickListener {
val intent = Intent(context,AddUpdateActivity::class.java)
startActivity(intent)
}
binding.rvHome.layoutManager = GridLayoutManager(requireActivity(),2)
mFavDishAdapter = FavDishAdapter(this)
binding.rvHome.adapter = mFavDishAdapter
mFavDishViewModel.allDishesList.observe(viewLifecycleOwner){dishes->
dishes.let{
if(it.isNotEmpty()){
binding.rvHome.visibility=View.VISIBLE
binding.txtNotice.visibility=View.GONE
mFavDishAdapter.getAllDishes(it)
}else{
binding.rvHome.visibility =View.GONE
binding.txtNotice.visibility=View.VISIBLE
}
}
}
return root
}
fun dishDetails(favDish: FavDish){
if(requireActivity() is MainActivity){
(activity as MainActivity?)!!.hideBottomNavigationView()
}
findNavController().navigate(AllDishesFragmentDirections.actionNavigationAllDishesToDishDetailsFragment(favDish))
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
And the DetailDishFragment ,where it should receive the args and parse it occured the error :
class DishDetailsFragment : Fragment() {
private val mFavDishViewModel : FavDishViewModel by viewModels{
FavDishViewModelFactory((requireActivity().application as FavDishApplication).repository)
}
private var mBinding : FragmentDishDetailsBinding? =null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mBinding = FragmentDishDetailsBinding.inflate(inflater,container,false)
return mBinding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val args:DishDetailsFragmentArgs by navArgs()
args.let{
try {
Glide.with(requireActivity())
.load(it.dishDetails.image)
}catch(e: IOException){
}
}
}
}
And the problem description :
Internal Error occurred while analyzing this expression:
org.jetbrains.kotlin.descriptors.InvalidModuleException: Accessing invalid module descriptor <production sources for module FavDishKT.app> is a module[ModuleDescriptorImpl#10578057]
at org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl.assertValid(ModuleDescriptorImpl.kt:62)
at org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl.getPackage(ModuleDescriptorImpl.kt:84)
at org.jetbrains.kotlin.resolve.lazy.FileScopeFactory.createScopesForFile(FileScopeFactory.kt:62)
at org.jetbrains.kotlin.resolve.lazy.FileScopeFactory.createScopesForFile$default(FileScopeFactory.kt:61)
at org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl$cache$1.invoke(FileScopeProvider.kt:48)
at org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl$cache$1.invoke(FileScopeProvider.kt:46)
at org.jetbrains.kotlin.storage.LockBasedStorageManager$MapBasedMemoizedFunction.invoke(LockBasedStorageManager.java:578)
at org.jetbrains.kotlin.storage.LockBasedStorageManager$MapBasedMemoizedFunctionToNotNull.invoke(LockBasedStorageManager.java:651)
at org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl.getFileScopes(FileScopeProvider.kt:53)
at org.jetbrains.kotlin.resolve.lazy.FileScopeProvider$DefaultImpls.getFileResolutionScope(FileScopeProvider.kt:30)
at org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl.getFileResolutionScope(FileScopeProvider.kt:40)
at org.jetbrains.kotlin.resolve.lazy.DeclarationScopeProviderImpl.getResolutionScopeForDeclaration(DeclarationScopeProviderImpl.java:60)
at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.getOuterScope(LazyClassDescriptor.java:353)
at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$scopeForClassHeaderResolution$1.invoke(ClassResolutionScopesSupport.kt:44)
at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$scopeForClassHeaderResolution$1.invoke(ClassResolutionScopesSupport.kt:43)
at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageM...
Could anyone helps ?Thank you so much in advance !!
install and uninstall kotlin plugin.it Worked for me
For My case, I had the same issue but it was only happening in one particular project, The rest were okay so I tried upgrading the build Gradle plugin of the specific project to 7.3.0 and It worked. You can try to see if this helps
Invalid Cache Restart with both vcs and system cache option selected can resolved this issue. It worked for me.
Update: These warnings frequently coming in Dolphin Version of Android Studio(stable) causing faliure of auto suggestion and layout rendering. If you want to get rid of them for stable version you can use Chipmunk version or try canary channels of dolphin.
Updating ktx implementation and syncing gradle worked for me!

Can't import androidx.datastore.dataStore (trying to recreate Google Codelab Example)

Problem:
I'm trying to recreate this codelab tutorial project https://developer.android.com/codelabs/android-proto-datastore, but Android Studio can't import androidx.datastore.dataStore
Steps:
create new Kotlin project with an empty Acivity
modify gradle file
Switch to Android Studio's Project view
create a folder named proto inside of app/src/main
create and modify file user_prefs.proto inside of app/src/main/proto
Build -> Clean Project -> rebuild project
Create a serializer class called UserPreferencesSerializer
Trying to add the following Code to the empty MainActivity.kt
private const val DATA_STORE_FILE_NAME = "user_prefs.pb"
private val Context.userPreferencesStore: DataStore
by dataStore(
fileName = DATA_STORE_FILE_NAME,
serializer = UserPreferencesSerializer )
After this step Android Studio marks dataStore and shows the warning "Unresolved reference: dataStore" I'm also unable to import androidx.datastore.dataStore, but I can't find a missing import in my gradle file. Please, can someone tell me how I can resolve this problem?
Code:
build.gradle
plugins {
id 'com.android.application'
id 'kotlin-android'
id "com.google.protobuf" version "0.8.12"
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.example.test"
minSdkVersion 28
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation "androidx.datastore:datastore-core:1.0.0-alpha08"
implementation "com.google.protobuf:protobuf-javalite:3.11.0"
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.10.0"
}
// Generates the java Protobuf-lite code for the Protobufs in this project. See
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
// for more information.
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
}
user_prefs.proto
syntax = "proto3";
option java_package = "com.example.test";
option java_multiple_files = true;
message UserPreferences {
// filter for showing / hiding completed tasks
bool show_completed = 1;
}
UserPreferencesSerializer
package com.example.test
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer
import com.google.protobuf.InvalidProtocolBufferException
import java.io.InputStream
import java.io.OutputStream
object UserPreferencesSerializer : Serializer<UserPreferences> {
override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance()
override suspend fun readFrom(input: InputStream): UserPreferences {
try {
return UserPreferences.parseFrom(input)
} catch (exception: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", exception)
}
}
override suspend fun writeTo(t: UserPreferences, output: OutputStream) = t.writeTo(output)
}
MainActivity.kt
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.datastore.core.DataStore
private const val DATA_STORE_FILE_NAME = "user_prefs.pb"
private val Context.userPreferencesStore: DataStore<UserPreferences> by dataStore(
fileName = DATA_STORE_FILE_NAME,
serializer = UserPreferencesSerializer
)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
I also had this problem while working on a project.
I found that I was using datastore-core and needed datastore-preferences. So I changed my dependency declaration from:
implementation 'androidx.datastore:datastore-core:1.0.0-alpha08'
to:
implementation 'androidx.datastore:datastore-preferences:1.0.0'
Possibly there was a breaking change between alpha08 and the 1.0.0 release.
The dataStore delegate is part of the androidx.datastore:datastore library.
Add the dependency to your modudle's build.gradle file. Replace $dataStoreVersion with the version of data store which you use, e.g. 1.0.0:
implementation "androidx.datastore:datastore:$dataStoreVersion"
You can find the available versions here in Google's Maven repository.
After adding this dependency, you can use by dataStore by adding the following import to your class:
import androidx.datastore.dataStore
In Android documentation:
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
This line should be at the top of your code.
See DataStore documenation
After a lot of searching, The problem is simple. Make sure Gradle is not in offline mode.
Also, ensure that you use this dependency.
implementation "androidx.datastore:datastore-preferences:$dataStoreVersion"