Issue
I would like to create an Android application that is capable of setting timer and that would handle implicit intent AlarmClock.ACTION_SET_TIMER
. This also will allow it to react to Google assistant 'Set timer for' command.
Upon receiving this special intent i would like the app to set timer without showing the activity (to respect AlarmClock.EXTRA_SKIP_UI
).
If the application was not launched when I use Google Assistant to set timer, it works as expected: the activity is not shown, but the code to set the timer gets executed.
However, if I launch my activity with launcher, then use home button to return to the main screen (i.e. the activity can still be found in the recents), the behavior is wrong. The activity is displayed, the onStart()
method is called (even though I call finish()
from onCreate
).
As far as I understand from the documentation, if I call finish()
from onCreate
no other lifecycle callbacks should be invoked, and the activity should not be displayed.
What's even more confusing, apparently onCreate
and onStart
see different intents.
From AndroidManifest.xml
:
<activity android:name="com.example.testlifecycle.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_TIMER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
MainActivity.kt
:
class MainActivity : AppCompatActivity() {
companion object {
const val TAG = "MainActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
Log.d(TAG, "onCreate called")
super.onCreate(savedInstanceState)
logIntent()
if (intent.action == AlarmClock.ACTION_SET_TIMER) {
Log.d(TAG, "Timer intent received, closing")
// Do something useful here
setResult(Activity.RESULT_OK)
finish()
return
}
setContentView(R.layout.activity_main)
}
override fun onStart() {
Log.d(TAG,"onStart called")
super.onStart()
logIntent()
}
private fun logIntent() {
Log.d(TAG, "Intent action ${intent.action}")
Log.d(TAG, "Intent flags ${intent.flags.toString(16)}")
intent.extras?.keySet()?.forEach {
Log.d(TAG, "Intent extra $it = ${intent.extras?.get(it)}")
}
}
}
Scenario 1
Application is not launched, use Google Assistant to set timer.
2020-02-02 18:21:28.376 22269-22269/com.example.testlifycycle D/MainActivity: onCreate called
2020-02-02 18:21:28.408 22269-22269/com.example.testlifycycle D/MainActivity: Intent action android.intent.action.SET_TIMER
2020-02-02 18:21:28.409 22269-22269/com.example.testlifycycle D/MainActivity: Intent flags 10000000
2020-02-02 18:21:28.409 22269-22269/com.example.testlifycycle D/MainActivity: Intent extra android.intent.extra.alarm.SKIP_UI = true
2020-02-02 18:21:28.409 22269-22269/com.example.testlifycycle D/MainActivity: Intent extra com.google.android.apps.gsa.shared.util.starter.IntentStarter.ERROR_TOAST_ID = 2131951799
2020-02-02 18:21:28.410 22269-22269/com.example.testlifycycle D/MainActivity: Intent extra android.intent.extra.REFERRER_NAME = android-app://com.google.android.googlequicksearchbox/https/www.google.com
2020-02-02 18:21:28.410 22269-22269/com.example.testlifycycle D/MainActivity: Intent extra android.intent.extra.alarm.LENGTH = 3600
2020-02-02 18:21:28.410 22269-22269/com.example.testlifycycle D/MainActivity: Intent extra KEY_HANDOVER_THROUGH_VELVET = true
2020-02-02 18:21:28.410 22269-22269/com.example.testlifycycle D/MainActivity: Timer intent received, closing
Here everything works as expected.
Scenario 2
Application is started by launcher, press Home button and then use Google Assistant to set timer.
Start the app with launcher, looks as expected:
2020-02-02 18:24:30.052 22269-22269/com.example.testlifycycle D/MainActivity: onCreate called
2020-02-02 18:24:30.055 22269-22269/com.example.testlifycycle D/MainActivity: Intent action android.intent.action.MAIN
2020-02-02 18:24:30.055 22269-22269/com.example.testlifycycle D/MainActivity: Intent flags 10200000
2020-02-02 18:24:30.055 22269-22269/com.example.testlifycycle D/MainActivity: Intent extra profile = 0
2020-02-02 18:24:30.158 22269-22269/com.example.testlifycycle D/MainActivity: onStart called
2020-02-02 18:24:30.158 22269-22269/com.example.testlifycycle D/MainActivity: Intent action android.intent.action.MAIN
2020-02-02 18:24:30.158 22269-22269/com.example.testlifycycle D/MainActivity: Intent flags 10200000
2020-02-02 18:24:30.158 22269-22269/com.example.testlifycycle D/MainActivity: Intent extra profile = 0
Press home button, and use Google Assistant:
2020-02-02 18:26:21.398 23158-23158/com.example.testlifycycle D/MainActivity: onCreate called
2020-02-02 18:26:21.402 23158-23158/com.example.testlifycycle D/MainActivity: Intent action android.intent.action.SET_TIMER
2020-02-02 18:26:21.402 23158-23158/com.example.testlifycycle D/MainActivity: Intent flags 10400000
2020-02-02 18:26:21.402 23158-23158/com.example.testlifycycle D/MainActivity: Intent extra android.intent.extra.alarm.SKIP_UI = true
2020-02-02 18:26:21.402 23158-23158/com.example.testlifycycle D/MainActivity: Intent extra com.google.android.apps.gsa.shared.util.starter.IntentStarter.ERROR_TOAST_ID = 2131951799
2020-02-02 18:26:21.403 23158-23158/com.example.testlifycycle D/MainActivity: Intent extra android.intent.extra.REFERRER_NAME = android-app://com.google.android.googlequicksearchbox/https/www.google.com
2020-02-02 18:26:21.403 23158-23158/com.example.testlifycycle D/MainActivity: Intent extra android.intent.extra.alarm.LENGTH = 60
2020-02-02 18:26:21.403 23158-23158/com.example.testlifycycle D/MainActivity: Intent extra KEY_HANDOVER_THROUGH_VELVET = true
2020-02-02 18:26:21.403 23158-23158/com.example.testlifycycle D/MainActivity: Timer intent received, closing
2020-02-02 18:26:21.446 23158-23158/com.example.testlifycycle D/MainActivity: onStart called
2020-02-02 18:26:21.446 23158-23158/com.example.testlifycycle D/MainActivity: Intent action android.intent.action.MAIN
2020-02-02 18:26:21.446 23158-23158/com.example.testlifycycle D/MainActivity: Intent flags 10200000
2020-02-02 18:26:21.447 23158-23158/com.example.testlifycycle D/MainActivity: Intent extra profile = 0
The problems I see here:
onStart
is called even thoughfinsh
was called fromonCreate
onStart
logs an intent with actionMAIN
whileonCreate
logs (expected)SET_TIMER
I guess I might lack understanding on Android intent/lifecycle mechanisms. I would appreciate any help with this matter.
Solution
You are dealing with two difference instances of MainActivity
:
- You tap the launcher icon
- Android creates an instance of
MainActivity
, andonCreate()
/onStart()
are called on it - You press home
- You do some Assistant-y thing that triggers that
SET_ALARM
implicitIntent
- Android creates a second instance of
MainActivity
into your existing task and brings that task to the foreground - That second instance of
MainActivity
is called withonCreate()
, and there youfinish()
that instance - Since your task is in the foreground, and there is another activity in that task (the first instance of
MainActivity
), that activity is brought back to the foreground, andonStart()
is called on it
I cannot speak specifically to Assistant, as I avoid it entirely. However, if you want the Assistant-launched activity to be in a separate task, and not bring any existing task to the foreground, you will need to do some stuff in the manifest for that. My starting point would be to have separate activities for the separate scenarios, then use android:taskAffinity
on the SET_ALARM
activity to have it route to a separate task.
Answered By - CommonsWare
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.