I am making a library that can be used to incorporate breaking and entering detection into any application
There is an arduino set to the alarm of the house which sends an SMS to a specific phone upon trigger
Within my sdk I register an sms receiver which upon receiving an sms with a specific text, should show a full screen activity (on top of the lockscreen too) that will alert the user
I created an application to test this behaviour
the application's package is : com.example.demo
the library's package is : com.example.sdk
the sms receiver looks like this:
class SMSReceiver : BroadcastReceiver() {
companion object {
const val TAG = "SMSReceiver"
}
private val logger by lazy { Injections.logger }
override fun onReceive(context: Context?, intent: Intent?) {
logger.log(TAG) { "Got sms" }
val ctx = context ?: return
val bundle = intent?.extras ?: return
val format = bundle.getString("format") ?: return
val pdus = (bundle["pdus"] as? Array<*>) ?: return
for (idx in pdus.indices) {
val pdu = pdus[idx] as? ByteArray ?: continue
val msg = SmsMessage.createFromPdu(pdu, format)
if (msg.messageBody.startsWith("theft event", true)) {
logger.log(TAG) { "Got theft event" }
abortBroadcast()
showTheftActivity(ctx, msg.messageBody)
break
}
}
}
private fun showTheftActivity(context: Context, messageBody: String) {
val intent = Intent(context, TheftActivity::class.java)
intent.addFlags(Intent.FLAG_FROM_BACKGROUND)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
// .addCategory(Intent.CATEGORY_LAUNCHER)
val location = messageBody.split(" ").getOrNull(2)
if (location != null) {
val coords = location.split(",")
if (coords.size == 2) {
val x = coords[0].toBigDecimalOrNull()
val y = coords[1].toBigDecimalOrNull()
if (x != null && y != null) {
intent.putExtra(TheftActivity.X, x.toString())
intent.putExtra(TheftActivity.Y, y.toString())
}
}
}
context.startActivity(intent)
}
}
the activity that should show on top of everything is this :
class TheftActivity : Activity() {
companion object {
const val X = "locationX"
const val Y = "locationY"
}
private val x: String? by lazy { intent.getStringExtra(X) }
private val y: String? by lazy { intent.getStringExtra(Y) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_theft)
val location = findViewById<Button>(R.id.locate)
if (x != null && y != null) {
location.setOnClickListener { Toast.makeText(applicationContext, "Going to $x , $y", Toast.LENGTH_SHORT).show() }
location.isEnabled = true
finish()
} else {
location.isEnabled = false
}
findViewById<Button>(R.id.cancel).setOnClickListener {
finish()
}
turnScreenOnAndKeyguardOff()
}
private fun turnScreenOnAndKeyguardOff() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)
setTurnScreenOn(true)
} else {
window.addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
)
}
with(getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
requestDismissKeyguard(this#TheftActivity, null)
}
}
}
override fun onDestroy() {
super.onDestroy()
turnScreenOffAndKeyguardOn()
}
private fun turnScreenOffAndKeyguardOn() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(false)
setTurnScreenOn(false)
} else {
window.clearFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
)
}
}
}
and the sdk's android manifest contains this:
<application>
<activity
android:name=".ui.TheftActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:label="#string/title_activity_theft"
android:showOnLockScreen="true"
android:theme="#style/Theme.Sdk.Fullscreen" />
<receiver
android:name=".receivers.SMSReceiver"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
</application>
when testing this on an emulator I send the sms to trigger the theft event
if the activity for testing (the one in the com.example.demo package) is not closed then it is brought to the front , but if it is closed nothing happens (though I do see the log messages from the receiver)
how can I make my sms receiver open the TheftActivity instead of the main activity from the main package?
edit: if it helps, the theft activity seems to start and then get immediately destroyed
It looks like the system can't bring the activity to the foreground due to the restrictions implemented in Android Q
With Android Q, it is impossible to start an activity from the background automatically if your app does not include those exceptions listed in the link below.
https://developer.android.com/guide/components/activities/background-starts
For possible solutions :
https://stackoverflow.com/a/59421118/11982611
Related
I'm trying to develop an app that:
Listens to push notifications
If the push notification is from WhatsApp + contains certain info, the app should call a specific number.
For the sake of the argument, let's assume that both permissions (call + notification listener) have already been granted.
So I used the below code (and of course, added the listener to the manifest), which works while the app is in the front, but not when it's in the background or closed. I also tried replacing "startActivity" with "startService", but that didn't work either. What's the correct way to leave the service running in the background and actually calling a number even though the app is in the background or closed? Also, is there a certain way to achieve this even the phone is locked?
class NotificationListener : NotificationListenerService() {
companion object {
private const val TAG = "NotificationListener"
private const val WA_PACKAGE = "com.whatsapp"
}
override fun onListenerConnected() {
Log.i(TAG, "Notification Listener connected")
Toast.makeText(applicationContext, "Notification Listener connected", Toast.LENGTH_SHORT).show()
}
override fun onNotificationPosted(sbn: StatusBarNotification) {
if (sbn.packageName != WA_PACKAGE) {
return
}
val notification = sbn.notification
val extras: Bundle = notification.extras
val from = extras.getString(NotificationCompat.EXTRA_TITLE)
val message = extras.getString(NotificationCompat.EXTRA_TEXT)
if (from != null && from.contains("test") && message != null && message.contains("gate")) {
val msg = "[$from]\n[$message]"
Log.i(TAG, msg)
Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT).show()
attemptCallGate()
}
}
private fun attemptCallGate() {
when (ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
true -> callGate()
false -> Toast.makeText(applicationContext, R.string.access_denied, Toast.LENGTH_SHORT).show()
}
}
private fun callGate() {
val number = "1234567890"
try {
val callIntent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$number"))
callIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
Toast.makeText(applicationContext, "Attempting to call [$number]", Toast.LENGTH_SHORT).show()
startActivity(callIntent)
} catch (e: Exception) {
Toast.makeText(applicationContext, "Failed calling [$number] ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
I need to get the name of any Android app when it opens and launch a password window. I have searched everywhere how to find out the name of the app which the user has opened but I have found no working solution yet. I have a service which I'm running on the background, but it only returns my app's name or the home screen name, no matter which app I open.
Here is my service code:
package com.example.applock
import android.app.ActivityManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
import android.provider.Settings
class BackAppListenerService : Service() {
private var isRunning = false
private var lastApp = ""
override fun onCreate() {
println(TAG + "Service onCreate")
isRunning = true
val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
println(TAG + "Service onStartCommand")
//Creating new thread for my service
//Always write your long running tasks in a separate thread, to avoid ANR
Thread(Runnable { //Your logic that service will perform will be placed here
//In this example we are just looping and waits for 1000 milliseconds in each loop.
while (true) {
try {
Thread.sleep(1000)
} catch (e: Exception) {
}
val mActivityManager = this.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val mPackageName = mActivityManager.runningAppProcesses[0].processName
println(mPackageName)
}
}).start()
return START_STICKY
}
override fun onBind(arg0: Intent): IBinder? {
println(TAG + "Service onBind")
return null
}
override fun onDestroy() {
isRunning = false
println(TAG + "Service onDestroy")
}
companion object {
private const val TAG = "HelloService"
}
}
Ok, so I have figured it out.
First add this to your manifest.xml under the manifest tag:
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/>
Then go to apps permissions > Usage Stats go to your app and enable it. You can also make a pop up asking for the user to do it once the apps loads.
Now, to get name of the current foreground app:
fun getForegroundApp(): String {
var currentApp = "NULL"
// You can delete the if-else statement if you don't care about Android versions
// lower than 5.0. Just keep the code that is inside the if and delete the one
// inside the else statement.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val usm = this.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
val time = System.currentTimeMillis()
val appList =
usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1000, time)
if (appList != null && appList.size > 0) {
val mySortedMap: SortedMap<Long, UsageStats> =
TreeMap<Long, UsageStats>()
for (usageStats in appList) {
mySortedMap.put(usageStats.lastTimeUsed, usageStats)
}
if (mySortedMap != null && !mySortedMap.isEmpty()) {
currentApp = mySortedMap.get(mySortedMap.lastKey())!!.getPackageName()
}
}
} else {
val am = this.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val tasks = am.runningAppProcesses
currentApp = tasks[0].processName
}
// Get only the app name name
return currentApp.split(".").last()
}
Sometimes the app name isn't the one displayed, for example the "gmail" app has the name "gm" when I tested it. The home screen name also changes from device to device
The logic to call it every few milliseconds to get the current foreground app is simple:
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
//Creating new thread for my service
//Always write your long running tasks in a separate thread, to avoid ANR
Thread(Runnable {
while (true) {
try {
Thread.sleep(10)
} catch (e: Exception) {
}
val currentApp = getForegroundApp()
if (currentApp != lastApp) {
println(currentApp)
// New app on front
lastApp = currentApp
println(currentApp)
// Do whatever you wan
}
}
}).start()
return START_STICKY
}
And that's it.
I am implementing a custom alarm with some logic behind it. When the alarm fires and the Broadcast receiver run the onReceive, the app starts a new activity for vibration and ringtone deactivation.
Because is an alarm, I want it to fires also when the phone is locked, and in sleep mode as well.
The alarm fires properly, even if the phone is in sleep mode. However, when I test the app on my smartphone (not just mine, but also my brother one), if the screen is OFF, it is not turned ON. On the contrary, the system works properly on the simulator with a pixel2 and android 9.
Below the receiver, and the manifest for the receiver and the app to disable the vibration and the tone ring.
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val extras = intent?.getExtras()
val alarmId = intent?.getIntExtra("alarmId",-1)
if(alarmId != null){
val ta = MemoryManager.getAlarmWithId(alarmId.toInt())
if(ta != null && ta!!.isSet){
startAlarmActivity(context,alarmId)
}
}
}
}
<activity
android:name=".AlarmDeactivation"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="#string/title_activity_alarm_deactivation"
android:theme="#style/AppTheme.NoActionBar"
android:showOnLockScreen="true"
android:turnScreenOn="true">
</activity>
<receiver android:name=".AlarmReceiver">
</receiver>
And this how I set the alarm.
if (Build.VERSION.SDK_INT >= 23) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pendingIntent)
}
else if (Build.VERSION.SDK_INT >= 19) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent)
}
else{
alarmManager.set(AlarmManager.RTC_WAKEUP, time, pendingIntent)
}
How can I let the app to turn on the screen when the alarm fires?
Thanks to all.
EDIT
As suggested here, I tried to define a class where I implemented the powerManager WakeLock acquire and release, but without any changed.
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val extras = intent?.getExtras()
val alarmId = intent?.getIntExtra("alarmId",-1)
AlertWakeLock.acquireWakeLock(context!!)
if(alarmId != null){
val ta = MemoryManager.getAlarmWithId(alarmId.toInt())
if(ta != null && ta!!.isSet){
startAlarmActivity(context,alarmId)
}
}
}
}
class AlertWakeLock {
companion object {
private var wakeLock: PowerManager.WakeLock? = null
fun acquireWakeLock(context: Context) {
if (wakeLock != null) {
return
}
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
wakeLock = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK or
PowerManager.ACQUIRE_CAUSES_WAKEUP or
PowerManager.ON_AFTER_RELEASE, "ToddlerTimer:WakeLock"
)
wakeLock!!.acquire()
}
fun releaseWakeLock() {
if (wakeLock != null) {
wakeLock!!.release()
wakeLock = null
}
}
}
}
class AlarmDeactivation : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
try {
super.onCreate(savedInstanceState)
setContentView(R.layout.alarm_deactivation)
val alarmId = intent.getIntExtra("ALARM_ID", -1)
val ta = MemoryManager.getAlarmWithId(alarmId.toInt())
val button: View = findViewById(R.id.saveButton)
val afm = AlarmFeedbackManager(this)
afm.play()
button.setOnClickListener{
MemoryManager.deSetAlarm(alarmId)
MemoryManager.saveAlarmIntoMemory()
afm.stop()
val mCtx = GlobalApplication.appContext
val intent = Intent(mCtx, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
mCtx?.startActivity(intent)
finish()
}
}
catch (e: Exception){
var i = e
}
}
override fun onDestroy() {
super.onDestroy()
AlertWakeLock.releaseWakeLock()
}
}
I have successfully implemented SMS retriever in my application using the guideline here . My Code is working fine and otp is auto populated in many devices but some of the devices like vivo v15 pro, redmi note 4 it is not working (BroadcastReceiver's onReceive() not get triggered) . i have attached my code here. check and let me know if you have any solution for this. Thanks
Manifest.xml
<receiver
android:name=".sms.SMSRetrieverBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" />
</intent-filter>
</receiver>
SMSRetrieverBroadcastReceiver.kt
class SMSRetrieverBroadcastReceiver : BroadcastReceiver() {
companion object{
private var otpReceiver: OtpReceiver? = null
fun initOTPListener(receiver: OtpReceiver) {
this.otpReceiver = receiver
}
}
override fun onReceive(context: Context, intent: Intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
val extras = intent.extras
val status = extras!!.get(SmsRetriever.EXTRA_STATUS) as Status
when (status.statusCode) {
CommonStatusCodes.SUCCESS -> {
// Get SMS message contents
var otp: String = extras.get(SmsRetriever.EXTRA_SMS_MESSAGE) as String
Timber.d("OTP_Message "+otp)
if(otpReceiver != null) {
otpReceiver!!.onOTPReceived(otp)
}
}
CommonStatusCodes.TIMEOUT -> {
// Waiting for SMS timed out (5 minutes)
// Handle the error ...
if(otpReceiver != null) {
otpReceiver!!.onOTPTimeOut()
}
}
}
}}
MainActivity.kt
fun startListeningForSMS() {
SMSRetrieverBroadcastReceiver.initOTPListener(this)
startSmsListener()
}
private fun startSmsListener() {
val client = SmsRetriever.getClient(mContext)
val task = client.startSmsRetriever()
task.addOnSuccessListener {
Timber.d("Success")
}
task.addOnFailureListener {
Timber.d("Failed")
}
}
override fun onOTPReceived(otp: String) {
setReceivedOtp(otp)
}
override fun onOTPTimeOut() {
setReceivedOtp(null)
}
Go to Messeges-click on (:) on top left ->Go to setting->Disable prevention from Otp verification.It worked fine in my case.
Can you check with this intent-filter action:
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
Basically, whenever I launch a second activity and pause the application and relaunch app by tapping on its icon in launcher, app does not resumes on second activity but goes back to first activity. On top of that, if I pause the app(view in task manager), which app makes shows whole activity again like really fast(talking in milliseconds) and then continues to task manager.
Manifest file
<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"
tools:replace="android:icon">
<activity
android:name=".Activity.MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Activity.ReferenceActivity"
android:configChanges="orientation|screenSize" /> <!-- Find a better solution for orientation change -->
<meta-data
android:name="preloaded_fonts"
android:resource="#array/preloaded_fonts" />
<activity android:name=".Activity.AboutActivity"></activity>
</application>
Since you would have noticed launchMode set to "singleTask", there is a reason behind it. My app contains recycler view. Its data is updated from second activity. Without launchMode set to singleTask, recyclerview data is not updated so to update the data I had to relaunch whole app just to see any data change. It is a temporary workaround but I am hoping if someone would help me on this matter as well.
About reycler view not updating, I have asked about that question countless times but never got a solution. Yes, I have gone through probably hundreds of similar problems on stack over flow and none of them work. Please bear in mind that I am beginner in Android development hence I do not have knowledge on advanced things like MvvM, RxJava or live data architecture.
P.S Just so any one gives a solution that I recall my data loader function again somewhere in onStart or on Resume, I have tried that countless times and it does not works. My app is not using fragments or any other advanced stuff just basic activities. Any help is appreciated!
Second activity
class ReferenceActivity : AppCompatActivity() {
private var dbHandler: PediaDatabase? = null
private var note = UserNotes()
private var noteExisted: Boolean = false
private var cardAdapterPos: Int? = null
private var title: String? = null
private var text: String? = null
private var sharingMenuOpened: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_reference)
val toolbarRef: Toolbar = findViewById(R.id.toolbarRefID)
setSupportActionBar(toolbarRef)
val toolbarTxtView = findViewById<TextView>(R.id.refToolbarTitleID)
supportActionBar!!.setDisplayShowTitleEnabled(false)
overridePendingTransition(R.anim.slide_in, R.anim.slide_out)
dbHandler = PediaDatabase(this)
val data = intent
if (!isNewNote) {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
if (data != null) {
noteExisted = true
this.cardAdapterPos = data.extras.getInt("cardPosition")
cardID = data.extras.getInt("cardID")
existingNote = dbHandler!!.readNote(cardID)
text = existingNote.noteText
title = existingNote.noteTitle
refTitleID.setText(existingNote.noteTitle, TextView.BufferType.EDITABLE)
refTextID.setText(existingNote.noteText, TextView.BufferType.EDITABLE)
toolbarTxtView.text = "Created: " + existingNote.noteDate.toString()
}
} else {
toolbarTxtView.text = "New note"
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
}
}
override fun onResume() {
super.onResume()
sharingMenuOpened = false
}
override fun onPause() {
super.onPause()
if (!sharingMenuOpened)
saveNote()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.top_menu, menu)
val addItem: MenuItem = menu!!.findItem(R.id.add_note_menu)
val delItem: MenuItem = menu.findItem(R.id.delete_note_menu)
val shareButton: MenuItem = menu.findItem(R.id.shareID)
addItem.isVisible = false
delItem.isVisible = false
shareButton.isVisible = false
if (noteExisted) {
delItem.isVisible = true
shareButton.isVisible = true
}
return true
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
if (item!!.itemId == R.id.delete_note_menu) {
val deletionMsg = SweetAlertDialog(this, SweetAlertDialog.WARNING_TYPE)
deletionMsg.titleText = "Delete this note?"
deletionMsg.confirmText = "Yes"
deletionMsg.setCancelable(false)
deletionMsg.setConfirmClickListener(object : SweetAlertDialog.OnSweetClickListener {
override fun onClick(sweetAlertDialog: SweetAlertDialog?) {
dbHandler!!.deleteNote(cardID)
deletionMsg.dismissWithAnimation()
val successMsg = SweetAlertDialog(this#ReferenceActivity, SweetAlertDialog.SUCCESS_TYPE)
successMsg.setCancelable(false)
successMsg.titleText = "Note deleted!"
successMsg.setConfirmClickListener(object : SweetAlertDialog.OnSweetClickListener {
override fun onClick(sweetAlertDialog: SweetAlertDialog?) {
successMsg.dismissWithAnimation()
finish()
}
}).show()
}
})
deletionMsg.setCancelButton("No", object : SweetAlertDialog.OnSweetClickListener {
override fun onClick(sweetAlertDialog: SweetAlertDialog?) {
deletionMsg.dismissWithAnimation()
}
})
deletionMsg.show()
}
if (item.itemId == R.id.shareID) {
var sharingTitle: String = title!!.trim()
var sharingText: String = text!!.trim()
sharingMenuOpened = true
val sharingIntent = Intent(android.content.Intent.ACTION_SEND)
sharingIntent.type = "text/plain"
val shareBody: String? = sharingText
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, sharingTitle)
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody)
startActivity(Intent.createChooser(sharingIntent, "Share to"))
}
return super.onOptionsItemSelected(item)
}
private fun saveNote() {
title = refTitleID.text.toString().trim()
text = refTextID.text.toString().trim()
if (existingNote.noteText == text && existingNote.noteTitle == title) {
finish()
} else {
if (noteExisted) {
if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
dbHandler!!.deleteNote(cardID)
val parsedColor = Color.parseColor("#263238")
Toasty.Config.getInstance().setInfoColor(parsedColor).apply()
Toasty.info(this, "Note deleted", Toast.LENGTH_SHORT, true).show()
} else {
if (TextUtils.isEmpty(title))
title = "No title"
existingNote.noteTitle = title
existingNote.noteText = text
existingNote.noteDate = System.currentTimeMillis().toString()
dbHandler!!.updateNote(existingNote)
Toasty.success(this, "Note saved", Toast.LENGTH_SHORT, true).show()
startActivity(Intent(this, MainActivity::class.java))
finish()
}
} else {
if (TextUtils.isEmpty(title) && TextUtils.isEmpty(text)) {
finish()
} else {
if (TextUtils.isEmpty(title))
title = "No title"
note.noteTitle = title
note.noteText = text
note.noteDate = System.currentTimeMillis().toString()
dbHandler!!.createNote(note)
Toasty.success(this, "Note saved", Toast.LENGTH_SHORT, true).show()
startActivity(Intent(this, MainActivity::class.java))
finish()
}
}
}
}
}
You are invoking finish() in saveNote() method, which is being called in onPause(), hence every time your app goes into background, current activity is being finished (closed) due to invokation of finish() in it.
Remove the calls to finish() in saveNote() method, and you will be resumed to your current activity instead of landing on launcher/Main Activity.
You can try use this in MainActivty:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Intents.isActivityExpandedFromLauncherIcon(this)) {
finish()
} else {
setContentView(R.layout.activity_main)
}
}
upd:
public class Intents {
public static boolean isActivityExpandedFromLauncherIcon(#NonNull Activity activity) {
return !activity.isTaskRoot() && isActivityStartedFromLauncherIcon(activity.getIntent());
}
public static boolean isActivityStartedFromLauncherIcon(#Nullable Intent intent) {
return intent != null &&
intent.hasCategory(Intent.CATEGORY_LAUNCHER) &&
intent.getAction() != null &&
intent.getAction().equals(Intent.ACTION_MAIN);
}
}