While working with Kotlin onConfigurationChanged Is not called - kotlin

I checked similar questions and answers but even if everything looks OK it is not called when orientation has been changed.
Here is my codes;
Manifest
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="true"
android:label="#string/app_name"
android:theme="#style/Theme.AppName">
println never prints
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
println("onConfigurationChanged")
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show()
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show()
}
}

Related

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>

Kotlin. Doesn't show files in the directory

Good evening! I can't output files in a specific directory on an android device, and I can't solve it.
Here is the program code
package com.example.nt_music
import android.os.Bundle
import android.os.Environment
import androidx.appcompat.app.AppCompatActivity
import java.io.File
import java.util.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
println()
//readSongs(dir, ext)
val gpath: String = Environment.getExternalStorageDirectory().absolutePath
val spath = "Music"
val fullpath = File(gpath + File.separator + spath)
println("fullpath")
println(fullpath)
imageReaderNew(fullpath)
}
fun imageReaderNew(root: File) {
val fileList: ArrayList<File> = ArrayList()
val listAllFiles = root.listFiles()
if (listAllFiles == null) println("null")
if (listAllFiles != null && listAllFiles.size > 0) {
for (currentFile in listAllFiles) {
if (currentFile.name.endsWith(".mp3")) {
// File absolute path
println("downloadFilePath")
println( currentFile.getAbsolutePath())
// File Name
println("downloadFileName")
println(currentFile.getName())
fileList.add(currentFile.absoluteFile)
}
}
println("fileList")
println(fileList.size)
}
}
}
in the AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.nt_music">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<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=".MainActivity"
android:label="#string/app_name"
android:launchMode="singleTop"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Perhaps I put something wrong in it or something needs to be added
output:
I/System.out: fullpath
/storage/emulated/0/Music
I/System.out: null
But why null!?
maybe it has something to do with these errors that appear in the console
E/libc: Access denied finding property "ro.vendor.df.effect.conflict"
E/Perf: Fail to get file list com.example.nt_music
E/Perf: getFolderSize() : Exception_1 = java.lang.NullPointerException: Attempt to get length of null array
Fail to get file list com.example.nt_music
getFolderSize() : Exception_1 = java.lang.NullPointerException: Attempt to get length of null array
You can check the required permissions programmatically by calling this method
fun checkPermissions()
{
this.onPermissionGranted = onPermissionGranted
val ungrantedPermissions = requiredPermissionsStillNeeded()
if (ungrantedPermissions.isEmpty()) {
// All permission granted, you can list your files...
} else {
ActivityCompat.requestPermissions(activity, ungrantedPermissions, 1)
}
}
This function calls this method
private fun requiredPermissionsStillNeeded(): Array<String>
{
val permissions = HashSet<String>()
for (permission in getRequiredPermissions()) {
permissions.add(permission)
}
val i = permissions.iterator()
while (i.hasNext()) {
val permission = i.next()
if (ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED) {
i.remove()
}
}
return permissions.toTypedArray()
}
private fun getRequiredPermissions(): Array<String>
{
var permissions: Array<String>? = null
try {
permissions = activity.packageManager.getPackageInfo(activity.packageName, PackageManager.GET_PERMISSIONS).requestedPermissions
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
return permissions?.clone() ?: arrayOf()
}
Lastly, you'll need to override this method in your activity. It is called recursively until your user has granted all the permissions needed by the app.
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
checkPermissions()
}
After that, your app should be able to list the files in the folder.
EDIT
These are all functions that check whether the users have granted you the required permissions.
You should add them to your MainActivity class and edit your onCreate method like this
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
println()
//readSongs(dir, ext)
checkPermissions()
}
And then call your list file function in checkPermissions() like this
fun checkPermissions()
{
this.onPermissionGranted = onPermissionGranted
val ungrantedPermissions = requiredPermissionsStillNeeded()
if (ungrantedPermissions.isEmpty()) {
val gpath: String = Environment.getExternalStorageDirectory().absolutePath
val spath = "Music"
val fullpath = File(gpath + File.separator + spath)
println("fullpath")
println(fullpath)
imageReaderNew(fullpath)
} else {
ActivityCompat.requestPermissions(activity, ungrantedPermissions, 1)
}
}

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()
}

Broadcast Receiver wont start Activity when app is not running

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?

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>