Kotlin: How to get Custom Intent in Broadcast? - android

I create Custom Intent as below.
Intent().also { intent ->
intent.setAction("STEP_COUNT_NOTIFICATION")
intent.putExtra("stepCount", todayTotalStepCount)
sendBroadcast(intent)
}
And I add this Custom Intent in my Broadcast Receiver tag of AndroidManifest.
<receiver
android:name=".BroadcastReceiver.BroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_DATE_CHANGED" />
<action android:name="com.chungchunon.chunchunon_android.STEP_COUNTER_NOTIFICATION" />
</intent-filter>
</receiver>
Since I use two Intent, one is official intent and the other is custom, I need to distinguish received Intent.
onReceive function of BroadcastReceiver
override fun onReceive(context: Context?, intent: Intent?) {
val intentAction = intent!!.action
when (intentAction) {
Intent.ACTION_DATE_CHANGED -> {
// todayTotalStepCount = 0
var todayStepCountSet = hashMapOf<String, Int?>(
"todayStepCount" to 0
)
userDB
.document("$userId")
.set(todayStepCountSet, SetOptions.merge())
}
???? ->
}
}
How can I get STEP_COUNT_NOTIFICATION which is my custom intent?

First, create a public constant for your custom action:
companion object {
const val ACTION_STEP_COUNTER_NOTIFICATION = "com.chungchunon.chunchunon_android.STEP_COUNTER_NOTIFICATION"
}
For your Intent, use the same action:
Intent().also { intent ->
intent.setAction(ACTION_STEP_COUNTER_NOTIFICATION)
intent.putExtra("stepCount", todayTotalStepCount)
sendBroadcast(intent)
}
Also, use the same action in your BroadcastReceiver:
override fun onReceive(context: Context?, intent: Intent?) {
val intentAction = intent!!.action
when (intentAction) {
Intent.ACTION_DATE_CHANGED -> {
// todayTotalStepCount = 0
var todayStepCountSet = hashMapOf<String, Int?>(
"todayStepCount" to 0
)
userDB
.document("$userId")
.set(todayStepCountSet, SetOptions.merge())
}
// Your custom action
ACTION_STEP_COUNTER_NOTIFICATION -> { // Add your code here }
}
}
Lastly, for AndroidManifest.xml:
<receiver
android:name=".BroadcastReceiver.BroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_DATE_CHANGED" />
// action should be the same as the value of ACTION_STEP_COUNTER_NOTIFICATION
<action android:name="com.chungchunon.chunchunon_android.STEP_COUNTER_NOTIFICATION" />
</intent-filter>
</receiver>
action name should be the same for Intent, AndroidManifest.xml and BroadcastReceiver

Your Intent action needs to match the android:name for the corresponding <intent-filter>. Yours do not.
You have:
intent.setAction("STEP_COUNT_NOTIFICATION")
This has an action string of STEP_COUNT_NOTIFICATION.
You also have:
<action android:name="com.chungchunon.chunchunon_android.STEP_COUNTER_NOTIFICATION" />
This has an action string of com.chungchunon.chunchunon_android.STEP_COUNTER_NOTIFICATION.
Those two are not the same. They need to be the same. I recommend using com.chungchunon.chunchunon_android.STEP_COUNTER_NOTIFICATION in both places.

Related

NFC Read/Write on Android

I have the following code
val pendingIntent = PendingIntent.getActivity(activity, 101,
Intent(activity,classType).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_IMMUTABLE)
val nfcIntentFilter = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED)
val filters = arrayOf(nfcIntentFilter)
val TechLists = arrayOf(arrayOf(Ndef::class.java.name),
arrayOf(NdefFormatable::class.java.name))
nfcAdapter.enableForegroundDispatch(activity, pendingIntent, filters, TechLists)``
and in the activity I am trying to get the tag , action , message
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
val type: String? = intent.type
val action: String? = intent.action
}
However, action is already and tag is also null:
val tag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
it.getParcelableExtra(NfcAdapter.EXTRA_TAG,Tag::class.java)
} else {
it.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
}
In the manifest I have the following inside the activity tag:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain"/>
</intent-filter>
I also have the permission enabled:
<uses-permission android:name="android.permission.NFC" />
Tried multiple codes and change the intent filter NDEF_DISCOVERED to TAG_DISCOVERD but everything failed. I have tested it also on three separate phones and also same issue. onNewIntent gets called but the intent does not provide information

Is my alarmManager code, capable to send broadcast?

