Broadcast Receiver wont start Activity when app is not running - kotlin

I am trying to open activity through Broadcast Receiver.
Here is the Manifest :
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.andreasgift.myclock">
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#drawable/ic_launcher"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".Alarm.AlarmNotifActivity" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SET_ALARM" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<receiver android:name=".Alarm.AlarmReceiver" />
</application>
</manifest>
Here is the activity that I am trying to start
class AlarmNotifActivity : AppCompatActivity() {
private val snoozeTiming = 600000L
private var label: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
Log.d("ALARM","alarmnotif activity")
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_alarm_notif)
intent.getStringExtra(Constants().ALARM_LABEL_KEY)?.let {
label = it
this.label_tv.setText(label)
}
}
override fun onResume() {
super.onResume()
Log.d("ALARM","RESUME ACR")
}
override fun onStart() {
super.onStart()
Log.d("ALARM","ON START")
}
fun dismissButton(view: View) {
finish()
}
fun snoozeButton(view: View) {
val alarmManager = this.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val nextintent = Intent(this, AlarmReceiver::class.java)
label?.let { intent.putExtra(Constants().ALARM_LABEL_KEY, label) }
val pendingIntent = PendingIntent.getBroadcast(
this#AlarmNotifActivity,
0,
nextintent,
PendingIntent.FLAG_UPDATE_CURRENT
)
alarmManager.set(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + snoozeTiming,
pendingIntent
)
finish()
}
}
And here is the Broadcast Receiver class:
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val openNext = Intent(context, AlarmNotifActivity::class.java)
openNext.putExtra(Constants().ALARM_LABEL_KEY, intent.getStringExtra(Constants().ALARM_LABEL_KEY))
openNext.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(openNext)
Log.d("ALARM","Broadcast receiver working")
}
}
The feature is working fine when the application is open. However it seems wont execute the startActivity(Intent) when the application is closed. But it did execute the last log.d in the broadcast receiver.
I have tried to replace context with context.applicationContext, but it didn't change anything.
I have tried to replace the activity class to another activity, but also no succeed.
I have tried to replace the action from open activity into showing toast. Toast is shown, work perfectly fine.
Here is the logcat after the Broadcast Receiver execute the onReceive
2019-11-05 16:42:29.962 1992-3189/system_process I/ActivityTaskManager: START u0 {flg=0x10000000 cmp=com.andreasgift.myclock/.Alarm.AlarmNotifActivity (has extras)} from uid 10133
2019-11-05 16:42:29.962 1992-3189/system_process W/ActivityTaskManager: Background activity start [callingPackage: com.andreasgift.myclock; callingUid: 10133; isCallingUidForeground: false; isCallingUidPersistentSystemProcess: false; realCallingUid: 10133; isRealCallingUidForeground: false; isRealCallingUidPersistentSystemProcess: false; originatingPendingIntent: null; isBgStartWhitelisted: false; intent: Intent { flg=0x10000000 cmp=com.andreasgift.myclock/.Alarm.AlarmNotifActivity (has extras) }; callerApp: ProcessRecord{c678bfd 7783:com.andreasgift.myclock/u0a133}]
2019-11-05 16:42:29.963 1992-2036/system_process I/libprocessgroup: Successfully killed process cgroup uid 10095 pid 6926 in 79ms
2019-11-05 16:42:29.964 7783-7783/com.andreasgift.myclock D/ALARM: Broadcast receiver working
Anybody have idea what goes wrong here?

Related

How to pass value from Activity to Widget use Intent action extra

I would like pass an value to my widget using Intent. But something wrong, I can not receive my intent extra value..
My MainActivity:
var testValue = "test"
val intent = Intent("my.action.string")
intent.putExtra("extra", testValue)
sendBroadcast(intent)
Manifest:
<receiver
android:name=".ui.widget.RateWidget"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="my.action.string" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/rate_widget_info" />
</receiver>
My Widget class:
class RateWidget: AppWidgetProvider() {
private var myMessage = "default"
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
val action = intent?.action
if (action == "my.action.string") {
val state = intent.extras!!.getString("extra")
if (state != null) {
myMessage = state
}
}
}
Try it in widget:
val state = intent.getStringExtra("extra")

Open application from scanned qr code in default camera

I am using breoadcast receiver to open my app when scanning a qr code from the device default camera. I tested this with Android 11 and Android 12 and it works fine. When I tried this out with Android 13 the qr string is shown when I click on it, it's opened on the browser.
Here is my implementation of the Qr code scan method:
In AndroidManifest.xml
<activity
android:name="com.cibingenierie.nomadmobile.screens.MainActivity"
android:exported="true"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="nm" />
</intent-filter>
</activity>
On MainActivity.kt
here is my onCrete function
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = intent
val data = intent.data.toString()
val dataArgs = data.replace("nm://", "")
onBroadcastReceived()
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
setContent {
AppTheme {
NomadApplication(userViewModel, registerViewModel, ticketViewModel,intent)
}
}
}
and onBroadcastReceived function
private fun onBroadcastReceived() {
val receiver = (object : ExternalReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val launchIntent = Intent("android.intent.action.MAIN")
launchIntent.putExtra("camera", "")
context.startActivity(launchIntent)
}
})
}
Can you please help me fix this?
I dont get an error it's actually the qr code string always detected as a web url

How to inject class into MainActivity with Koin

I want to create a Splash screen and show it as long as the authentication state of the user gets determined. I have a global singleton called AuthStateController which holds my state and some extra functions.
But because the installSplashScreen function is outside of a composable I can't use Koin to inject the AuthStateController class to get access to my loading state.
Below is my MainActivity with all my Koin modules. And the installSplashScreen function.
class MainActivity : ComponentActivity() {
// not allowed because outside of Composable
private val authStateController: AuthStateController by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startKoin {
androidLogger(if (BuildConfig.DEBUG) Level.ERROR else Level.NONE)
androidContext(this#MainActivity)
modules(listOf(appModule, networkModule, viewModelModule, interactorsModule))
}
installSplashScreen().apply {
setKeepVisibleCondition {
// need AuthStateController instance to determine loading state
authStateController.state.value.isLoading
}
}
setContent {
M3Theme {
SetupNavGraph()
}
}
}
}
}
This is the Koin module that provides my AuthStateController class:
val appModule = module {
single { AuthStateController(get()) }
}
And this is my AuthStateController class which holds my state and some extra functions:
class AuthStateController(
private val getMeInterceptor: GetMeInterceptor
) {
val state: MutableState<AuthState> = mutableStateOf(AuthState())
fun fetchMe() {
val me = getMeInterceptor.execute().collect(CoroutineScope(Dispatchers.IO)) { dataState ->
dataState.data?.let {
state.value =
state.value.copy(profile = it, isAuthenticated = true, isLoading = false)
}
}
}
init {
val token = settings.getString(Constants.AUTH_TOKEN)
if (token.isNotBlank()) {
fetchMe()
state.value = state.value.copy(authToken = token)
}
}
}
How can I get access to the singleton created with Koin inside the MainActivity and use it inside the installSplashScreen function?
Edit
Android Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.sessions_clean.android">
<uses-permission android:name="android.permission.INTERNET" />
<application
// app crashes when adding line below
android:name=".MainApplication"
android:allowBackup="false"
android:supportsRtl="true"
android:theme="#style/Theme.App.Starting"
android:usesCleartextTraffic="true">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="#style/Theme.App.Starting">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
When I add android:name to the already existing application tag the App crashes instantly.
But when I create a new application tag for the new MainApplication I get errors in the IDE like Attribute android:allowBackup is not allowed here
I think you can startKoin inside the Application
MainApplication.kt
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
...
}
}
}
AndroidManifest.xml
<manifest ...>
<application
android:name=".MainApplication"
...>
...
</application>
</manifest>

java.lang.IllegalStateException: KoinApplication has not been started

