I am trying to put together just a basic IntentService/BroadcastReceiver combo.
Yes, I know that what is currently there isn't really useful, but I just wanted to get the broadcast running.
The Service receives my initial intent, but the Receiver doesn't get the return Broadcast (debugger). Other answers didn't help me, and I feel like I've tried everything. It has to be something trivial, but I just can't put my finger on it.
Displaying Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val filter = IntentFilter()
filter.addAction(Constants.BROADCAST_ACTION)
filter.addDataScheme("http")
val dsl = DownloadStateReceiver()
LocalBroadcastManager.getInstance(this).registerReceiver(dsl,filter)
buttonLogin.setOnClickListener({
var loginIntent = Intent(this,RawRestJsonPull::class.java)
loginIntent.putExtra("userName",editLogin.text.toString())
loginIntent.putExtra("password",editPassword.text.toString())
this.startService(loginIntent)
})
}
class DownloadStateReceiver : BroadcastReceiver()
{
override fun onReceive(currentContext: Context, incomingIntent: Intent) {
Log.d("?????","d")
}
}
}
Service and the action constants:
class RawRestJsonPull : IntentService("rest" ) {
override fun onHandleIntent(incomingIntent: Intent) {
var outgoingIntent = Intent(Constants.BROADCAST_ACTION)
var status = 0
var userName : String? = incomingIntent.getStringExtra("userName")
var password : String? = incomingIntent.getStringExtra("password")
outgoingIntent.putExtra(Constants.EXTENDED_DATA_STATUS,status).putExtra("userName",userName).
putExtra("password",password)
outgoingIntent.flags = Intent.FLAG_INCLUDE_STOPPED_PACKAGES
LocalBroadcastManager.getInstance(this).sendBroadcast(outgoingIntent)
}
}
class Constants
{
companion object {
val BROADCAST_ACTION = "com.example.hubert1224.xxx.BROADCAST"
val EXTENDED_DATA_STATUS = "com.example.hubert1224.xxx.STATUS"
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.hubert1224.xxx">
<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">
<activity
android:name=".MainActivity"
android:windowSoftInputMode="stateVisible">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".RawRestJsonPull" />
<receiver android:name=".MainActivity$DownloadStateReceiver" />
</application>
</manifest>
Try that
First make your BroadcastReceiver static or it's own class, I am not sure Android can work with non static inner classes.
Second, I am not sure why your receiver intent filter has a
filter.addDataScheme("http")
But you are missing that from the Intent you are firing from your service, if you don't need it, remove it.
Try removing the data scheme http from the filter. 2. Try adding the category default to the filter.
Related
New to Android & Kotlin, and I need help with Intents and Intent Filters.
I'm using a Zebra MC2700 with Android Studio 2021 & Kotlin.
My Main Activity sets up the DataWedge profile and then starts another activity. The second activity should have an intent filter, so that I can make use of onNewIntent. The process is nicely demonstrated in this tutorial https://github.com/darryncampbell/DataWedge-GettingStarted-Samples I was able to duplicate & modify that app. But I cannot get my OnIntent routine to be called in anything other than the main activity.
I have also read the topic "Using DataWedge for Multiple Activities on Zebra Barcode Scanner doesn't work in Kotlin" But I'm still missing something. Surely it has to do with the Android manifest and the Intent Filter / Listener setup.
The DWUtilities.kt file is the same as the example except for the filter action:
intentProps.putString(
"intent_action",
"com.example.simplescan.ACTION")
My Main Activity has a button that launches the second activity.
val intent = Intent(this, SubActivityConsume::class.java)
startActivity(intent)
This is the second activity where the scan should be handled:
class SubActivityConsume : AppCompatActivity(), View.OnTouchListener{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sub_consume)
val btnScan = findViewById<Button>(R.id.btnScan)
btnScan.setOnTouchListener(this)
}
// Zebra DataWedge Stuff
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
displayScanResult(intent)
}
and here is my latest Android Manifest
(Edited to have the whole .xml file in case there are other issues I'm missing)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.simplescan">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<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/Theme.SimpleScan">
<activity
android:name=".ViewLists"
android:exported="false"
android:label="View Lists" />
<activity
android:name=".SubActivityConsume"
android:exported="false"
android:label="Scan Consumed Material"
android:launchMode="singleTop">
<intent-filter>
<action android:name="com.example.simplescan.ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".SubActivityReceive"
android:exported="false"
android:label="Scan Received Material" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Both the soft trigger and the device trigger fire the scanner, and it reads the barcode, and a beep is heard. But, onNewIntent() is never called.
I also had problems with this; the way I got it working was to set the profile to Broadcast Intent, and set a BroadcastReceiver in the activity that needs it.
In DWUtilities.kt, change
intentProps.putString("intent_delivery", "0") // StartActivity
to
intentProps.putString("intent_delivery", "2") // Broadcast Intent
Then in SubActivityConsume.kt set up your broadcast receiver...
class SubActivityConsume : AppCompatActivity(), View.OnTouchListener{
lateinit var broadcastReceiver: BroadcastRecevier
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sub_consume)
val btnScan = findViewById<Button>(R.id.btnScan)
btnScan.setOnTouchListener(this)
broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, scanIntent: Intent?) {
Log.v("SCAN","broadcast received: ${scanIntent?.action}")
when (scanIntent?.action) {
"com.example.simplescan.ACTION" -> {
displayScanResult(scanIntent)
}
}
}
}
DWUtilities.CreateDWProfile(this)
}
override fun onResume() {
super.onResume()
this.registerReceiver(broadcastReceiver,
IntentFilter("com.example.simplescan.ACTION"))
Log.v("SCAN","Broadcast receiver registered")
}
override fun onPause() {
super.onPause()
this.unregisterReceiver(broadcastReceiver)
Log.v("SCAN","Broadcast receiver unregistered")
}
I need to get the name of the app chosen by user fired with Intent.ACTION_SEND for analytic purposes. The name of app will be obtained through BroadcastReceiver.
It works until one day, the security engineer in our team informed us that all PendingIntent in the codebase must have PendingIntent.FLAG_IMMUTABLE to be secure.
The flag added breaks the existing functionality because intent?.getParcelableExtra<ComponentName>(Intent.EXTRA_CHOSEN_COMPONENT)?.packageName will always return null.
Is there anything I can do? PendingIntent.FLAG_MUTABLE is sadly not an option for me.
You can find same way of doing this from Android Documentation - Getting information about sharing
MainActivity.kt
const val PENDING_INTENT_REQUEST_CODE = 0x1000
const val THIRD_PARTY_SHARE_REQUEST_CODE = 0x1001
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnShare.setOnClickListener {
openThirdPartyShareDialog()
}
}
private fun openThirdPartyShareDialog() {
val thirdPartyShareIntent = Intent().apply {
action = Intent.ACTION_SEND
type = "text/plain"
}
val broadcastIntent = Intent(this, ThirdPartyAppBroadcastReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(this,
PENDING_INTENT_REQUEST_CODE,
broadcastIntent,
getPendingFlagIntent()
)
startActivityForResult(Intent.createChooser(
thirdPartyShareIntent,
null,
pendingIntent.intentSender
), THIRD_PARTY_SHARE_REQUEST_CODE)
}
private fun getPendingFlagIntent(): Int {
var flags = PendingIntent.FLAG_UPDATE_CURRENT
if (Build.VERSION.SDK_INT >= 23) {
flags = flags or PendingIntent.FLAG_IMMUTABLE
}
return flags
}
}
ThirdPartyAppBroadcastReceiver.kt
class ThirdPartyAppBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
// packageName will always be null !
val packageName =
intent?.getParcelableExtra<ComponentName>(Intent.EXTRA_CHOSEN_COMPONENT)?.packageName
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.flamyoad.broadcast_share"
>
<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/Theme.Broadcastshare"
>
<receiver android:name="com.flamyoad.broadcast_share.ThirdPartyAppBroadcastReceiver" />
<activity
android:name=".MainActivity"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Turns out it's fine to remove PendingIntent.FLAG_IMMUTABLE if you're using explicit intent.
Android apps send messages between components using Intents. Intents
can either specify the target component (Explicit Intent) or list a
general action and let the operating system deliver the Intent to any
component on the device that registers an Intent Filter matching that
action (Implicit Intent).
PendingIntents are Intents delegated to another app to be delivered at
some future time. Creating an implicit intent wrapped under a
PendingIntent is a security vulnerability that might lead to
denial-of-service, private data theft, and privilege escalation.
You can read more about it from Remediation for Implicit PendingIntent Vulnerability
I'm trying to use androidx.work:work-multiprocess:2.5.0 to run work in a process specified using Configuration.setDefaultProcessName(), but no matter which process I enqueue the work from, the doWork() method is called in main application process.
As stated in documentation RemoteWorkManager always reaches out to the designated process. The in-process scheduler also runs in the designated process.
My full test project is here: https://github.com/padreMateo88/multiprocessWorkManagerTest
I use the following dependencies:
implementation 'androidx.work:work-runtime-ktx:2.5.0'
implementation 'androidx.work:work-multiprocess:2.5.0'
I removed the default WorkManagerInitialiser in manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.workmanagertest">
<application
android:name=".MyApplication"
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">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name="com.example.workmanagertest.SecondProcessBroadcastReceiver"
android:process=":second_process" />
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
</application>
</manifest>
implemented Configuration.Provider in Application class:
class MyApplication : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration = WorkConfigurationProvider().get()
}
class WorkConfigurationProvider {
fun get() = Configuration.Builder().setDefaultProcessName(processName).build()
companion object {
private const val processName = "com.example.workmanagertest:second_process"
}
}
And use RemoteWorkManager.getInstance() to enqueue my work:
class SampleWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
Util.d(applicationContext, "SampleWorker.doWork()")
return Result.success()
}
companion object {
#JvmStatic
fun enqueueWork(context: Context) {
Util.d(context,"SampleWorker.enqueueWork()")
try {
val rwm = RemoteWorkManager.getInstance(context)
Util.d(context,"RemoteWorkManager hash ${rwm.hashCode()}")
rwm.enqueueUniqueWork(
"SampleWorker",
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.from(SampleWorker::class.java)
)
} catch (ex: Throwable) {
Util.d(context,"SampleWorker, WorkManager is not initialized properly, reason: " + ex.message)
}
}
}
}
What am I doing wrong?
In order for the doWork() method to be called in the designated process you also need to set the designated process name for RemoteWorkerManagerService and SystemJobService in your manifest:
<service
android:name="androidx.work.multiprocess.RemoteWorkManagerService"
tools:replace="android:process"
android:process=":second_process"/>
<service
android:name="androidx.work.impl.background.systemjob.SystemJobService"
tools:replace="android:process"
android:process=":second_process"/>
You can find a working example here:
https://github.com/padreMateo88/multiprocessWorkManagerTest
This implementation is based on clues I got from Google Issue Tracker:
https://issuetracker.google.com/issues/180255558
I'm testing some functions with flutter to try to find out if I'm able to handle it and right now it doesn't seem that I would be able to make it.
I've set up a app which should be able to handle incoming text from android intents. With that text I've wanted to open a specific screen of my app and load specific data into the screen.
I've tried so many different approaches that I can't even remember when it worked best and what went wrong.
Right now I have the following code.
AndroidManifest.xml
<application
android:name=".MyApplication"
android:label="My cool App"
android:icon="#mipmap/ic_launcher">
<activity
android:name=".MyFlutterActivity"
android:launchMode="standard"
android:theme="#style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<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.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
MyApplication.kt
class MyApplication : FlutterApplication(), PluginRegistrantCallback {
lateinit var flutterEngine: FlutterEngine
override fun registerWith(registry: PluginRegistry) {
FirebaseCloudMessagingPluginRegistrant.registerWith(registry)
}
override fun onCreate() {
super.onCreate()
// Instantiate a FlutterEngine.
flutterEngine = FlutterEngine(this)
// Start executing Dart code to pre-warm the FlutterEngine.
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// Cache the FlutterEngine to be used by FlutterActivity.
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine)
FlutterFirebaseMessagingService.setPluginRegistrant(this)
}
As you can see I'm also using the FlutterFirebaseMessagingService to receive notifications, which I'm right now not already sure if it is still working as it already was.
FlutterActivity.kt
class MyFlutterActivity : FlutterActivity() {
private var sharedText: String? = null
override fun provideFlutterEngine(context: Context): FlutterEngine? {
return FlutterEngineCache.getInstance().get("my_engine_id")
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
// Use the GeneratedPluginRegistrant to add every plugin that's in the pubspec.
//GeneratedPluginRegistrant.registerWith(flutterEngine)
handleShareIntent()
MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "com.example.app.shared.data")
.setMethodCallHandler {call: MethodCall , result: MethodChannel.Result ->
if (call.method.contentEquals("getSharedText")) {
result.success(sharedText)
sharedText = null
}
}
}
fun handleShareIntent() {
//val intent = getIntent()
val action = intent.action
val type = intent.type
if (Intent.ACTION_SEND.equals(action) && type != null) {
if ("text/plain".equals(type)) {
handleSendText(intent)
}
}
}
fun handleSendText(intent: Intent) {
sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)
}
}
main.dart
static const platform =
const MethodChannel('com.example.app.shared.data');
static Stream<String> get sharedData {
return new Stream<String>.fromFuture(
platform.invokeMethod("getSharedText"));
}
//... inside build
StreamProvider<String>.value(
lazy: true,
value: sharedData,
),
// .. handle inside build
final String sharedData = Provider.of<String>(context);
// .. handle shared Data and open Screen..
I've tried to log every step to find out what's not working.
So I connected my android phone and tried to debug, but didn't found a step where to handle the incoming data, even if the text is successfully handled inside the "handleSendText" method of the FlutterAcitivity.kt.
What even more confused me is, that if i save a dart file / press hot reload, my breakpoint inside the sharedData getter of the main.dart is getting called and from there everything works as it should.
But why do I have to hot reload where it should start itself instead.
Am I'm missing anything or doing wrong at this point?
Any help appreciated.
I have tried every possible answer on SO but nothing has helped.
I want to reset alarms using AlarmManager on device reboot and the code to do exactly that works, but it doesn't when I put it inside the receiver.
I had tried creating a service but that didn't seem to work at all which that was probably incompetence of my part, however I just can't see why this code isn't working.
AndroidManifest:
<manifest
(...)
<uses-permission android:name = "android.permission.RECEIVE_BOOT_COMPLETED" />
(...)
<application
(...)
<receiver
android:name = ".utils.DeviceRebootReceiver">
<intent-filter>
<action android:name = "android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
DeviceRebootReceiver:
class DeviceRebootReceiver : BroadcastReceiver() {
override fun onReceive(context : Context?, intent : Intent?) {
resetAlarms(context)
}
}
fun resetAlarms(context:Context):
fun resetAlarms(context : Context?) {
suspend fun resetAlarmsCoroutine(context : Context) {
val reminders = NotesDatabase.getInstance(context).notesDatabaseDAO.getAllActiveReminders()
reminders.forEach {
if (it.reminderTime > System.currentTimeMillis()) {
createAlarm(it.reminderTime, it.reminderMessage,null,context)
}
}
}
CoroutineScope(Dispatchers.IO).launch {
if (context != null) {
resetAlarmsCoroutine(context)
}
}
}
No need to show the createAlarm() function cause it works fine, BUT since I had seen that AlarmManager could cause problems on reboot I do instantiate it there like so:
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager