imagine this stack situation :
A - B- C - D - B , A, B and C are activities, but D is a service.
Basically, i have a service (D) and from that service i want to call an already existing activity (B).
I know that if I want to re-use an activity, all i got to do is to use the flags (or change the manifest) SingleTop (will re-use the activity if it is already on top) or SingleTask (will re-use the activity whether it is on top or not).
The problem is that since i am inside a Service, i will have to add the flag FLAG_ACTIVITY_NEW_TASK, so that i can call an activity. Also, i added SingleTask as a launch mode in my manifest so that that activity will be re-used.
This works fine since it re-uses the same activity and comes back to the method onNewIntent(Intent intent). The problem is that everything that i put as an extra on that intent comes as null. I try to send 2 strings and 2 booleans through that intent and all of them reach the onNewIntent(Intent intent) as null.
How can i solve this ? Do I have to do something inside the onNewIntent(Intent intent) method, before getting the extras ? Are there any better alternatives ?
PS:
I heard about the StartActivityForResult or something similar. That would only work 50% of the times, since this is for a " chat-like" application.
So I will be on the "chat", from where I go to another activity where i can select something to send. After that, i will go to the service, where the transfer is done, and then back to the "chat".
But when I receive something, I am already on the "chat", so the startActivityForResult wouldn't work in this case (the service to receive would be running on the background + I don't wan't to finish the receiving part, because i want to be always listening for something).
Here is the code from the service where i try to re-launch the single activity :
Intent transfRec=new Intent(ServerComm.this ,TransferRecordActivity.class);
transfRec.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
transfRec.putExtra("receivedFilePath",appName+".apk");
transfRec.putExtra("joinMode","appselection");
transfRec.putExtra("nameOfTheApp",appName);
transfRec.putExtra("received",false);
transfRec.putExtra("isHotspot",isHotspot);
startActivity(transfRec);
Here is the code of my onNewIntent(Intent intent) :
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
System.out.println("I am here in the new intent");
if(intent==null){
System.out.println("Intent is null inside the new intent method !!!");
}
tmpFilePath=getIntent().getStringExtra("receivedFilePath");
System.out.println("The tmpFilePath is : "+tmpFilePath);
received=getIntent().getBooleanExtra("received",true);
nameOfTheApp=getIntent().getStringExtra("nameOfTheApp");
isHotspot=getIntent().getStringExtra("isHotspot");
System.out.println("O received boolean esta a : : : "+received);
textView.setVisibility(View.GONE);
receivedFilePath= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/"+tmpFilePath;
System.out.println("transfer REcord Activity _: the received file path is :::: " +receivedFilePath);
getReceivedApps();
adapter= new HighwayTransferRecordCustomAdapter(this,listOfItems);
receivedAppListView.setAdapter(adapter);
EDIT: As you guys can see i check if the intent is null, and it is not, since it doesn't do the system.out.println that there is on that condition !
The issue is that you are calling getIntent() within your onNewIntent(). From the docs for getIntent():
Return the intent that started this activity.
Therefore, you are getting the intent provided to onCreate(). To get the intent supplied to onNewIntent(), you simply use intent which is provided in the method signature:
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
tmpFilePath=intent.getStringExtra("receivedFilePath");
...
}
Related
I am working on an app where I am drawing my custom lockscreen (An Activity) over the system's default lock.
Everything is working perfectly fine except one thing, I am using a reciever and whenever there is an incoming call, that reciever gets called and from inside that reciever I am closing the activity.
Note : This is happening only in case of OnePlus device, on any other device it's working perfectly.
private class CallStateListener extends PhoneStateListener {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
OverlayActivity overlayActivity = new OverlayActivity();
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
// System.out.println("RINGING");
overlayActivity.finish();
// System.out.println("Activity has been closed!!");
break;
}
}
}
Both the lines before and after the code where I am closing the activity is working completely fine, but the activity is not getting closed.
And if you are creating Activity Object its not working at all you
needs and Actual Activity Object or Context of Activity to close it.
I have a class in a service
MyClass m = new MyClass();
and inside my class I check if I have permission to overlay the view; if so, it's ok, otherwise I must start an activity
if (Settings.canDrawOverlays(mContext)) {
// draw over app
} else {
// start the activity
Intent i = new Intent(context,Calls.class);
context.startActivity(i);
}
When I start the activity I have a problem communicating between the class and the activity. I know how to use the interface but how can I register it in activity.
Some time I want to pass an object or data from the class to the activity or from the activity to the class... how can I do that?
I saw many examples in Stack Overflow about how to communicate between service and activity; they suggest to start the class from the activity but this does not work in my app because my class must be running all the time.
Perhaps you could use an event bus like mechanism where you can send or receive events through out your app, Though there are several libraries out there, I would recommend using Otto library for android.
Usage is pretty simple just register in your activity onCreate
Bus bus = new Bus();
bus.register(this);
For sending events
// example data to post
public class TestData {
public String message;
}
// post this data
bus.post(new TestData().message="Hello from the activity");
And subscribe to events like this
#Subscribe public void getMessage(TestData data) {
// TODO: React to the event somehow!
}
More info here
If you want to implement a communication pattern between a Service and an Activity, you should use a LocalBroadcastManager.
It will turn handy because, in case your Service is still on but your Activity
has been destroyed (very common situation), then the 'messagging' between the two will simply have no effect (no NPE or whatsoever will be thrown).
Step 1
Create a BroadcastReceiver in your Activity and define an ID / Filter
this.localBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Do what you have to do here if you receive data from the Service / Background Task
}
}
public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL")
Step 2
In your Activity register the broadcast in onResume() and unregister it in onPause().
#Override
protected void onResume() {
// Listen if a Service send me some data
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}
#Override
protected void onPause() {
// I'm going to the background / or being destroyed: no need to listen to anything anymore...
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}
Your Activity is now ready to receive data from any other component in your Application.
If it's in the background, then there is no need to update the UI: in fact the Activity will not respond if in the background.
In the same way, if it's being garbage collected, the Receiver will be unregistered and the Activity will just not respond to anything.
If the Activity is resumed / restarted, onResume() will be triggered and the Receiver will be registered again.
Step 3
All you need to do right now, is send data from the Service.
Simply call
final Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER);
// put your data in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
and your Activity will accordingly respond to the signal.
It's surprising how few people know about the LocalBroadcastManager and instead use some self-implemented callback / singleton pattern, which increases complexity and non-readability.
This pattern is built-in in Android, so you don't need external libraries. As for security, this ensures that your signals stay internal to your application: no data can therefore be read by other apps.
I similarly answered to another question here.
I'm a bit confused on Intents.
Why is
Intent implicit=new Intent(IDownload.class.getName());
an Implicit intent
while
Intent explicit=new Intent(implicit);
is an Explicit intent while it doesn't seem to add anything new in its definition. The system doesn't seem to draw any new information that wasn't previously provided by implicit intent above?
In the Android documentation (Intent types),
Explicit intents specify the component to start by name (the fully-qualified class name). You'll typically use an explicit intent to start a component in your own app, because you know the class name of the activity or service you want to start......
Intent implicit=new Intent(IDownload.class.getName()); seems to fulfill this requirement, but is still regarded as an Implicit intent, which as per the documentation:
Implicit intents do not name a specific component, but instead declare a general action to perform, which allows a component from another app to handle it.....
Intent explicit=new Intent(implicit); seems to contradict this, but is still regarded as an Explicit intent.
UPDATE - Sample implementation
ref : Binding to a Service
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
appContext=(Application)getActivity().getApplicationContext();
Intent implicit=new Intent(IDownload.class.getName());
List<ResolveInfo> matches=getActivity().getPackageManager()
.queryIntentServices(implicit, 0);
if (matches.size() == 0) {
Toast.makeText(getActivity(), "Cannot find a matching service!",
Toast.LENGTH_LONG).show();
}
else if (matches.size() > 1) {
Toast.makeText(getActivity(), "Found multiple matching services!",
Toast.LENGTH_LONG).show();
}
else {
ServiceInfo svcInfo=matches.get(0).serviceInfo;
try {
String otherHash=SignatureUtils.getSignatureHash(getActivity(),
svcInfo.applicationInfo.packageName);
String expected=getActivity().getString(R.string.expected_sig_hash);
if (expected.equals(otherHash)) {
Intent explicit=new Intent(implicit);
ComponentName cn=new ComponentName(svcInfo.applicationInfo.packageName,
svcInfo.name);
explicit.setComponent(cn);
appContext.bindService(explicit, this, Context.BIND_AUTO_CREATE);
}
else {
Toast.makeText(getActivity(), "Unexpected signature found!",
Toast.LENGTH_LONG).show();
}
}
catch (Exception e) {
Log.e(getClass().getSimpleName(), "Exception trying to get signature hash", e);
}
}
}
The linked example includes these lines
Intent explicit=new Intent(implicit);
ComponentName cn=new ComponentName(svcInfo.applicationInfo.packageName,
svcInfo.name);
explicit.setComponent(cn);
The first line simply creates a new Intent instance that's a copy of the old one. While the variable might be explicit, at this point in time, it is still an implicit intent.
The second line creates a ComponentName object based on the single matching result from an earlier getActivity().getPackageManager().queryIntentServices(implicit, 0) call. At this point, explicit is still an implicit intent.
The third line is the magic. Once the specific ComponentName is assigned to the Intent instance, explicit truly becomes an explicit intent.
I've a Fragment fr, Service ser, BroadcastReceiver brc.
In fr I have a switch, when checked it launches ser, when un-checked it stops it, so far so good.
In ser, I have registered a BroadcastReceiver named brc and added an IntentFilter named USER_PRESENT, When the device is locked, I have the Service call stopSelf() and register that BroadcastReceiver, when the phone is unlocked, onReceive() will be called, in that method I've added a code to start the Service again .
Now, the problem .. I can't stop the Service when the switch is un-checked, even though I'm calling stopService(intentname); .
And my guess is, it's a different Intent which launched by a different class. So I've stopped that Intent like this:
stopService(brc.intentname);
Nothing happened and the Service keeps running. What's the solution for this?.
Code:
Fragment:
if ( !Switched ) {
getActivity().startService(ServiceIntent);
}
if( Switched ){
getActivity().stopService(ServiceIntent);
getActivity().stopService(BroadcastTest.ServiceIntent);
}
Service:
if( !isScreenOn && isLocked )
{
this.getApplicationContext().registerReceiver(new BroadcastTest(), new IntentFilter("android.intent.action.USER_PRESENT"));
CancelTimer();
stopSelf();
}
BroadcastTest:
ServiceIntent = new Intent(context, Service.class);
context.startService(ServiceIntent);
I search a lot and I tried several ways but I couldn't find anything that avoid my error.
I'm working on app for my learning, where is an String in my MainActivity and then I call it in my Service. I tried something like this:
This next one goes in my myService.class
//In my myService.class that extends Service
public class myService extends Service{
AlarmManager manager;
PendingIntent pintent;
String te;
#Override
public void onCreate()
{
manager = (AlarmManager)(this.getSystemService(Context.ALARM_SERVICE));
pintent = PendingIntent.getBroadcast( this, 0, new Intent("blahblah"), 0 );}
#Override
public int onStartCommand(Intent intent, int flags, int startid)
{
super.onStartCommand(intent, flags, startid);
te = intent.getStringExtra("tst"); //if I change this line to te = "something", everything goes fine
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive( Context context, Intent intent )
{
Toast.makeText(getApplicationContext(),te, Toast.LENGTH_SHORT).show();
}
};
this.registerReceiver(receiver, new IntentFilter("blahblah") );
// set alarm to fire 30 min and waits 2 min
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1000*30*60, 1000*2*60, pintent);
return START_STICKY;}
public IBinder onBind(Intent p1)
{
// TODO: Implement this method
return null;
}
}
Here the code runs perfectly, but when I exit my App, it crashes. After 1 minute, my device shows again that my App crashes, confirming my app "successfully" ran into background. What is wrong with it?
I also learned I could use IntentService instead of Service, wich one is better for long tasks and what is the difference between them ?
EDIT***
I received the following error: java.lang.NullPointerExeption.
So I change this:
te = intent.getStringExtra("tst");
To this:
try
{
te = intent.getStringExtra("tst");
}
catch(NullPointerException e)
{}
When I changed it my app works with any error, but The problem is: I need to retrieve my String from my MainActivity, when I close my app my service runs without errors but my "te" String takes null valor, what can I do to "save" my String in my service, to be able to use it and keeping showing the "working" message after I close my App ? Should I use SharedPreferences ?
Hope I was clear.
IntentService is different from Service.
IntentService Self kills the service as it finishes the task. Whereas Service runs for ever unless you kill it.
For my coding experience, I would use IntentService only for small tasks that run for a couple of seconds and use Service for long running one and call StopSelf() as needed.
Kindly post the log to answer your problem