I am learning kotlin using koin. While running the application in catlog I see the following message.
java.lang.IllegalStateException: KoinApplication has not been started
though I have used startKoin in MyApplication
class MyApplication : Application() {
var listOfModules = module {
single { GitHubServiceApi() }
}
override fun onCreate() {
super.onCreate()
startKoin {
androidLogger()
androidContext(this#MyApplication)
modules(listOfModules)
}
}
}
Adding "android:name=".TheApplication" in the Manifest file solved the issue.
android:name=".TheApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_app_icon_round"
android:supportsRtl="true"
android:theme="#style/Theme.Shrine">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
"android:name=".TheApplication" is the Class Name from Koin
class TheApplication : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
startKoin {
androidLogger()
androidContext(androidContext = this#TheApplication)
modules(
listOfModules
)
}
}
}
In you manifest.xml,
<application>
android:name=".MyApplication"
...
</application
Add this line in application tag.
Basically, you need to give the name of the class where you called startKoin() method, in the Manifest as an application name. And this will let your configure logging, properties loading and modules. Check this out: https://doc.insert-koin.io/#/koin-core/dsl
If you write unit test, you also should add startKoin and inject dependencies:
#Before
fun startKoinForTest() {
startKoin {
modules(DI.modules(...))
}
}
#After
fun stopKoinAfterTest() = stopKoin()
See also Koin Android Test.
private fun getKoin(activity: ComponentActivity): Koin {
return if (activity is KoinComponent) {
activity.getKoin()
} else {
GlobalContext.getOrNull() ?: startKoin {
androidContext(activity)
modules(listModule)
}.koin
}
}
fun ComponentActivity.contextAwareActivityScope() = runCatching {
LifecycleScopeDelegate<Activity>(
lifecycleOwner = this,
koin = getKoin(this)
)}.getOrElse { activityScope()
}

unresolved reference: override (onCreate)

why is there an unresolved reference on override at the onCreate method? (Kotlin)
// unresolved reference: override
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// MapFragment erstellen und anzeigen
startMapFragment()
mapFragment.getMapAsync(this)
// OnClickListener für SpeicherDetailActivity
btnDetailSpeichern.setOnClickListener {
val detailIntent = Intent(this, SpeicherDetailActivity::class.java)
startActivity(detailIntent)
}
}
I tried to clean and rebuild the Project.
I deleted the onCreate method and let AndroidStudio generate an new one (by pressing CTRL + O).
I copied an onCreate method from another Activitiy,
but nothing of this worked.
Here is the full Activity:
class SpeicherActivity : AppCompatActivity(), OnMapReadyCallback {
// Variablem für MapFragment erstellen
var mapFragment = MapFragment.newInstance()
var fragmentTransaction = fragmentManager.beginTransaction()
var muennerstand : Ort = Ort(20.0, 5.0, "Münnerstadt", "", 0.0f)
var standartMarker = MarkerOptions().position(LatLng(muennerstand.latitute, muennerstand.longitute)).title("standart")
// GPS TEST
val locManager = LocationManager.GPS_PROVIDER.
// unresolved reference: override
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// MapFragment erstellen und anzeigen
startMapFragment()
mapFragment.getMapAsync(this)
// OnClickListener für SpeicherDetailActivity
btnDetailSpeichern.setOnClickListener {
val detailIntent = Intent(this, SpeicherDetailActivity::class.java)
startActivity(detailIntent)
}
}
// MapFragment erstellen und anzeigen
fun startMapFragment() {
fragmentTransaction.add(R.id.fragmentContainer, mapFragment)
fragmentTransaction.commit()
}
override fun onMapReady(mMap : GoogleMap?) {
mMap?.addMarker(standartMarker)
}
}
There is a syntax error in your code. Remove the full-stop at the end of the "locManager" variable declaration line. That should fix your issue.
// GPS TEST
val locManager = LocationManager.GPS_PROVIDER
Here is the manifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.gehring.lukas.spots">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MenuActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SpeicherActivity" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyC0UexE7q6vmBsohyJPkIwRbp1V75em9o4" />
<activity
android:name=".SpeicherDetailActivity"
android:label="#string/title_activity_speicher_detail"
android:theme="#style/AppTheme.NoActionBar"></activity>
</application>
</manifest>