How do I pass data from Service to Activity in Kotlin? - android

Beginner warning although CS student
I have a Service named RestApiService and an activity named MainActivity.
I have a token which I got stored in RestApiService.
In link is screenshot of the RestApiService in imgur
https://imgur.com/a/Msu04xj
I only find tutorials in Java and I can´t translate.
How do I get it from there to MainActivity? TIA

A way to do this is by using some database, store it there and then you can just observe the database on MainAcitivty level to get that value once its available.
An alternative would be to use shared preferences liveData which you will observe on MainActivity again but I guess that's a bit trickier
I would go with creating an entity which you will save in database using Room and then use a ViewModel component to expose liveData to the MainActivity

Easiest way- bind to the service, and define a Binder for the service with a function to get the value. Then you can just call that function to get the token. The Binder is returned to the Activity as the return from bindService().
Here's Google's example of doing so in kotlin: https://developer.android.com/guide/components/bound-services#kotlin

BroadCastReceiver will help you, in your service send a broadcast intent and in your activity you will receive your data.

You can use Local BroadcastReceiver for sending data from service to Activity
Please try this code in your RestApiService class where you get token:
val intent = Intent("RestApiData")
intent.putExtra("token", myToken)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
In above code "RestApiData" is main key of broadcast receiver that you need for getting data in activity, "token" is key for myToken value
In Your MainActivity class use below code in onCreate method:
LocalBroadcastManager.getInstance(this).registerReceiver(tokenPassingReceiver, IntentFilter("RestApiData"))
Now use below code outside the onCreate method of activity:
private val tokenPassingReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val bundle = intent.extras
if (bundle != null) {
if (bundle.containsKey("token")) {
val tokenData = bundle.getString("token")
Log.e("MainActivity--","token--$tokenData")
}
}
}
}
For more information check this link: Kotlin Android Broadcast Intents and Broadcast Receivers
I hope it may help you.

Related

How to use Room Database inside BroadcastReceiver?

Good day. My app needs to receive SMS and what I am trying to do right now is I want to insert the data to the database from BroadcastReceiver as soon as the app received the message but I failed. Here is my code.
public class SmsReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ViewModel model = ViewModelProviders.of((FragmentActivity) context).get(ViewModel.class);
Bundle bundle = intent.getExtras();
Object[] objects = (Object[]) Objects.requireNonNull(bundle).get("pdus");
SmsMessage[] smsMessages = new SmsMessage[Objects.requireNonNull(objects).length];
Sms sms = new Sms();
for (int i = 0; i < smsMessages.length; i++) {
smsMessages[i] = SmsMessage.createFromPdu((byte[]) objects[i]);
sms = new Sms(
smsMessages[i].getOriginatingAddress(),
smsMessages[i].getMessageBody(),
smsMessages[i].getTimestampMillis()
);
}
model.insert(new ReplyToVotersQueue(sms.getBody(),sms.getAddress()));
}
}
As we can see, I am trying to insert the data to database but I've got an error that says
java.lang.RuntimeException: Unable to start receiver
mgb.com.smspoll.Services.SmsReceiver: java.lang.ClassCastException:
android.app.ReceiverRestrictedContext cannot be cast to
androidx.fragment.app.FragmentActivity
I found from the internet while searching the answer to this problem that BroadcastReceiver cannot convert to Activity because BroadcastReceiver can run without context. If that so then is there any
way to insert the data to database using room database after receiving the SMS? Since my app is an SMS Poll, I need to run my app in the background process.
The problem here is that you're trying to retrieve the ViewModel outside an activity or a fragment, which is not a good practice and also doesn't work.
The solution is this:
Separate your Room DB operation from ViewModel in a separate singleton class. Use it in ViewModel and any other place required. When Broadcast is received, write data to DB through this singleton class rather than ViewModel.
For more, please take a look at this post:
The correct way to obtain a ViewModel instance outside of an Activity or a Fragment

Testing Activity Recognition Transition API

I am currently developing an app which uses Activity Recognition Transition API (the new one, not the old, check the link below). My question is, how can I test my app? More exactly, how can I "manually" trigger transition events? Do I really have to put my phone and laptop into my backpack and go for a ride on a bicycle to trigger the ON_BYCICLE/ACTIVITY_TRANSITION_ENTER event? There must be an easier way :) Maybe using adb? https://developer.android.com/studio/test/command-line
API documentation: https://developer.android.com/guide/topics/location/transitions
You can use following code to emulate the event
var intent = Intent()
// Your broadcast receiver action
intent.action = BuildConfig.APPLICATION_ID + "TRANSITIONS_RECEIVER_ACTION"
var events: ArrayList<ActivityTransitionEvent> = arrayListOf()
// You can set desired events with their corresponding state
var transitionEvent = ActivityTransitionEvent(DetectedActivity.IN_VEHICLE, ActivityTransition.ACTIVITY_TRANSITION_ENTER, SystemClock.elapsedRealtimeNanos())
events.add(transitionEvent)
var result = ActivityTransitionResult(events)
SafeParcelableSerializer.serializeToIntentExtra(result, intent, "com.google.android.location.internal.EXTRA_ACTIVITY_TRANSITION_RESULT")
activity?.sendBroadcast(intent)
I have created simple activity with two buttons in my application where I broadcast these events(Start and Stop) respectively. This helps me debug the application gracefully.
I don't know of any way to simulate a transition event from outside your app. But within your app, you can construct a suitable Intent and send it to your receiver.
Add this method to any Context (e.g. an Activity or Service): (Kotlin)
class MyService: Service() {
fun sendFakeActivityTransitionEvent() {
// name your intended recipient class
val intent = Intent(this, MyReceiver::class.java)
val events: ArrayList<ActivityTransitionEvent> = arrayListOf()
// create fake events
events.add(
ActivityTransitionEvent(
DetectedActivity.ON_BICYCLE,
ActivityTransition.ACTIVITY_TRANSITION_ENTER,
SystemClock.elapsedRealtimeNanos()
)
)
// finally, serialize and send
val result = ActivityTransitionResult(events)
SafeParcelableSerializer.serializeToIntentExtra(
result,
intent,
"com.google.android.location.internal.EXTRA_ACTIVITY_TRANSITION_RESULT"
)
this.sendBroadcast(intent)
}
}
Substitute your desired recipient for MyReceiver -- any subclass of BroadcastReceiver should work.
Then call sendFakeActivityTransitionEvent() when desired.

