I'm Trying to make a viewmodel scoped to my application to control logic related to showing of not showing pin in multi activity app .
I've used AndroidViewModel to pass the application to it and here is the class for AppViewModel
@HiltViewModel
class AppViewModel @Inject constructor(
private val getUserPassCodeUseCase: GetUserPassCodeUseCase,
private val isPasscodeInputUseCase: IsPasscodeInputUseCase,
private val clearAllDataUseCase: ClearAllDataUseCase,
@ApplicationContext private val context: Context
) : AndroidViewModel((context as App)) {
private val _openPin = MutableSharedFlow<Long>()
val openPin = _openPin.asSharedFlow()
// uptime in millis
private var time: Long = 0
private var restoreStatus = RestoreStatus.EMPTY
private var isPasscode = false
private var passCode = ""
init {
// get user status
}
private fun checkIfShouldLock() {
viewModelScope.launch {
isPasscode = withContext(IO) {
isPasscodeInputUseCase()
}
val userHasAccount = (restoreStatus == RestoreStatus.ID_SUBMISSION
|| restoreStatus == RestoreStatus.TERMS_AND_CONDITION
|| restoreStatus == RestoreStatus.ACTIVATE_CARD
|| restoreStatus == RestoreStatus.FULL_NAME
|| restoreStatus == RestoreStatus.COMPLETED)
if (true)
_openPin.emit (System.currentTimeMillis() )
}
}
fun onResume() {
updatePasscode()
if (!isPasscode) {
time = 0
return
}
val now = SystemClock.elapsedRealtime()
when {
time == 0L -> {
// remember first value
time = now
}
// check is session expired
now - time > sessionExpiredTime -> {
time = now
when (restoreStatus) {
RestoreStatus.COMPLETED -> checkIfShouldLock()
RestoreStatus.EMPTY -> {}
else -> {}
}
}
else -> {
time = now
}
}
}
fun onPause() {
updatePasscode()
viewModelScope.launch {
delay(Constants.PASSCODE_DELAY)
if (!isPasscode) {
time = 0
return@launch
}
// start "timer"
time = SystemClock.elapsedRealtime()
}
}
private fun updatePasscode() {
viewModelScope.launch {
isPasscode = withContext(IO) {
isPasscodeInputUseCase()
}
}
}
fun logout() {
viewModelScope.launch {
withContext(Dispatchers.IO) {
clearAllDataUseCase()
}
}
}
companion object {
const val MAIN_VIEW_MODEL_TAG = "AppViewModel"
}
}
and here is my application class and how i try to access the viewmodel
@HiltAndroidApp
class App : Application(), Application.ActivityLifecycleCallbacks,
Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory
override fun getWorkManagerConfiguration() =
Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
// uptime in millis
private var time: Long = 0
var appViewModel : AppViewModel ?=null
var currentActivity : String ?=null
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
appViewModel = ViewModelProvider.AndroidViewModelFactory(this).create(AppViewModel::class.java)
appViewModel?.openPin?.onEach {
// if (authToken.isNotEmpty())
when (currentActivity) {
SplashActivity::class.java.name,
PinActivity::class.java.name -> Unit
else -> {
startActivity(Intent(this, PinActivity::class.java).apply {
// flag of should end with result or not
// putExtra(Constants.IS_CAME_FROM_BACKGROUND, true)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}
}
}
setupCrashlytics()
if (BuildConfig.DEBUG)
Timber.plant(Timber.DebugTree())
else
Timber.plant(CrashReportingTree())
DyScan.init(this, Constants.DYSCAN_API_KEY)
registerActivityLifecycleCallbacks(this)
}
private fun setupCrashlytics() {
with(FirebaseCrashlytics.getInstance()) {
setCrashlyticsCollectionEnabled(!BuildConfig.DEBUG)
}
}
private fun isDeviceRooted(): Boolean {
var process: Process? = null
return try {
process = Runtime.getRuntime().exec("su")
true
} catch (e: Exception) {
Timber.i(e, "Rooted device command exception")
false
} finally {
if (process != null) {
try {
process.destroy()
} catch (e: Exception) {
Timber.i(e, "Rooted device command close exception")
}
}
}
}
private fun hideSystemBars(activity: Activity) {
val windowInsetsController =
ViewCompat.getWindowInsetsController(activity.window.decorView) ?: return
windowInsetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE
windowInsetsController.hide(WindowInsetsCompat.Type.navigationBars())
}
override fun onActivityCreated(activity: Activity, p1: Bundle?) {
currentActivity = activity.localClassName
if (isDeviceRooted()) {
Toast.makeText(
activity,
getString(R.string.rooted_device_message),
Toast.LENGTH_SHORT
).show()
activity.finishAffinity()
}
}
override fun onActivityStarted(p0: Activity) {
appViewModel?.onResume()
}
override fun onActivityResumed(p0: Activity) {}
override fun onActivityPaused(p0: Activity) {}
override fun onActivityStopped(activity: Activity) {
appViewModel?.onPause()
}
override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {}
override fun onActivityDestroyed(p0: Activity) {}
}
i keep getting RuntimeException: Cannot create an instance of class x.x.AppViewModel
2022-03-08 22:29:44.189 10889-10889/com.x.x E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.x.x, PID: 10889
java.lang.RuntimeException: Unable to create application com.x.x.App: java.lang.RuntimeException: Cannot create an instance of class com.x.x.AppViewModel
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6991)
at android.app.ActivityThread.access$1700(ActivityThread.java:274)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2093)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:233)
at android.app.ActivityThread.main(ActivityThread.java:8010)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:978)
Caused by: java.lang.RuntimeException: Cannot create an instance of class com.x.x.AppViewModel
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:230)
at com.x.x.App.onCreate(App.kt:54)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1208)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6986)
at android.app.ActivityThread.access$1700(ActivityThread.java:274)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2093)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:233)
at android.app.ActivityThread.main(ActivityThread.java:8010)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:978)
Caused by: java.lang.NoSuchMethodException: com.x.x.AppViewModel.<init> [class android.app.Application]
at java.lang.Class.getConstructor0(Class.java:2332)
at java.lang.Class.getConstructor(Class.java:1728)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:228)
at com.x.x.App.onCreate(App.kt:54)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1208)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6986)
at android.app.ActivityThread.access$1700(ActivityThread.java:274)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2093)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:233)
at android.app.ActivityThread.main(ActivityThread.java:8010)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:978)
What i am doing wrong in this implementation ? Is this the right way to implement a viewModel scoped to application ?