Access TextViews from broadcast receiver - android

In my broadcast receiver, I'm accessing a method that calls another method which is in my MainActivity class. The method from my MainActivity class uses variables that are set in the onCreate() method.
The problem I'm having is that when the broadcast receiver tries to access this method when the app has been closed, it finds the variables null because the onCreate method hasn't ran to set the variables.
Should I try to trigger the onCreate method from my broadcast receiver, or do I have this whole setup wrong? I tried to condense the code a bit so it's not too long but below you can see the gist of what I'm trying to do.
The error I'm getting is that "text" and "ringer" are null when trying to setText. This only happens when the broadcast receiver runs while the app is closed and not running.
WifiScanCompleteReceiver code:
public class WifiScanCompleteReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
...
}
public static void activate() {
...
MainActivity.statusText();
}
}
Activity code:
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
...
setContentView(R.layout.activity_main);
text = (TextView)findViewById(R.id.state);
ringer = (TextView)findViewById(R.id.mode);
state = mSettings.getString("state", "init");
mode = mSettings.getInt("ringer", 0);
statusText();
}
public static void statusText() {
text.setText(state);
if (mode == 1) {
ringer.setText("Vibrate");
} else if (mode == 2) {
ringer.setText("Normal");
} else {
ringer.setText("Unkown");
}
}
}

Instead of accessing the TextView from the receiver, I just triggered the main activity as a service with context.startService(Intent); and that seemed to get me what I wanted.
I needed to trigger the code in my main activity to run even if the main activity had been stopped. So this solved that for me.

You can solve this using interface:
1) Create an interface
interface MyListener {
public void doSomething();
}
2) Initialize the Listener in BroadcastReceiver
public class WifiScanCompleteReceiver extends BroadcastReceiver {
private MyListener listener;
public void onReceive(Context context, Intent intent) {
listener = (MyListener)context;
listener.doSomething(); // Call interface method
}
}
3) Implement interface in the Activity and override the method
public class MainActivity extends Activity {
// Your Activity code
public static void statusText() {
text.setText(state);
if (mode == 1) {
ringer.setText("Vibrate");
} else if (mode == 2) {
ringer.setText("Normal");
} else {
ringer.setText("Unkown");
}
}
#Override
public void doSomething(){
statusText();
}
}
Relevant Link:
If you want to read the advantage of using interface Read this

Its not a good approach to call activity's method directly from the receiver. App will crash in a case when your activity is not visible, but due to receiver's call it will try to execute activity's code.
You can use local broadcast here. Instead of calling activity's method from receiver send local broadcast, which you need to register in your activity and in receiver of local broadcast call your activity method. This method(Activity's method) call from local broadcast will only execute when your activity will be visible and will not result in app crash.

Related

Best alternative to Stop an Activity started from a Service

I started an activity with the following code from my service:
Intent cmActivityIntent = new Intent(getApplicationContext(), CallManagementActivity.class);
cmActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(cmActivityIntent);
In the app's manifest the launchMode of the activity is defined as android:launchMode="singleTask".
I've been looking for ways of stopping the activity on the service's onDestroy() but I haven't been able to find any documentation that can help me. And I've only found one way of doing it.
I've seen the aproach of checking on the serviceif the activity is instanced using an activity-class-static-property (public static boolean isInstanced) and sending another intent to the activity with a FINISH_ACTIVITY extra if it is. Then, on the activity's onNewIntent() the flag would be checked and the activity would be finished if the flag was included.
This approach doesn't feel right to me, since intents are supposed to be used start activities and not stopping them.
Does someone know any other way of how to acomplish this?
Many alternatives have been given to solve this issue:
Use an Intent and set an Extra.
Send a broadcast intent from the Service and register a BroadcastReceiver on the Activity.
Use the LocalBroadcastManager (similar to previous alternative)
Bind the Activity to the Service and pass it a Messenger so the Service can use it to communicate with the Activity.
None of them have I found a good solution, in fact, since the service is only accesible from the package, the solution I'm sticking with is the Observer Pattern.
Next you'll find the code I used. The activities implement the Observer interface (my own) and the service the abstract class ObservableService.
ObservableService:
public abstract class ObservableService extends Service {
#Override
public void onCreate() {
super.onCreate();
serviceStarted = true;
observers = new ArrayList<Observer>();
}
#Override
public void onDestroy() {
super.onDestroy();
serviceStarted = false;
}
//---------------------------------------------------------
//
// Singleton methods
//
//---------------------------------------------------------
/**
* Flag set to <code>true</code> if the service is running and
* <code>false</code> if not.
*/
private static boolean serviceStarted;
/**
* Used to check if the service is started.
* #return <code>true</code> if the service is running and <code>false</code>
* if not.
*/
public static boolean isServiceStarted() {
return serviceStarted;
}
//---------------------------------------------------------
//
// Observable methods an constants
//
//---------------------------------------------------------
/**
* Used to notify the observers that the service has been stopped.
*/
public static String UPDATE_TYPE_SERVICE_STOPPED = "service_stopped";
protected static ArrayList<Observer> observers;
public static void addObserver(Observer observer) {
observers.add(observer);
}
public static void removeObserver(Observer observer) {
observers.remove(observer);
}
protected static void notifyObservers(String updateType) {
for (Observer observer : observers) {
observer.onObservableUpdate(updateType);
}
}
}
Observer:
public interface Observer {
public void onObservableUpdate(String updateType);
}
The activities just register themselves as observers at onResume() and unregister at onPause(). The isServiceStarted() is used to finish the activity if the service is already stopped.
#Override
protected void onResume() {
super.onResume();
if (InCallService.isServiceStarted()) {
InCallService.addObserver(this);
} else {
this.finish();
}
}
#Override
protected void onPause() {
super.onPause();
InCallService.removeObserver(this);
}
public void onObservableUpdate(String updateType) {
if (updateType.equals(ObservableService.UPDATE_TYPE_SERVICE_STOPPED)) {
this.finish();
}
}
I chose this because I found it to be a very simple and high-performance alternative.

Update view from different thread Android

I make a simple Android Apps that will update its view when an SMS is received. This is the code for my receiver class
public class SMSReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
intent.setClass(context, SMSReceiverService.class);
intent.putExtra("result", getResultCode());
WakefulIntentService.sendWakefulWork(context.getApplicationContext(), intent);
}
}
That class will call SMSReceiverService class to handle the oncoming SMS and execute method notifyMessageReceived, which is shown below.
private void notifyMessageRecevied(SMS message) {
if(!isInMyApps()) {
launchPopUp();
}
else {
//updating view should go here
}
}
The problem is, I don't know how to update the view in my activity (which is in separate class, not the SMSReceiverService class), when I tried to update my TextView in my activity, it thrown an CalledFromWrongThreadException. Can anybody please help me ?
Thanks in advance, and sorry about my bad English...
You can create an activity variable to hold the instance of the activity you want.
In SMSReceiver (the one you want to call):
SMSReceiverService.setMainActivity(this);
In SMSReceiverService (the one you want to update from):
public static SMSReceiver smsActivity;
public static void setMainActivity(SMSReceiver activity)
{
smsActivity = activity;
}
...
smsActivity.runOnUiThread(new Runnable() {
public void run() {
try{
smsActivity.textView.setText("");
}
catch{}
}
}
Assuming SMSActivity is the file that contains the view you want to update.
Assuming the service has Context of User Activity
Activity a=(Activity)userContext;
a.runOnUiThread(/*runnable method of activity which calls UpdateView() */);

Android MediaPlayer On Stop Event, or similar, exists?

I have several activities which use several audio features. For that, I have a MediaPlayer in a singleton java class, so the activities interact with that class and just exist on the media player.
One of the features is to stop automatically the media player after X minutes. So I created a timer in the singleton class and stops perfectly the radio streaming. the problem is that there is no feedback or callback to the running activity. There is a play/stop button wich has to change the image and I do not know how can I capture that onStop event or whatever....or can be called from a single java class the current activity class running, so I could call a function of the activity in order to change the image?
You probably want to use a broadcast receiver for this.
From your singlton class which does the stopping, when your timer stops the music, call this method:
public void broadcastMusicPaused(View v){
Intent broadcast = new Intent();
broadcast.setAction("MUSIC_STOPPED");
sendBroadcast(broadcast);
}
Then, from your controlling activity, set up your receiver like this:
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), "Music Paused", Toast.LENGTH_SHORT).show();
displayMusicStopped(); //switches images
}
};
#Override
protected void onResume() {
IntentFilter filter = new IntentFilter();
filter.addAction("MUSIC_STOPPED");
registerReceiver(receiver, filter);
super.onResume();
}
#Override
protected void onPause() {
unregisterReceiver(receiver);
super.onPause();
}
First of all, thanks jameo for his answer, sounds pretty good, but i do not know if i will have time to try, i promise i will if i can this week or next time i have a similar issue.
Finally i did the trick this way:
1 - Create a Interface with Method onStopMediaPlayer(); //For example call MediaPlayerStopInterface
public interface MediaPlayerStopInterface {
/**
* Called when the player timer ends
*/
public void onStopMediaPlayer();
}
2 - My activities classes implements the interface switching images.
public class PortadaActivity extends Activity implements MediaPlayerStopInterface{
public void onStopMediaPlayer(){
//Switch images or whatever
}
}
3 - My singletton class has an object of the type of the interface MediaPlayerStopInterface
public class AudioControllerClass { //The Singletton Java Class
private MediaPlayerStopInterface currentActivity;
public void setCurrentActivity(MediaPlayerStopInterface mpsi){
currentActivity=mpsi;
}
}
4 - My activities classes in onResume() do a Singlettonclass.setStoppedPlayerInterface(this), so i always have a reference of the running activitie.
public class PortadaActivity extends Activity implements MediaPlayerStopInterface{
public void onResume() {
AudioControllerClass.getInstance(getApplicationContext()).setCurrentActivity(this); //In every resume the singletton class knows who was the last one in being active
}
}
5 - when timer execute, as i have the activitie class reference, i just call object_StoppedPlayerInterface.stoppedPlayer();
public class AudioControllerClass { //The Singletton Java Class
class TimerRadio extends TimerTask {
public void run() {
if(whatever==true){
currentActivity.onStopMediaPlayer();
}
}
}
}
Finally, i didn't code it, but the callback to onStopMediaplayer in activities must be done with a Handler, if you do not want a "Only UI thread can touch his views" exception :P
It works perfectly :). But i don't know if it is a really bad practice or is not so horrible xD
Anyway thanks Jameo. Yours sound much more elegant :P

