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
Related
I have two activities SplashScreen and MainActivity.
MainActvity download some data and show it.
Now I want to start both activities on application start, MainActivity in background and SplashScreen in foreground and when the data of MainActivity download complete I want to make MainActivity foreground and finish SplashScreen.
I have seen many solution but no one is going good in my situation.
Please give me some suggestion or example .
I think it's better to make splash screen to download data and when it completed show new activity (MainActivity). In onStart metod use downloaded data stored on phone.
Your example: The only way You can have resolve this your way is by start MainActivity first and from onStart start SplashScreen:
class SplashScreen extends Activitiy {
public void onStart() {
EventBus.register(this);
}
#Subscribe
public void finishedDownload(FinishedDownloadEvent) {
this.finish()
}
}
class MainActivity extens Activity () {
public void onStart () {
open(new Intent(SplashScreen.class)); // We would like to have
download();
}
public void download (OnFinish onFinish) {
... download
EventBus.post(new FinishedDownloadEvent());
}
}
I forgot:
class EventBus {
private final static Bus instance = new Bus();
public static void register (Object obj) {
instance.register(obj)
}
public static void post (Event obj) {
instance.post(obj)
}
}
EventBuss
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.
In this question some years ago someone suggested unit-testing a startService call by using a specific Context.
Now, about 4 years later, I am wondering if this functionality can't be handled by some Framework like Espresso.
I have an intentservice:
public class MyService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
if (/* condition */) {
/* do Something short-lasting */
}
return;
}
My code starts a service in onCreate()
public class ListActivity extends SomeActivityClass {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService(new Intent(this, MyService.class));
}
My testcode expects the service to be started:
#RunWith(AndroidJUnit4.class)
public class ListActivityTest {
#Rule
public final ActivityTestRule<ListActivity> rule
= new ActivityTestRule<ListActivity>(ListActivity.class, true, false);
#Test
public void test_startService() {
Intents.init();
Context targetContext = InstrumentationRegistry.getTargetContext();
Intent showListIntent = new Intent(targetContext, ListActivity.class);
rule.launchActivity(showListIntent);
Intents.intended(IntentMatchers.hasComponent(MyService.class.getName()));
Intents.release();
}
}
My problem is, that it seems that the launch of the ListActivity is done but the framework does not wait for the intent to start the service to be issued.
I've tried to use an IdlingResource, but that means that i would have to add test code to production code, which i don't want to do, obviously: The IntentService i want to start is a short running piece of code, so recording some state about wether the IntentService is running or has ended already to return it to the IdlingResource callback is not an option.
Any hints how to check the startService call?
EDIT
Well, it seems that Robolectric can achieve exactly what i wanted using shadows.
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() */);
I have a service which has a method that downloads an image from an URL and returns an Uri.
That service will get more complex when it has all the intended features. Therefore,
I'm invoking its methods within a thread.
My problem is how to warn the activity that the service has done it's work.
I could change a class isFinished variable but the activity had to be constantly checking
for its value.
I just want the service to tell the activity that it's work is done and the resources are
available for use.
I thought something in the lines of the service calling stopSelf() and the activity was
warned through "onServiceDisconnected" but that didn't seem very "political correct".
Thanks in advance
There are two ways to do it.
1. You can start your activity using by firing an intent.
2. You can Broadcast an intent and write receiver for it in your app when your receiver receives intent and onreceive method is called in this method you can start your activity using intent.
cheers...
public class MyActivity extends Activity{
public MyActivity() {
...
MyThread thread = new MyThread(this);
thread.start();
}
public void onFinishedThread(...) {
}
}
class MyThread extends Thread {
MyActivity activity;
public MyThread(MyActivity activity) {
this.activity = activity;
}
public void run() {
// do work
...
this.activity.onFinishedThread(...);
}
}