I'm trying to create an alarm Manager that sends notification every 24 hours at specific time consulted other Stackoverflow code. I tried this code below but onReceive() never gets executed. What i'm doing wrong?
private var HOUR_TO_SHOW_PUSH = 21
private var MINUTE_TO_SHOW_PUSH = 0
private val REQUEST_CODE = 100
private lateinit var alarmManager: AlarmManager
private lateinit var pendingIntent: PendingIntent
class MainActivity : AppCompatActivity() {
...
alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(this, AlertReceiver::class.java)
pendingIntent = PendingIntent.getBroadcast(this, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val calendar = GregorianCalendar.getInstance().apply {
if (get(Calendar.HOUR_OF_DAY) >= HOUR_TO_SHOW_PUSH) {
add(Calendar.DAY_OF_MONTH, 1)
}
set(Calendar.HOUR_OF_DAY, HOUR_TO_SHOW_PUSH)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
AlarmManager.INTERVAL_DAY,
pendingIntent
)
AlertReceiver
class AlertReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context, "Alarm", Toast.LENGTH_LONG).show()
println("Alarm")
Log.e("Alarm","Alarm Triggered")
}
Manifest
<receiver
android:name=".AlertReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
<action android:name="MyBroadcastReceiverAction"/>
</intent-filter
</receiver>
Normally you would register your receiver in the AndroidManifest like this:
<receiver android:name=".MyBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
</intent-filter>
</receiver>
https://developer.android.com/guide/components/broadcasts
it depends on the battery optimization applied to the app by the device, it varies from a device manufacturer to other
Normally you will find it in Settings > Apps > "your app name" > Battery Saver Set it to No Restrictions

How can I know an Activity launched from dynamic shortcuts when I use singleTask?

ActivityMain is singleTask.
I can use Code A to create a dynamic shortcuts icon in screen, it will show ActivityMain when I click the shortcuts icon.
You know I can also show ActivityMain by clicking the app icon.
How can I know a Activity lanuched from dynamic shortcuts when I use singleTask ?
BTW, Code B doesn't work.
Code A
#TargetApi(25)
private fun createShorcut() {
val sM = getSystemService(ShortcutManager::class.java)
val intent1 = Intent(this, ActivityMain::class.java)
intent1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent1.setAction("My")
val shortcut1 = ShortcutInfo.Builder(this, "shortcut1")
.setIntent(intent1)
.setShortLabel("Shortcut 1")
.setLongLabel("Shortcut 2")
.setShortLabel("This is the shortcut 1")
.setDisabledMessage("Login to open this")
.setIcon(Icon.createWithResource(this, R.drawable.notify_icon))
.build()
sM.dynamicShortcuts = Arrays.asList(shortcut1)
}
#TargetApi(25)
private fun removeShorcuts() {
val shortcutManager = getSystemService(ShortcutManager::class.java)
shortcutManager.disableShortcuts(Arrays.asList("shortcut1"))
shortcutManager.removeAllDynamicShortcuts()
}
if (Build.VERSION.SDK_INT >= 25) {
createShorcut()
}else{
removeShorcuts()
}
<activity android:name=".ui.ActivityMain"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Code B
override fun onResume() {
super.onResume()
if (mActivity.intent.action.equals("My")) {
toast("ActivityMain from dynamic shortcuts")
}
}
Added Content:
To prateek: Thanks!
Do you mean I have to check it both onCreate and onNewIntent event when I use SingleTask?
In SingleTask:
A: When I run the APP (Click either App Icon or Shortcut Icon) for the first time, only onCreate event is fired.
B: If the app is already running, when I fire the App again (Click either App Icon or Shortcut Icon) , only onNewIntent event is fired.
So I have to check it both onCreate and onNewIntent event just like Code C, right?
Code C
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.btnClose).setOnClickListener {
finish()
}
createShorcut()
var which= this.intent.getBooleanExtra("yourkey", false)
displayResult(which)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
var which= intent?.getBooleanExtra("yourkey", false)
displayResult(which?:false)
}
private fun displayResult(which:Boolean){
if (which) {
Toast.makeText(this, "From ShortCut Icon", Toast.LENGTH_LONG).show()
}else{
Toast.makeText(this, "From App Icon", Toast.LENGTH_LONG).show()
}
}
private fun createShorcut() {
val sM = getSystemService(ShortcutManager::class.java)
val intent1 = Intent(this, MainActivity::class.java)
intent1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent1.setAction("My")
intent1.putExtra("yourkey", true)
val shortcut1 = ShortcutInfo.Builder(this, "ShortCut Item")
.setIntent(intent1)
.setShortLabel("Shortcut Item")
.setIcon(Icon.createWithResource(this, R.drawable.ic_launcher_foreground))
.build()
sM.dynamicShortcuts = Arrays.asList(shortcut1)
}
}
<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
In my use case, I am adding a flag when creating intent to be launched via shortcut and always able to retrieve it at the other end.
Try something like .putExtra("fromShortcut", true). It should work.
Regarding retrieving action I haven't tried, sorry.
[Update]
This is how I am creating my intent object:
val shortcutIntent = Intent(context, YourActivity::class.java)
.setAction(Intent.ACTION_MAIN)
.putExtra("yourkey", true)
.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
)
and this is how you retrieve it at the receiving end:
mNewIntentReceived.getBooleanExtra("yourkey", false)
Also, I can observe you are not adding 'new task' flag in your intent, add that as well.

