What can keep activity alive after finish()? - android

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

Related

setOnClickListener is not showing the Toast or doing the Intent

this is in Kotlin, it doesn't seem to work no matter what I try it's just a button with a click listener. Its function is to take me to another activity, I tried a toast but didn't show either. I tried not using the function and also didn't work. and can we use this method with a text view? I'm new in Kotlin so easy on me...
here's the code
val startButton = binding.loginButton
startButton.setOnClickListener {
fun crtUser() {
Toast.makeText(this, "It's Working!", Toast.LENGTH_LONG).show()
val intent = Intent(this#LoginActivity, SignupActivity::class.java)
startActivity(intent)
}
crtUser()
}
}
I also used finish() after the Intent, and it crashed
Please do not use this syntax, it is not invalid, but improving it will be much better for you and for other developers to understand faster and easily what is going on :). Trigger the function from the on click event as follows:
fun onCreateOrOtherMethod() {
binding.button.setOnClickListener {
createUser()
}
}
And then you can have createUser() as an inner method of your current class:
private fun createUser() {
Toast.makeText(this, "It's Working!", Toast.LENGTH_LONG).show()
val intent = Intent(this#LoginActivity, SignupActivity::class.java)
startActivity(intent)
}
Little tip: don't abbreviate the methods names, you can use long name if needed as long as it improves the semantics of your code.
If it crashes, please, attach the Exception stack trace :D
It's working now just after I changed this
setContentView(R.layout.login_activity)
To this
setContentView(binding.root)
Any Idea Why??

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 close activity from class with context usage?

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

Kotlin overridden onBackPressed() function is never called when Action Bar back button is pressed

I've seen this question posed all over the place but I have not seen where anyone has been able to answer why the function doesn't get called.
I've run the app in debug mode with a break point set at the onBackPressed function and it completely ignores it. The compiler even recognizes and changes my red break point into a circle with a line through it.
I have a ContractSelectAdapter with a setOnClickListener passing a selected contract to ContractMenuActivity using putExtra(). Once in the ContractMenuActivity there is a button to open the activity below, UploadImageActivity and while in the activity I want a user to be able to hit the back button to reopen the ContractMenuActivity but within the onCreate function of ContractMenuActivity is a getStringExtra call that causing it to crash because there is no "extra". Which is why I'm trying to use the onBackPressed. In theory it should work perfectly.
I've tried putting a call to the onBackPressed function manually in the onCreate function of UploadImageActivity just to make sure it works and it does. It immediately opens ContractMenuActivity. So the function works it just never gets called for some reason. Extremely frustrating.
the onBackPressed function is at the very end of the UploadImageActivity class (first code excerpt)
Any help would be GREATLY appreciated.
class UploadImageActivity : AppCompatActivity() {
var prefs: Prefs? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_upload_image)
prefs = Prefs(this)
}
override fun onBackPressed()
{
super.onBackPressed()
val returnIntent = Intent(this, ContractMenuActivity::class.java)
returnIntent.putExtra(SELECTED_CONTRACT, prefs!!.rmsAppContractID)
startActivity(returnIntent)
}
}
class ContractMenuActivity : AppCompatActivity()
{
var selectedContract: String = ""
var prefs: Prefs? = null
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_contract_menu)
prefs = Prefs(this)
val btnUploadImage = findViewById<Button>(R.id.btnUploadImage)
selectedContract = intent.getStringExtra(SELECTED_CONTRACT)
prefs!!.rmsAppContractID = this.selectedContract
var contractSelectMsg = "Contract ${prefs!!.rmsAppContractID} selected"
val duration = Toast.LENGTH_SHORT
Toast.makeText(this, contractSelectMsg, duration).show()
btnUploadImage.setOnClickListener {this.openUploadImage()}
}
public fun openUploadImage()
{
val registered = prefs!!.rmsAppRegistered
var message: String
if(registered)
{
val intent = Intent(this, UploadImageActivity::class.java)
startActivity(intent);
}
else
{
message = "You must Pair this device before you can upload an image."
val intent = Intent(this, PostResponseMessageActivity::class.java).apply {
putExtra(POST_RESPONSE_MESSAGE, message)
}
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}
Parent name in manifest:
<activity
android:name=".ContractMenuActivity"
android:label="#string/contractMenuLabel"
android:parentActivityName=".ContractSelectActivity">
</activity>
<activity
android:name=".UploadImageActivity"
android:label="#string/uploadImageLabel"
android:parentActivityName=".ContractMenuActivity">
</activity>
super.onBackPressed()
Will finish the current activity. If you want to do some action after user press back button, You must to past your code to top of super.onBackPressed().

Activity screen still here after finish()

I have a particular case where I need to ask something to the user when he starts a printer service.
So from the onStartPrinterDiscovery (so a service), I start an activity to display the dialog and when the action is done, I send a new intent which calls finish() nd so I see that onDestoy() is called.
Unfortunately when I hit the apps history button, I still see my activity's screen behind:
Could you tell me why and how to fix it please?
androidManifest.xml:
<activity
android:name=".DialogActivity"
android:noHistory="true"
android:theme="#android:style/Theme.Dialog"
android:launchMode="singleTop">
</activity>
DialogActivity:
class DialogActivity : Activity() {
var activity:Activity = this
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
setContentView(R.layout.floatingactivity)
setFinishOnTouchOutside(false)
onNewIntent(intent)
}
override fun onDestroy() {
super.onDestroy()
isDisplayed = false
}
override fun onNewIntent(intent: Intent) {
val extras = intent.extras
val action = extras.getString("action")
when (action) {
"showDialog" -> {
if (!isDisplayed) {
tvMessage.text = getString(R.string.ask_for_action)
isDisplayed = true
}
}
"showErrorDialog" -> {
if (!isDisplayed) {
tvMessage.text = getString(R.string.error_action)
isDisplayed = true
}
}
"dismissDialog" -> { activity.finish() }
else -> {}
}
if (isDisplayed) {
btCancel.setOnClickListener {
activity.finish()
}
}
}
companion object {
var isDisplayed = false
}
}
EDIT I add how I currently create my Intent because of one answer which could be a solution:
val intent = Intent(applicationContext, DialogActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
intent.putExtra("action","showDialog")
startActivity(intent)
Add the android:excludeFromRecents to your activity attributes, like so:
<activity
android:name=".DialogActivity"
android:theme="#android:style/Theme.Dialog"
android:excludeFromRecents="true"
android:launchMode="singleTop">
</activity>
This is from the docs of the android:excludeFromRecents:
Whether or not the task initiated by this activity should be excluded from the list of recently used applications, the overview screen. That is, when this activity is the root activity of a new task, this attribute determines whether the task should not appear in the list of recent apps. Set "true" if the task should be excluded from the list; set "false" if it should be included. The default value is "false".
The android:noHistory="true" is for different purposes. This is from the docs of the android:noHistory:
A value of "true" means that the activity will not leave a historical trace. It will not remain in the activity stack for the task, so the user will not be able to return to it. In this case, onActivityResult() is never called if you start another activity for a result from this activity.
Try this:-
Intent i = new Intent(this,YourFirstActivity.Class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
finish();
Or
use finishAffinity() suitable for >= API 16.

Categories

Resources