How to close activity from class with context usage? - android

I created another class where I have function of logout:
fun logOut(context: Context) {
context.stopService(Intent(context, CheckNewMessages::class.java))
val intent = Intent(context, LoginScr::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP and
Intent.FLAG_ACTIVITY_NEW_TASK and
Intent.FLAG_ACTIVITY_NO_ANIMATION
context.startActivity(intent)
(context as Activity).finish()
}
and as you can see I use this line for finishing activity:
(context as Activity).finish()
But it is still alive and as a result I have two or more same activities at my system. I tried a lot of ways like creating static variable at first activity and using this variable at the second one for closing. But my activity stays alive. I also tried to use lauchmode at manifest and some other ways. Maybe someone knows where I did a mistake?
UPDATE
Two places from which I call logOut(). 1st is interface between RV adapter and fragment:
override fun finish() {
APICallRequests.logOut(context!!)
activity!!.finishAffinity()
}
and 2nd at Interceptor for requests:
private fun updateAccessToken(context: Context) {
val sp = context.getSharedPreferences(Constants.SHARED_PREFS_STORAGE, 0)
synchronized(this) {
val tokensCall = accessTokenApi()
.getNewToken(ReqAccessToken(sp.getString("refresh_token", "")!!))
.execute()
if (tokensCall.isSuccessful) {
} else {
when (tokensCall.code()) {
500 -> {
val thread = object : Thread() {
override fun run() {
Looper.prepare()
Toast.makeText(cont, cont.getString(R.string.server_error_500), Toast.LENGTH_SHORT).show()
Looper.loop()
}
}
thread.start()
}
401 -> {
APICallRequests.logOut(context)
}
}
}
}
}

That's not the way it works. What happens is this. When you do:
context.startActivity(intent)
This doesn't start the new Activity immediately. It just requests that Android starts the new Activity when it gets control back. Then you do this:
(context as Activity).finish()
This call just finishes the current Activity. When you eventually return control to the Android framework, it will launch the new Activity as you requested in your call to startActivity().
If you want your app to exit (ie: all activities finished), you can just do:
(context as Activity).finishAffinity()
This call will finish the current Activity and all other activities in the task that belong to the same app.
NOTE: This only works if all activities in your app share the same affinity, which is the default case.

try to pass Activity instead of Context in inner param fun logOut(activity: Activity), this should help you if you are calling this function from activity. If you calling it from fragment you can use requareActivity.finish()

Related

registerForActivityResult() outside onCreate() with Compose

So I am trying to launch the intent Intent.ACTION_OPEN_DOCUMENT. I first tried with startActivityForResult but I noticed it was depreciated so I tried to find another way to do this. So I found the registerForActivityResult method but it turns out it must run after onCreate() has finished :
Note: While it is safe to call registerForActivityResult() before your fragment or activity is created, you cannot launch the ActivityResultLauncher until the fragment or activity's Lifecycle has reached CREATED.
Since I am using Jetpack Compose and setContent is in onCreate() my Activity has actually never finished creating because all my Composables functions are run in the setContent of my MainActivity
So how can I achieve this ?
Using the latest version of activity-compose you can use rememberLauncherForActivityResult() to register a request to Activity#startActivityForResult.
Something like:
val result = remember { mutableStateOf<Uri?>(null) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) {
result.value = it
}
Button(onClick = { launcher.launch(arrayOf("application/pdf")) }) {
Text(text = "Open Document")
}
result.value?.let {
//...
}

How to Pass finish() from Activity class as a function parameter (So the code can be reused in Kotlin)?

I have to use this peice of code twice in two different places in two different activites. No good programmer would willingly want to use same code in multiple places without reusing it.
//when back key is pressed
override fun onBackPressed() {
dialog.setContentView(twoBtnDialog.root)
twoBtnDialog.title.text = getString(R.string.warning)
twoBtnDialog.msgDialog.text = getString(R.string.backPressWarning)
twoBtnDialog.ok.text = getString(R.string.exit)
twoBtnDialog.cancel.text = getString(R.string.cancel)
twoBtnDialog.ok.setOnClickListener {
//do nav back
finish()
dialog.dismiss()
}
twoBtnDialog.cancel.setOnClickListener {
dialog.dismiss() //just do nothing
}
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog.show()
}
I can move it to one place, but the problem is I have to pass in the finish() function from Activity Class to close the calling activity.
My simple question is how can I resue it ? Or How can I pass this function (finish()) to a different class (which is in some other file).
Take a function type parameter in your method.
fun doBackPress(finish: () -> Unit) {
// you need to invoke the finish method when necessary
finish.invoke()
}
Then you need to call the method and have to pass the finish() method from any other activity or fragment method like bellow.
override fun onBackPressed() {
doBackPress { finish() }
}
You could make an interface and extension function, which I think is less messy than trying to pass everything you need as parameters to a function, because it communicates intent better and makes it harder to do something wrong.
interface MyDialogOwner {
val dialog: Dialog
val twoBtnDialog: MyDialogBinding
fun Activity.handleBackPress() {
//the exact same content you have in your function now.
}
}
// In Activity:
override fun onBackPressed() = handleBackPress()
Your Activities should implement the interface, using your existing properties for dialog and twoBtnDialog (just add override in front of their declarations).
I'm assuming twoBtnDialog is a view binding.

What can keep activity alive after finish()?

I have HomeScreen and LoginScr activity and I would like to move from HomeScreen to LoginScr activity. I do it in such way:
val thread = Thread(Runnable {
try {
val client = APICallRequests.client
client.dispatcher.executorService.shutdown()
client.connectionPool.evictAll()
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
})
thread.start()
val intent = Intent(this, LoginScr::class.java)
startActivity(intent)
finishAfterTransition()
As you can see I try to close okHttp3 client, because maybe it keeps HomeScr activity alive. Also I use finishAfterTransition(). Then when I move from LoginScr to HomeScr I do it in such way:
private fun moveToNextScreen(){
val goToMain = Intent(this, HomeScreen::class.java)
startActivity(goToMain
.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
)
finishAfterTransition()
overridePendingTransition(0, 0)
}
As I see I create two activity instances instead one. How I did it - added log to onCreate() fun. I totally don't understand why does it happen. Similar problem can be met after onBackPressed():
override fun onBackPressed() {
val startMain = Intent(Intent.ACTION_MAIN)
startMain.addCategory(Intent.CATEGORY_HOME)
startMain.flags = Intent.FLAG_ACTIVITY_NO_ANIMATION
startActivity(startMain)
finish()
super.onBackPressed()
}
I tried a lot of variants but my HomeScreen can stay alone. I also tried such ways at manifest:
android:launchMode="singleTask"
android:noHistory="true"
Maybe someone know how to solve this problem?
I think this answer can help you
https://stackoverflow.com/a/10862977/13221053
Further, if you want to keep your app active in background then you can use services for that purpose, here is the documentation
https://developer.android.com/guide/components/services

How to start an activity from flutter plugin using an API

So I am making a Flutter plugin and I am attempting to run Kotlin code on Android. The problem is, this code runs a method which attempts to start an activity without the FLAG_ACTIVITY_NEW_TASK flag on the intent. The problem with this is that it also does NOT have a way to give it an intent instance as it attempts to instantiate an instance inside the method itself. The method expects to be called from a button or other method that is stored on the UI and called from it. However, since it is called from the onMethodCall method in the Flutter plugin, it does not seem to work. I have attempted many workarounds such as adding a method inside the Activity and running the code inside while calling it from the flutter plugin class. I have also tried using the UIThread and no luck either. Any workarounds?
Note: I have not provided any code due to keeping this API hidden. It should only be known that I am running the code from the onMethodCall event.
Error: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
You can extend your plugin to implement ActivityAware in your plugin class, when you implement it, you get a couple of callbacks that gives you the current activity. Like this :
lateinit activity: Activity? = null
override fun onDetachedFromActivity() {
activity = null
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
activity = null
}
After that you can just startActivity from the assigned activity variable.
Let me know if you need further help.
As you mentioned, For Flutter plugin any platform-dependent logics should be kept in the subclass of FlutterActivity which was used to show flutter module/screens inside a native module. Now you can launch intent from that subclass without any additional flags.
#note - Subclass of FlutterActvity should be kept in the native module.
class FlutterResponseActivity : FlutterActivity() {
private var methodResult: Result? = null
override fun provideFlutterEngine(context: Context): FlutterEngine? {
return MyApplication.mContext.flutterEngine //Pre-warmed flutter engine
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"startMainActivity" -> {
startMainActivity()
result.success(true)
}
else -> result.notImplemented()
}
}
}
private fun startMainActivity() {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}

Cannot pass context between two classes [kotlin]

So, I am new to Kotlin.
I have two classes LoginActivity and DashboardActivity.
In the DashboardActivity I have a method:
fun createActivity(context: Context){
val intent = Intent(context, DashboardActivity::class.java)
startActivity(intent)
}
and in the LoginActivity, when the user clicks the button to login, I have a method that calls the previous method:
private fun onClickLogin() {
val username : String = editTextUsername.text.toString()
val password : String = editTextPassword.text.toString()
if (username.isEmpty()){
editTextUsername.error = "Insira um username"
return
}
if (password.isEmpty()){
editTextPassword.error = "Insira uma password"
return
}
if (username.compareTo("user") == 0 && password.compareTo("password") == 0){
DashboardActivity().createActivity(this)
}
}
The problem is, every time I try to call DashboardActivity().createActivity(this), I get a NullPointerException.
Here is the logcat:
Click here, please
So, why do I get this error? Is it because I cannot pass the context like this?
I have tried using this#LoginActivity but didn't work...
you can't do that:
DashboardActivity().createActivity(this)
in that line you are trying to initialize activity and then use a method.
What you should do is:
1.
startActivity(DashboardActivity.createActivity(this))
2. In DashboardActivity.kt
companion object {
fun createActivity(context: Context): Intent {
return Intent(context, DashboardActivity::class.java)
}
}
This creates createActivity method as "static" which means that you can access without need to initilize an intsance of this class.
use ApplicationContext
fun createActivity(){
val intent = Intent(applicationContext, DashboardActivity::class.java)
startActivity(intent)
}
every time I try to call DashboardActivity().createActivity(this), I get a NullPointerException
Never create an instance of an activity class yourself.
why do I get this error?
Because you created an instance of DashboardActivity yourself and tried using it.
I have tried using this#LoginActivity but didn't work...
Make createActivity() be a function on a companion object. This will require a slight change to your implementation, calling startActivity() on the passed-in Context.

Categories

Resources