Not able to access parent class variable in child class in kotlin - android

In kotlin I am trying to use parent class variables in child class but I am not able to use them ,as I am new to kotlin I don't understand how to do it simply
I am trying to access sharedPerfernces and getting but its giving me null
class webViewActivity : AppCompatActivity{
internal var shared_preferences: SharedPreferences? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
shared_preferences = this.getPreferences(Context.MODE_PRIVATE)
mContext = this
}
class JavaScriptInterface(private val mContext: Context) {
#JavascriptInterface
fun exampleGet(path: String): String {
return webViewActivity().shared_preferences!!.getString(path, "")
//here shared_perferences is null
}
}
}
why I am not able to access parent class variables without constructor of parent class in child class . give me some suggestion ,little help will be appreciated

Add inner before JavaScriptInterface class.
Just like that:
inner class JavaScriptInterface(private val mContext: Context) {
#JavascriptInterface
fun exampleGet(path: String): String {
return shared_preferences!!.getString(path, "")
}
}

Related

Value not saved into the data class

I am currently building an app and I have added Dagger Hilt in order to define a single class to access data. the injection seems working fine but I am not able to store a value in the data class I use.
I have created a Singleton first, which is used by the code to set/get value from a data structure.
#Singleton
class CarListMemorySource #Inject constructor() : CarListInterface {
private var extendedCarList: ExtendedCarList? = null
override fun setListOfVehicles(listOfVehicles: List<item>)
{
extendedCarList?.listOfVehicles = listOfVehicles
}
}
When I am calling setListOfVehicles the listOfVehicules contains 10 items but
The data structure ExtendedCarList is defined as below:
data class ExtendedCarList(
var listOfVehicles: List<item>
)
The Singleton in passed using Hilt like for example in the viewModel below:
#HiltViewModel
class HomeScreenViewModel #Inject constructor(
private val carList: CarListMemorySource
): ViewModel() {
fun getList() {
--> DO SOMETHING TO Get A ListA
carList.setListOfVehicles(ListA)
}
}
And in the activity, using the viewModel, I am just doing this:
#AndroidEntryPoint
class HomeScreenActivity: AppCompatActivity() {
private val viewModel: HomeScreenViewModel by viewModels()
....
viewModel.getList()
....
}
Any idea why and how to fix it ?
Thanks
you never initialize extendedCarList.
extendedCarList?.listOfVehicles = listOfVehicles
above line is exactly the same as
if (extendedCarList != null) extendedCarList.listOfVehicles = listOfVehicles
But it never passes the null check.
I think just changing
private var extendedCarList: ExtendedCarList? = null
to
private val extendedCarList = ExtendedCarList()
might solve it

Call Application context in Activity

I'm trying to call my database (made with Room) from an activity on Android, but it needs the application context, and if I passed "application : Application" in the constructor of my Activity, the build crash and tell me :
java.lang.Class<com.exemple.instabus.PhotoActivity> has no zero argument constructor
Here is my code :
class PhotoActivity(application: Application) : AppCompatActivity() {
private val pictureDao = PictureDatabase.getDatabase(app)
//Some code ....
I need a context, i've tried to pass "this" but i got another error
Can someone give me some help please, I'm a beginner in this technology
EDIT:
Here is my database class, just to show you why I need an Application Context
#Database(entities = [Picture::class], version = 1, exportSchema = false)
abstract class PictureDatabase : RoomDatabase(){
abstract fun pictureDao() : PictureDao
companion object{
#Volatile
private var INSTANCE : PictureDatabase? = null
fun getDatabase(context: Context): PictureDatabase {
if(INSTANCE == null){
synchronized(this){
INSTANCE = Room.databaseBuilder(
context.applicationContext,
PictureDatabase::class.java,
"pictures.db"
).build()
}
}
return INSTANCE!!
}
}
}
Activity is something we do declare in the manifest and then start them using intent, However, the creation of an instance of an activity is done by the system and not by us. An instance is created using constructor, but if it is us then we can have any number of overloaded constructors. But the system needs only one constructor which should be a zero parameter constructor and it should be public.
So your activity signature
class PhotoActivity(application: Application) : AppCompatActivity() {
should be changed to
class PhotoActivity() : AppCompatActivity() {
To call the fun getDatabase(context: Context): PictureDatabase you can pass this from the activity. Activity is an indirect child of Context.
You can do it in the following ways,
private val pictureDao by lazy{ PictureDatabase.getDatabase(this) }
private lateinit var pictureDao:PictureDatabase
then in onCreate() initialize it
final override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.your_layout)
PictureDatabase.getDatabase(this)
//your logic goes here
}
First create a ViewModel, and inside the ViewModel you can access the your Dao, pictureDao.
class PictureViewModel(application: Application) :
AndroidViewModel(application) {
val pictureDao: PictureDao
init {
pictureDao =
PictureDatabase.getDatabase(application).pictureDao()
}
}
Then in your activity, initialize the ViewModel. And access your Dao.
class PhotoActivity : AppCompatActivity() {
private lateinit var pictureViewModel: PictureViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_photo)
pictureViewModel =
ViewModelProviders.of(this).get(PictureViewModel::class.java)
//You can now access your Dao class here:
val pictureDao = pictureViewModel.pictureDao
}
}
You should not pass Application to the constructor. You should pass applicationContext to getDatabase(), like private val pictureDao = PictureDatabase.getDatabase(applicationContext)