How to Call an activity method from a BroadcastReceiver?

I am currently developing an app, I need to call an activity method from BroadcastReceiver.
My app is to when the user getting a call Broadcast receiver find that and send a message to that particular number.
I am able to get that no, but I want to send a message to that number, for this sending sms I created an activity in side sending sms method is there..
How do I call that particular method from my BroadcastReceiver.
You need to make that method static and public inside your activity.You can call that method like this:
ActivityName.mehtodName();
It will be much better if you put that method in your util class and call it from there.
You can create a Callback using an interface for calling that the activity method from BroadcastReceiver
interface MyCallback{
public void doStuff();
}
Provide the CallBack in the BroadcastReceiver
public class MyBroadcastReceiver extends BroadcastReceiver{
private MyCallback callback;
public MyBroadcastReceiver(MyCallback callback){
this.callback = callback; //<-- Initialse it
}
#Override
public void onReceive(Context context, Intent intent) {
callback.doStuff(); //<--- Invoke callback method
}
}
Now implement the Callback in your Activity and override the method
MyActvity extends AppcompatActivity implements MyCallback {
// Your Activity related code //
// new MyBroadcastReceiver(this); <-- this is how you create instance
private void sendSMS(){
// your logic to send SMS
}
#Override
public void doStuff(){
sendSMS(); //<--- Call the method to show alert dialog
}
}
You can read the advantage of using this approach here in detail

how to come back to my application after ACTION_CALL in android

I've got one question regarding the intent action ACTION_CALL.
What is the correct way of getting back to the own application/activity after the user ends the call?
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" +m1));
startActivity(intent);
I have made the call using the above code. Please provide me with a solution to call my own activity after the call action.
unfortunately some phones have settings to force going into for example the Call Log after a call...
However, you can run a loop after your startActivity to check TelephonyManager.getCallState, and when it's again TelephonyManager.CALL_STATE_IDLE, you can restart your own Activity
be sure to add some sleep to the loop
AFter the endofcall.......it just had to come back to the activity..!! you can handle that one onRestart();
I run with the same problem, and ended up solving it like this:
Make a Callback interface with single (or multiple if you want)
methods
Implement that interface in your activity
Make a reference to that interface inside PhoneStateListener class
Call a method within that interface when the call ended
public class CallTracker extends PhoneStateListener implements Runnable {
private Context mActivity;
private Callback mCallback;
public interface Callback {
void onCallEnded();
}
public CallTracker(Activity activity) {
super();
mActivity = activity;
if (mActivity instanceof Callback) {
mCallback = (Callback) mActivity;
}
}
#Override public void onCallStateChanged(int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_IDLE) {
mCallback.onCallEnded();
}
}
public class CallerActivity extends AppCompatActivity implements
CallTracker.Callback {
#Override public void onCallEnded() {
Toast.makeText(this, "Call ended!", Toast.LENGTH_SHORT).show();
}
}

Categories

Resources