How to pass data from a service to activity using #Receiver?

I am using Android annotation library, and I want to pass data from service to activity using broadcast receiver. I want to utilize #Reciever of Android annotation, my attempt is same as this SO question
Any help is appreciated.
In your Activity, add this receiver method:
#Receiver(actions = "GPSLocationUpdates", local = true)
void receiveLocationUpdates() {
// TODO
}
And broadcast your Intent as usual from the Service:
Intent intent = new Intent("GPSLocationUpdates");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);

How to pass class reference (not object) using Intent?

I have a scenario where there are multiple Activities and a Service.
All this Activities when need to get data from any URL, starts the Service (activity's class name is passed in Intent) and register a BroadcastReceiver to listen to responseIntent which will be sent by Service on getting the response. Service will pass the class name back in Intent along with response.
I have implemented this and it works. But the doubt is that from BroadcastReceiver's onReceive() method, how can I call a method of an activity which received a broadcast event?
Note : I want to deal with class reference but not an object. I tried this,
public void onReceive(Context context, Intent intent) {
Class<?> thisActivity = Class.forName(intent.getStringExtra("ClassName"));
thisActivity.hello(); // hello() is a public and static method
}
But it does not work.
Thank you.
You can't pass an object reference through an Intent. Imagine that Parcelable objects are just like Serializableobjects, their references are lost.
You should declare your BroadcastReceiver as an inner-class in your Activity, and then you will have a reference to the enclosing Activity. This is the way to do it.
You need to add the full package name to the 'ClassName' string, like this:
Class<?> thisActivity = Class.forName(intent.getStringExtra("com.android.youapp.ClassName"));
Note: if this class resides in a different library project you should set the string name accordingly.
How did you get your class name, which you are trying to extract from Intent's "ClassName"?
Preparing intent:
Intent i = new Intent(....);
String className = MyClass.class.getCanonicalName();
i.putExtra(CLASS_NAME, className);
Extracting intent content:
String canName = intent.getStringExtra(CLASS_NAME);
mClientClass = Class.forName(canName);
java.lang.reflect.Methodm m = mClientClass.getMethod("hello", new Class[] {});
Getting result:
Object result = m.invoke(mClientClass, new Object[] {});

Android Broadcast from Service To Activity

I am trying to send a Broadcast from a service out to an Activity. I can verify the broadcast is sent from within the service, but the Activity doesn't pick up anything.
Here is the relevant service code:
Intent i = new Intent(NEW_MESSAGE);
i.putExtra(FriendInfo.USERNAME, StringUtils.parseBareAddress(message.getFrom()));
i.putExtra(FriendInfo.MESSAGE, message.getBody());
i.putExtra("who", "1");
sendBroadcast(i);
And the receiving end in the activity class:
public class newMessage extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if(action.equalsIgnoreCase(IMService.NEW_MESSAGE)){
Bundle extra = intent.getExtras();
String username = extra.getString(FriendInfo.USERNAME);
String message = extra.getString(FriendInfo.MESSAGE);
String who = extra.getString("who");
}
}
}
The BroadcastReceiver is defined within an Activity. I am registering the receiver in the onCreate method of the Activity, not in the Manifest file.
I'm stumped as to why it won't rec. anything.
Any insight?
EDIT
Registering takes place as follows:
registerReceiver(messageReceiver, new IntentFilter(IMService.NEW_MESSAGE));
Where "messageReceiver" is defined as
private newMessage messageReceiver = new newMessage();
IMService.NEW_MESSAGE is merely a string = "NewMessage"
I'm not sure if it is specific to the set up, or if it is a fix in general, but moving the register/unregister to the onResume/onPause _respectively_ and not registering in the onCreate solved the problem for me.
Try this two things:
Use manifest file to register receiver(but it barely helps)
Try make your Receiver a regular class, not inner one.
Inner class broadcast receiver will not be able to handle broadcast(means it unable to locate that class ).
So make it as a separate class
Definitely it will work.

Categories

Resources