Thoughts Required ? Am i cleaning my ViewModel class properly?

I want to clean my viewmodel class by using raywenderlich approach.
So in this blog they have written that we there is a binder component which is not for user. but i want to use this class as helper class for viewmodel.
Here I am creating a class which i termed as Binder class for ViewModel, which my view will use to make contact with ViewModel class.
Now my Binder class is helping me in cleaning my ViewModel class like,
Edited :
So my View class will be like :
class UserDetailView : AppCompatActivity(), UiCallbacks {
lateinit var screenCallbacks: ScreenCallbacks;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
screenCallbacks = UserDetailBinder(this).bindLayout(R.layout.activity_learn_binding)
screenCallbacks.loadData()
}
override fun onUserNameChanged(userName: String) {
}
}
And my Binder class will be like :
class UserDetailBinder(private val userDetailView: UserDetailView) : ScreenCallbacks, ViewModelController {
private val userDetailObservers = UserDetailViewState(userDetailView)
init {
userDetailObservers.observe(userDetailView)
}
fun bindLayout(layout: Int): ScreenCallbacks {
binding<com.bold.job.databinding.ActivityLearnBindingBinding, OwnViewModel>(userDetailView, layout).let {
var viewModel = viewModel(userDetailView, OwnViewModel::class.java)
it.viewModel = viewModel
}
return this
}
override fun loadData() {
}
}

Access application context in companion object in kotlin

How can we access application context inside companion object in Android kotlin?
I have a companion object inside an abstract class and I want to access context to read Shared Preferences, but I'm not able to get the context.
UPDATE: I'm working with this stuff in an Android library and also the class that I'm working in is abstract
please see this go to link
class MainApplication : Application() {
init {
instance = this
}
companion object {
private var instance: MainApplication? = null
fun applicationContext() : Context {
return instance!!.applicationContext
}
}
override fun onCreate() {
super.onCreate()
// initialize for any
// Use ApplicationContext.
// example: SharedPreferences etc...
val context: Context = MainApplication.applicationContext()
}
}
Extends Application class like this
import android.app.Application
import android.content.Context
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
MyApplication.appContext = applicationContext
}
companion object {
lateinit var appContext: Context
}
}
then get context like this
val context = MyApplication.appContext
Actually I'm working inside an Android library and the class is abstract, so can't go with the already suggested solutions. However, I found way to do that.
Creat a lateinit Context field inside companion object.
abstract class MyClass {
companion object {
private lateinit var context: Context
fun setContext(con: Context) {
context=con
}
}
}
And then set it after the app has started
public class WelcomeActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
MyClass.Companion.setContext(this);
}
}
There is a super cool article from the guys from Firebase explaining how their SDK gets hold of the context.
Basically my contentprovider looks like this:
/**
* This content provider is only responsible to inject the application context into the common module.
*/
class ContextProvider : ContentProvider() {
companion object {
private val TAG = ContextProvider::class.java.simpleName
}
override fun onCreate(): Boolean {
context?.let {
Common.setContext(it)
return true
}
Logger.e(TAG, "Context injection to common failed. Context is null! Check ContextProvider registration in the Manifest!")
return false
}
override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String?): Cursor? = null
override fun getType(uri: Uri): String? = null
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int = 0
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>?): Int = 0
}
And the Common object, which I treat like an sibling of any Application class looks like this:
/**
* Partially working like an Application class by holding the appContext which makes it accessible inside this module.
*/
#SuppressLint("StaticFieldLeak")
object Common {
/**
* App appContext
*/
#Volatile
lateinit var appContext: Context
var isStoreVersion: Boolean = false
fun setContext(context: Context) {
appContext = context
}
}
As you can see I also enriched the Common object with a flag to store if the current build is a store version or not. Mainly because the BuildConfig of the app module is also not available in a module or library.
Don't forget to add the ContentProvider to the AndroidManifest of your library within the <application> tag
<provider android:name=".util.ContextProvider"
android:authorities="${applicationId}.common.util.contextprovider"
android:exported="false" />
You can save the instance directly inside a companion object and accessing it outside without problems, I think this approach is the simplest.
IMPORTANT: change the visibility of the instance property to private to ensure no one but Application has write access.
class App : Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
lateinit var instance: App
private set
}
}
class Test {
companion object {
lateinit var sharedPreferences: SharedPreferences
fun init(context: Context) {
// to prevent multiple initialization
if (!Companion::sharedPreferences.isInitialized) {
sharedPreferences = context.getSharedPreferences("preference_name", Context.MODE_PRIVATE)
}
}
}
}

Kotlin generic properties issue

I got some issues with Kotlin when translating my android project from java to Kotlin.
Say i have interface I and interface O which extends interface I.
interface I{
}
interface O: I{
}
And generic class A which have generic parameter V that extends interfaceI, and generic class B which extends class A:
abstract class A<V: I> {
}
class B : A<O>() {
}
When i'm trying to create such property:
val returnB: A<I>
get() = b
I'm getting compiler error 'required A, found B'. In Java this will work without any issues. How can i access this using Kotlin ?
I need to use this approach for Basic classes in my application.
BaseViewModel which have generic parameter for Navigator class:
abstract class BaseViewModel<N>(application: Application, val repositoryProvider:
RepositoryProvider) : AndroidViewModel(application) {
var navigator: N? = null
fun onDestroyView() {
navigator = null
}
open fun onViewAttached() {
}
}
BaseActivity class:
abstract class BaseActivity<T : ViewDataBinding, V : BaseViewModel<BaseNavigator>> : AppCompatActivity(),
BaseFragment.Callback, BaseNavigator {
// .......
private var mViewModel: V? = null
/**
* Override for set view model
* #return view model instance
*/
abstract val viewModel: V
// .......
}
BaseNavigator interface uses for VM - View communication:
interface BaseNavigator {
fun invokeIntent(intent: Intent?, b: Bundle?, c: Class<*>?,
forResult: Boolean, requestCode: Int)
fun replaceFragment(fragment: Fragment, addToBackStack: Boolean)
fun showDialogFragment(fragment: DialogFragment?, tag: String?)
fun showToast(message: String?)
}
Here example code where i'm extending these classes:
AuthViewModel:
class AuthViewModel(context: Application, repositoryProvider: RepositoryProvider) :
BaseViewModel<AuthNavigator>(context,repositoryProvider) {
// ....
}
AuthNavigator:
interface AuthNavigator : BaseNavigator {
fun requestGoogleAuth(code: Int)
fun requestFacebookAuth(callback: FacebookCallback<LoginResult>)
}
And AuthActivity class where error was appeared:
class AuthActivity : BaseActivity<ActivityAuthBinding, BaseViewModel<BaseNavagator>>(),
GoogleApiClient.OnConnectionFailedListener, AuthNavigator {
#Inject
lateinit var mViewModel: AuthViewModel
override val viewModel: BaseViewModel<BaseNavigator>
get() = mViewModel // Required:BaseViewModel<BaseNavigator> Found: AuthViewModel
}
I'm also tried to change generic parameter in AuthActivity from BaseViewModel to AuthViewModel, but compiler throws error 'required BaseViewModel'.
And i tried to change
override val viewModel: BaseViewModel<BaseNavigator>
get() = mViewModel
to
override val viewModel: AuthViewModel
get() = mViewModel
but in this case compiler throws error 'Property type is 'AuthViewModel', which is not a subtype type of overridden'.
update:
That works when i add out property to BaseViewModel:
BaseViewModel<out N : BaseNavigator>
But in this case i can only create
private var navigator: N? = null
which i need to be public so i can set it in the Activity class. Can i create public setter for this property? When i'm trying to create setter an error occurs:
private var navigator: N? = null
fun setNavigator(n: N) { // error: Type parameter N is declared as 'out' but occurs in 'in' position in type N
navigator = n
}
It looks like you are expecting the type parameter to behave covariantly. Kotlin uses declaration-site variance. If you do not specify the variance, generic type parameters are invariant.
In other words, right now there is no relationship between A<I> and A<O>. But if you declare
abstract class A<out V : I>
then A<O> is a subtype of A<I>.
(There is also <in> for contravariance, which works the other way around. See https://kotlinlang.org/docs/reference/generics.html for more details.)

Categories

Resources