android broadcast permission

<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_DATE_CHANGED"/>
</intent-filter>
</receiver>
Manifests
var intent2 = Intent(requireActivity(), MyReceiver::class.java)
intent2.putExtra("Test", "value")
intent2.setAction(Intent.ACTION_DATE_CHANGED)
override fun onReceive(context: Context, intent: Intent) {
var st = intent.getStringExtra("Test")
if (intent.action.equals(Intent.ACTION_DATE_CHANGED)) {
var st = intent.getStringExtra("Test")
var toast = Toast.makeText(context, st, Toast.LENGTH_LONG) //
toast.show()
}
}
java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.DATE_CHANGED from pid=3604, uid=10091
I get an error like this
I searched but couldn't find a solution
Please help...
Try this.
You cannot use ACTION_DATE_CHANGED because it's a private android action, so you need to create your custom action.
<receiver
android:name=".MyReceiver"
android:exported="false">
<intent-filter>
<action android:name="${applicationId}.receiver.RECEIVE_MESSAGE_ACTION" />
</intent-filter>
</receiver>
Then you need to register for your activity before using it. Usually, I put in the onCreate method.
private fun registerReceiver(){
val filter = IntentFilter()
filter.addAction(MyReceiver.RECEIVE_MESSAGE_ACTION)
val customReceiver = MyReceiver()
registerReceiver(customReceiver, filter)
}
And when you called to share your message, use the following code.
companion object {
const val RECEIVE_MESSAGE_ACTION = "${BuildConfig.APPLICATION_ID}.receiver.RECEIVE_MESSAGE_ACTION"
}
private fun callCustomBroadcast(message: String) {
val intent = Intent(RECEIVE_MESSAGE_ACTION)
intent.putExtra("mymessage", message)
sendBroadcast(intent)
}

Spotify Auth Library returning Type.EMPTY on login

I followed everything in the android authentication guide and the quick start guide. I did as the guide told me to generate a SHA1 and adding it to Spotify app dashboard, and I got the app_client and added it to my app as well. Both scenarios still return the same thing. I even tried implementing the Login thru Browser feature, yet it still returns type EMPTY.
Here's my Login class
class SignInActivity : AppCompatActivity(), ConnectionStateCallback, Player.NotificationCallback {
private var mPlayer : SpotifyPlayer? = null
private val CLIENT_ID = //replace this
private val REDIRECT_URI = //replace this
private val REQUEST_CODE = 1337
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
btnSignin.setOnClickListener {
val builder = AuthenticationRequest.Builder(CLIENT_ID, AuthenticationResponse.Type.TOKEN, REDIRECT_URI)
builder.setScopes(arrayOf("user-read-private", "streaming"))
val request = builder.build()
AuthenticationClient.openLoginActivity(this, REQUEST_CODE, request)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
// Check if result comes from the correct activity
if (requestCode == REQUEST_CODE) {
val response = AuthenticationClient.getResponse(resultCode, intent)
Log.i("LoginActivity", Gson().toJson(response))
when (response.type) {
// Response was successful and contains auth token
AuthenticationResponse.Type.TOKEN -> {
Log.i("LoginActivity", "is equals to TOKEN")
val playerConfig = Config(this, response.accessToken, CLIENT_ID)
Spotify.getPlayer(playerConfig, this, object : SpotifyPlayer.InitializationObserver {
override fun onInitialized(spotifyPlayer: SpotifyPlayer) {
mPlayer = spotifyPlayer
mPlayer!!.addConnectionStateCallback(this#SignInActivity)
mPlayer!!.addNotificationCallback(this#SignInActivity)
}
override fun onError(throwable: Throwable) {
Log.e("LoginActivity", "Could not initialize player: " + throwable.message)
}
})
}
// Auth flow returned an error
AuthenticationResponse.Type.ERROR -> {
Log.i("LoginActivity", "ERROR!: $response.error")
}
AuthenticationResponse.Type.EMPTY -> {
Log.i("LoginActivity", "EMPTY!")
}
}
}
}
override fun onLoggedIn() {
Log.d("LoginActivity", "User logged in")
// This is the line that plays a song.
mPlayer!!.playUri(null, "spotify:track:2TpxZ7JUBn3uw46aR7qd6V", 0, 0)
}
}
and here's my AndroidManifest 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"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".SignInActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<data
android:host="callback"
android:scheme="backdrop.io"/>
</intent-filter>
</activity>
<activity
android:name="com.spotify.sdk.android.authentication.LoginActivity"
android:theme="#android:style/Theme.Translucent.NoTitleBar"/>
</application>
I'm trying to get a Type.TOKEN from this
Had this exact same issue myself, the problem is with this line in onActivityResult():
val response = AuthenticationClient.getResponse(resultCode, intent)
data should be passed as the second argument instead of intent.
The reason this isn't a compiler error is due to Kotlin turning getters and setters into properties, intent is a call to the Activity method getIntent().

Categories

Resources