Here my scenario in which i am starting a service from an Activity which play music in background. When i press back button on this activity, activity get destroyed. but service is still running in background. I want to show a notification to user when this activity get destroyed so that they can play/pause/stop audio from notification. but i dont want notification to be displayed when the service get started.
below is my activity code :
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
public class PlayBackgroundAudioActivity extends AppCompatActivity {
private AudioServiceBinder audioServiceBinder = null;
private Handler audioProgressUpdateHandler = null;
// Show played audio progress.
private ProgressBar backgroundAudioProgress;
private TextView audioFileUrlTextView;
// This service connection object is the bridge between activity and background service.
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
// Cast and assign background service's onBind method returned iBander object.
audioServiceBinder = (AudioServiceBinder) iBinder;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_background_audio);
setTitle("dev2qa.com - Play Audio Use Background Service");
// Bind background audio service when activity is created.
bindAudioService();
final String audioFileUrl = "http://www.dev2qa.com/demo/media/test.mp3";
backgroundAudioProgress = (ProgressBar)findViewById(R.id.play_audio_in_background_service_progressbar);
// Get audio file url textview.
audioFileUrlTextView = (TextView)findViewById(R.id.audio_file_url_text_view);
if(audioFileUrlTextView != null)
{
// Show web audio file url in the text view.
audioFileUrlTextView.setText("Audio File Url. \r\n" + audioFileUrl);
}
// Click this button to start play audio in a background service.
Button startBackgroundAudio = (Button)findViewById(R.id.start_audio_in_background);
startBackgroundAudio.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Set web audio file url
audioServiceBinder.setAudioFileUrl(audioFileUrl);
// Web audio is a stream audio.
audioServiceBinder.setStreamAudio(true);
// Set application context.
audioServiceBinder.setContext(getApplicationContext());
// Initialize audio progress bar updater Handler object.
createAudioProgressbarUpdater();
audioServiceBinder.setAudioProgressUpdateHandler(audioProgressUpdateHandler);
// Start audio in background service.
audioServiceBinder.startAudio();
backgroundAudioProgress.setVisibility(ProgressBar.VISIBLE);
Toast.makeText(getApplicationContext(), "Start play web audio file.", Toast.LENGTH_LONG).show();
}
});
// Click this button to pause the audio played in background service.
Button pauseBackgroundAudio = (Button)findViewById(R.id.pause_audio_in_background);
pauseBackgroundAudio.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
audioServiceBinder.pauseAudio();
Toast.makeText(getApplicationContext(), "Play web audio file is paused.", Toast.LENGTH_LONG).show();
}
});
// Click this button to stop the media player in background service.
Button stopBackgroundAudio = (Button)findViewById(R.id.stop_audio_in_background);
stopBackgroundAudio.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
audioServiceBinder.stopAudio();
backgroundAudioProgress.setVisibility(ProgressBar.INVISIBLE);
Toast.makeText(getApplicationContext(), "Stop play web audio file.", Toast.LENGTH_LONG).show();
}
});
}
// Bind background service with caller activity. Then this activity can use
// background service's AudioServiceBinder instance to invoke related methods.
private void bindAudioService()
{
if(audioServiceBinder == null) {
Intent intent = new Intent(PlayBackgroundAudioActivity.this, AudioService.class);
// Below code will invoke serviceConnection's onServiceConnected method.
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
// Unbound background audio service with caller activity.
private void unBoundAudioService()
{
if(audioServiceBinder != null) {
unbindService(serviceConnection);
}
}
#Override
protected void onDestroy() {
// Unbound background audio service when activity is destroyed.
unBoundAudioService();
super.onDestroy();
}
// Create audio player progressbar updater.
// This updater is used to update progressbar to reflect audio play process.
private void createAudioProgressbarUpdater()
{
/* Initialize audio progress handler. */
if(audioProgressUpdateHandler==null) {
audioProgressUpdateHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// The update process message is sent from AudioServiceBinder class's thread object.
if (msg.what == audioServiceBinder.UPDATE_AUDIO_PROGRESS_BAR) {
if( audioServiceBinder != null) {
// Calculate the percentage.
int currProgress =audioServiceBinder.getAudioProgress();
// Update progressbar. Make the value 10 times to show more clear UI change.
backgroundAudioProgress.setProgress(currProgress*10);
}
}
}
};
}
}
#Override
public void onBackPressed() {
startActivity(new Intent(PlayBackgroundAudioActivity.this,ForeGroundService.class));
finish();
}
}
below is my service code:
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class AudioService extends Service {
private AudioServiceBinder audioServiceBinder = new AudioServiceBinder();
public AudioService() {
}
#Override
public IBinder onBind(Intent intent) {
return audioServiceBinder;
}
}
below is myaudio binder class:
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import java.io.IOException;
/**
* Created by Jerry on 2/15/2018.
*/
public class AudioServiceBinder extends Binder {
// Save local audio file uri ( local storage file. ).
private Uri audioFileUri = null;
// Save web audio file url.
private String audioFileUrl = "";
// Check if stream audio.
private boolean streamAudio = false;
// Media player that play audio.
private MediaPlayer audioPlayer = null;
// Caller activity context, used when play local audio file.
private Context context = null;
// This Handler object is a reference to the caller activity's Handler.
// In the caller activity's handler, it will update the audio play progress.
private Handler audioProgressUpdateHandler;
// This is the message signal that inform audio progress updater to update audio progress.
public final int UPDATE_AUDIO_PROGRESS_BAR = 1;
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public String getAudioFileUrl() {
return audioFileUrl;
}
public void setAudioFileUrl(String audioFileUrl) {
this.audioFileUrl = audioFileUrl;
}
public boolean isStreamAudio() {
return streamAudio;
}
public void setStreamAudio(boolean streamAudio) {
this.streamAudio = streamAudio;
}
public Uri getAudioFileUri() {
return audioFileUri;
}
public void setAudioFileUri(Uri audioFileUri) {
this.audioFileUri = audioFileUri;
}
public Handler getAudioProgressUpdateHandler() {
return audioProgressUpdateHandler;
}
public void setAudioProgressUpdateHandler(Handler audioProgressUpdateHandler) {
this.audioProgressUpdateHandler = audioProgressUpdateHandler;
}
// Start play audio.
public void startAudio()
{
initAudioPlayer();
if(audioPlayer!=null) {
audioPlayer.start();
}
}
// Pause playing audio.
public void pauseAudio()
{
if(audioPlayer!=null) {
audioPlayer.pause();
}
}
// Stop play audio.
public void stopAudio()
{
if(audioPlayer!=null) {
audioPlayer.stop();
destroyAudioPlayer();
}
}
// Initialise audio player.
private void initAudioPlayer()
{
try {
if (audioPlayer == null) {
audioPlayer = new MediaPlayer();
if (!TextUtils.isEmpty(getAudioFileUrl())) {
if (isStreamAudio()) {
audioPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
audioPlayer.setDataSource(getAudioFileUrl());
} else {
audioPlayer.setDataSource(getContext(), getAudioFileUri());
}
audioPlayer.prepare();
// This thread object will send update audio progress message to caller activity every 1 second.
Thread updateAudioProgressThread = new Thread()
{
#Override
public void run() {
while(true)
{
// Create update audio progress message.
Message updateAudioProgressMsg = new Message();
updateAudioProgressMsg.what = UPDATE_AUDIO_PROGRESS_BAR;
// Send the message to caller activity's update audio prgressbar Handler object.
audioProgressUpdateHandler.sendMessage(updateAudioProgressMsg);
// Sleep one second.
try {
Thread.sleep(1000);
}catch(InterruptedException ex)
{
ex.printStackTrace();
}
}
}
};
// Run above thread object.
updateAudioProgressThread.start();
}
}catch(IOException ex)
{
ex.printStackTrace();
}
}
// Destroy audio player.
private void destroyAudioPlayer()
{
if(audioPlayer!=null)
{
if(audioPlayer.isPlaying())
{
audioPlayer.stop();
}
audioPlayer.release();
audioPlayer = null;
}
}
// Return current audio play position.
public int getCurrentAudioPosition()
{
int ret = 0;
if(audioPlayer != null)
{
ret = audioPlayer.getCurrentPosition();
}
return ret;
}
// Return total audio file duration.
public int getTotalAudioDuration()
{
int ret = 0;
if(audioPlayer != null)
{
ret = audioPlayer.getDuration();
}
return ret;
}
// Return current audio player progress value.
public int getAudioProgress()
{
int ret = 0;
int currAudioPosition = getCurrentAudioPosition();
int totalAudioDuration = getTotalAudioDuration();
if(totalAudioDuration > 0) {
ret = (currAudioPosition * 100) / totalAudioDuration;
}
return ret;
}
}
Basically you should reconsider the time when your notification is shown. Since the onDestroy method of an activity may not be always called, I would prefer to use another point when to display your notification. You should be also aware of the new restrictions regarding background services since android 8 (API 26), if you do not explicitly mark them as a foreground service, then they might be killed by the OS while the app is in the background.
So, for your purpose it might be an option to use the method startForeground with a notification id and call it when your activity is going to the background (e.g. at onStop), then it will display the notification (which is the requirement for keeping the service in the foreground). If you decide to go back to your activity after some time, you may call stopForeground to stop the foreground mode and dismiss your notification.
Related
Right now in my application downloading code is working but when i download multiple files it download randomly not in queue, i need it download in queue and this current download is not displaying in notification bar only display in activity.
I want to know how to possible multiple downloading with displaying in notification bar and also display progress bar in current activity.
Thank you
Looper
By using looper you download multiple file in queue one by one.
Steps 1: Lets create DownloadThreadListener.java interface which will be used to get thread updates.
package com.example.looper;
public interface DownloadThreadListener {
void handleDownloadThreadUpdate();
}
Steps 2: Lets now create a class named DownloadTask.java which will simulate the downloading. We will be using random time sleep to simulate the download time.
package com.example.looper;
import java.util.Random;
import android.util.Log;
/**
* This is not a real download task. It just sleeps for some random time when
* it's launched. The idea is not to require a connection and not to eat it.
*
*/
public class DownloadTask implements Runnable {
private static final String TAG = DownloadTask.class.getSimpleName();
private static final Random random = new Random();
private int lengthSec;
public DownloadTask() {
lengthSec = random.nextInt(3) + 1;
}
#Override
public void run() {
try {
Thread.sleep(lengthSec * 1000);
// it's a good idea to always catch Throwable
// in isolated "codelets" like Runnable or Thread
// otherwise the exception might be sunk by some
// agent that actually runs your Runnable - you
// never know what it might be.
} catch (Throwable t) {
Log.e(TAG, "Error in DownloadTask", t);
}
}
}
Steps 3: Now lets create the thread subclass which will act as pipeline. First we will call Looper.prepare() to make this Thread act as pipeline. Next new Handler() will be called to handle message queue on this thread. Finally Looper.loop() will be called to start running the message loop. New tasks will be added using enqueueDownload(final DownloadTask task).
package com.example.looper;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
public final class DownloadThread extends Thread {
private static final String TAG = DownloadThread.class.getSimpleName();
private Handler handler;
private int totalQueued;
private int totalCompleted;
private DownloadThreadListener listener;
public DownloadThread(DownloadThreadListener listener) {
this.listener = listener;
}
#Override
public void run() {
try {
// preparing a looper on current thread
// the current thread is being detected implicitly
Looper.prepare();
Log.i(TAG, "DownloadThread entering the loop");
// now, the handler will automatically bind to the
// Looper that is attached to the current thread
// You don't need to specify the Looper explicitly
handler = new Handler();
// After the following line the thread will start
// running the message loop and will not normally
// exit the loop unless a problem happens or you
// quit() the looper (see below)
Looper.loop();
Log.i(TAG, "DownloadThread exiting gracefully");
} catch (Throwable t) {
Log.e(TAG, "DownloadThread halted due to an error", t);
}
}
// This method is allowed to be called from any thread
public synchronized void requestStop() {
// using the handler, post a Runnable that will quit()
// the Looper attached to our DownloadThread
// obviously, all previously queued tasks will be executed
// before the loop gets the quit Runnable
handler.post(new Runnable() {
#Override
public void run() {
// This is guaranteed to run on the DownloadThread
// so we can use myLooper() to get its looper
Log.i(TAG, "DownloadThread loop quitting by request");
Looper.myLooper().quit();
}
});
}
public synchronized void enqueueDownload(final DownloadTask task) {
// Wrap DownloadTask into another Runnable to track the statistics
handler.post(new Runnable() {
#Override
public void run() {
try {
task.run();
} finally {
// register task completion
synchronized (DownloadThread.this) {
totalCompleted++;
}
// tell the listener something has happened
signalUpdate();
}
}
});
totalQueued++;
// tell the listeners the queue is now longer
signalUpdate();
}
public synchronized int getTotalQueued() {
return totalQueued;
}
public synchronized int getTotalCompleted() {
return totalCompleted;
}
// Please note! This method will normally be called from the download
// thread.
// Thus, it is up for the listener to deal with that (in case it is a UI
// component,
// it has to execute the signal handling code in the UI thread using Handler
// - see
// DownloadQueueActivity for example).
private void signalUpdate() {
if (listener != null) {
listener.handleDownloadThreadUpdate();
}
}
}
Steps 4: Now add full functionality to MainActivity.java . Here also we will create handler so that we can post events on main thread.
package com.example.looper;
import java.util.Random;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Vibrator;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity implements DownloadThreadListener,
OnClickListener {
private DownloadThread downloadThread;
private Handler handler;
private ProgressBar progressBar;
private TextView statusText;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create and launch the download thread
downloadThread = new DownloadThread(this);
downloadThread.start();
// Create the Handler. It will implicitly bind to the Looper
// that is internally created for this thread (since it is the UI
// thread)
handler = new Handler();
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
statusText = (TextView) findViewById(R.id.status_text);
Button scheduleButton = (Button) findViewById(R.id.schedule_button);
scheduleButton.setOnClickListener(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
// request the thread to stop
downloadThread.requestStop();
}
// note! this might be called from another thread
#Override
public void handleDownloadThreadUpdate() {
// we want to modify the progress bar so we need to do it from the UI
// thread
// how can we make sure the code runs in the UI thread? use the handler!
handler.post(new Runnable() {
#Override
public void run() {
int total = downloadThread.getTotalQueued();
int completed = downloadThread.getTotalCompleted();
progressBar.setMax(total);
progressBar.setProgress(0); // need to do it due to a
// ProgressBar bug
progressBar.setProgress(completed);
statusText.setText(String.format("Downloaded %d/%d", completed,
total));
// vibrate for fun
if (completed == total) {
((Vibrator) getSystemService(VIBRATOR_SERVICE))
.vibrate(100);
}
}
});
}
#Override
public void onClick(View source) {
if (source.getId() == R.id.schedule_button) {
int totalTasks = new Random().nextInt(3) + 1;
for (int i = 0; i < totalTasks; ++i) {
downloadThread.enqueueDownload(new DownloadTask());
}
}
}
}
Steps 5: Add Vibrate permission
<uses-permission android:name="android.permission.VIBRATE" />
Original Post Complete tutorial here.
I have two fragments (left and right) and getting in the left fragment a list of Radiostreams. By clicking on one of these streams, the right fragment should change the Name of the Stream and start playing the stream with the given uri.
2 Problems:
Some of the radio streams aren't up to date, so a lot of them aren't working anymore. The problem is, this causes my app to do a forceclose! I did error handling, but after calling such a stream I get:
03-20 14:23:28.192: A/libc(1021): Fatal signal 11 (SIGSEGV) at
0x00000000 (code=1)
03-20 14:23:28.192: W/AudioSystem(1021): AudioFlinger server died!
03-20 14:23:28.192: W/IMediaDeathNotifier(1021): media server died
03-20 14:23:28.192: E/MediaPlayer(1021): error (100, 0)
03-20 14:23:28.192: I/ServiceManager(1021): Waiting for service
media.audio_flinger...
03-20 14:23:28.752: I/dalvikvm(1021): threadid=3: reacting to signal 3
03-20 14:23:28.782: I/dalvikvm(1021): Wrote stack traces to
'/data/anr/traces.txt'
03-20 14:23:29.192: I/ServiceManager(1021): Waiting for service
media.audio_flinger...
I don't know why. Is there any other way to do error handling? Or is there a way to check all the streams before calling mediaPlayer.setDataSource(uri) to avoid preparing defekt uris? (see my code at the end)
I'm controlling the left ListFragment with a remote control. When I try to switch very fast from one channel to the other everything is very laggy. It seems that the reinstanciation of the Mediaplayer take very long. When I don't reinstanciate I get an runtimeerror when I call mediaPlayer.setDataSource(..) again. Is there a way to call .setDataSource two times on one MediaPlayer Object?
Here is my code:
My MediaPlayer Wrapper class:
package net.smart4life.tvplay.model;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.util.Log;
/**
* A wrapper class for {#link android.media.MediaPlayer}.
* <p>
* Encapsulates an instance of MediaPlayer, and makes a record of its internal
* state accessible via a {#link MediaPlayerWrapper#getState()} accessor.
*/
public class MediaPlayerStateWrapper {
private static String tag = "MediaPlayerWrapper";
private MediaPlayer mPlayer;
private State currentState;
private MediaPlayerStateWrapper mWrapper;
public MediaPlayerStateWrapper() {
mWrapper = this;
mPlayer = new MediaPlayer();
currentState = State.IDLE;
mPlayer.setOnPreparedListener(mOnPreparedListener);
mPlayer.setOnCompletionListener(mOnCompletionListener);
mPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
mPlayer.setOnErrorListener(mOnErrorListener);
mPlayer.setOnInfoListener(mOnInfoListener);
}
/* METHOD WRAPPING FOR STATE CHANGES */
public static enum State {
IDLE, ERROR, INITIALIZED, PREPARING, PREPARED, STARTED, STOPPED, PLAYBACK_COMPLETE, PAUSED;
}
public void setDataSource(String path) {
if (currentState == State.IDLE) {
try {
mPlayer.setDataSource(path);
currentState = State.INITIALIZED;
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} else
throw new RuntimeException();
}
public void prepareAsync() {
Log.d(tag, "prepareAsync()");
if (EnumSet.of(State.INITIALIZED, State.STOPPED).contains(currentState)) {
mPlayer.prepareAsync();
currentState = State.PREPARING;
} else
throw new RuntimeException();
}
public boolean isPlaying() {
Log.d(tag, "isPlaying()");
if (currentState != State.ERROR) {
return mPlayer.isPlaying();
} else
throw new RuntimeException();
}
public void seekTo(int msec) {
Log.d(tag, "seekTo()");
if (EnumSet.of(State.PREPARED, State.STARTED, State.PAUSED,
State.PLAYBACK_COMPLETE).contains(currentState)) {
mPlayer.seekTo(msec);
} else
throw new RuntimeException();
}
public void pause() {
Log.d(tag, "pause()");
if (EnumSet.of(State.STARTED, State.PAUSED).contains(currentState)) {
mPlayer.pause();
currentState = State.PAUSED;
} else
throw new RuntimeException();
}
public void start() {
Log.d(tag, "start()");
if (EnumSet.of(State.PREPARED, State.STARTED, State.PAUSED,
State.PLAYBACK_COMPLETE).contains(currentState)) {
mPlayer.start();
currentState = State.STARTED;
} else
throw new RuntimeException();
}
public void stop() {
Log.d(tag, "stop()");
if (EnumSet.of(State.PREPARED, State.STARTED, State.STOPPED,
State.PAUSED, State.PLAYBACK_COMPLETE).contains(currentState)) {
mPlayer.stop();
currentState = State.STOPPED;
} else
throw new RuntimeException();
}
public void reset() {
Log.d(tag, "reset()");
mPlayer.reset();
currentState = State.IDLE;
}
/**
* #return The current state of the mediaplayer state machine.
*/
public State getState() {
Log.d(tag, "getState()");
return currentState;
}
public void release() {
Log.d(tag, "release()");
mPlayer.release();
}
/* INTERNAL LISTENERS */
private OnPreparedListener mOnPreparedListener = new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
Log.d(tag, "on prepared");
currentState = State.PREPARED;
mWrapper.onPrepared(mp);
mPlayer.start();
currentState = State.STARTED;
}
};
private OnCompletionListener mOnCompletionListener = new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Log.d(tag, "on completion");
currentState = State.PLAYBACK_COMPLETE;
mWrapper.onCompletion(mp);
}
};
private OnBufferingUpdateListener mOnBufferingUpdateListener = new OnBufferingUpdateListener() {
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
Log.d(tag, "on buffering update");
mWrapper.onBufferingUpdate(mp, percent);
}
};
private OnErrorListener mOnErrorListener = new OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.d(tag, "on error");
currentState = State.ERROR;
mWrapper.onError(mp, what, extra);
return false;
}
};
private OnInfoListener mOnInfoListener = new OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
Log.d(tag, "on info");
mWrapper.onInfo(mp, what, extra);
return false;
}
};
/* EXTERNAL STUBS TO OVERRIDE */
public void onPrepared(MediaPlayer mp) {
}
public void onCompletion(MediaPlayer mp) {
}
public void onBufferingUpdate(MediaPlayer mp, int percent) {
}
boolean onError(MediaPlayer mp, int what, int extra) {
// Error Handling of type: "MEdiaPlayer error(100,0)
mp.stop();
mp.release();
return false;
}
public boolean onInfo(MediaPlayer mp, int what, int extra) {
return false;
}
/* OTHER STUFF */
public int getCurrentPosition() {
if (currentState != State.ERROR) {
return mPlayer.getCurrentPosition();
} else {
return 0;
}
}
public int getDuration() {
// Prepared, Started, Paused, Stopped, PlaybackCompleted
if (EnumSet.of(State.PREPARED, State.STARTED, State.PAUSED,
State.STOPPED, State.PLAYBACK_COMPLETE).contains(currentState)) {
return mPlayer.getDuration();
} else {
return 100;
}
}
}
Here is my TestFragment (right Fragment). Note: the left Fragment is calling the method "newChannel(radioChannel)" from TestFragment, everytime a listitem was clicked.
package net.smart4life.tvplay.fragment;
import java.io.IOException;
import net.smart4life.tvplay.R;
import net.smart4life.tvplay.model.MediaPlayerStateWrapper;
import net.smart4life.tvplay.model.RadioChannel;
import android.app.Fragment;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
public class TestFragment extends Fragment {
private RadioChannel radioCh;
private TextView tv_RadioCh;
private MediaPlayerStateWrapper mediaWrapper;
private View view;
// firstcall
public TestFragment(RadioChannel radioChannel) {
this.radioCh = radioChannel;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
tv_RadioCh = (TextView) view.findViewById(R.id.radioText);
mediaWrapper = new MediaPlayerStateWrapper();
newChannel(radioCh);
}
public void newChannel (RadioChannel radioChannel) {
this.radioCh = radioChannel;
Log.e("RadioChannel", radioCh.getName());
tv_RadioCh.setText(radioCh.getName());
if(mediaWrapper.isPlaying()) {
mediaWrapper.stop();
mediaWrapper.reset();
} else if(mediaWrapper.getState() == MediaPlayerStateWrapper.State.PREPARING) {
mediaWrapper.release();
mediaWrapper = new MediaPlayerStateWrapper();
}
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_radio_player, container,
false);
return view;
}
#Override
public void onDetach() {
super.onDetach();
mediaWrapper.release();
}
}
Pros, could you please help me with one or both questions?
If a stream can't load you're often stucked in the prepare state, you can try this here, when mediaWrapper.getState() == MediaPlayerStateWrapper.State.ERROR:
mediaWrapper.reset();
mediaWrapper.release();
System.gc();
mediaWrapper = new MediaPlayerStateWrapper();
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
Best to put it in an AsyncTask, to avoid Not responding error. Or when you get an Error you have to create a new MediaPlayer, because Media Server died:
if(mediaWrapper.getState() == MediaPlayerStateWrapper.State.ERROR){
mediaWrapper = new MediaPlayerStateWrapper();
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
}
If the MediaPlayer is playing a stream you have to stop and reset it first :
mediaWrapper.stop();
mediaWrapper.reset();
mediaWrapper.setDataSource(radioCh.getUrl().toString());
mediaWrapper.prepareAsync();
It's working for me but i think it isn't the best way. Hope someone can find a better solution for what to do, when you're stucked in the prepare state.
Regarding the audioflinger service error, as you have noticed, it is marked by "what == 100" or error(100,0).
What you can do to avoid audioflinger error from my humble experience:
Avoid fast calls to the service (I do add like 500 millis delay after creating the player)
Limit the number of concurrent Mediaplayers active at the same time.
What you can do to handle audioflinger error:
Detect the audioflinger error 100, set a flag it occurred and disable GUI (releasing the player only is recommended, as stopping it when it already is in error state is not safe and will throw IllegalStateException & error(38,0)
Start another thread that keeps testing the service is back (could be by creating a new mediaplayer with no exceptions) with a timeout of let's say 5-10 seconds
When the Service is back reset the flag and re-enable GUI
So referring to your code:
boolean onError(MediaPlayer mp, int what, int extra) {
// Error Handling of type: "MEdiaPlayer error(100,0)
mp.release();
// here you add logic communicating the wrapper or main UI thread
// to disable GUI and set a flag
return false;
}
Then you add a method to handle this at the wrapper.
I would be really grateful when you work this out and post a solution. I too am facing a very similar problem.
I know this question has been asked many times before and might seem to be a conglomeration of several questions, but I feel that it is relevant and important to many developers; I need to create a background music Service that can run across multiple activities for my Android game that ends when the application is terminated and pauses in all of the following circumstances:
A certain Activity that has its own music is started. (Resume when this Activity finishes. This happens to be an AndEngine activity.)
The home screen is pressed and the app is backgrounded, or the application is terminated. Resumes when the app returns to the foreground. Requires use of onUserLeaveHint(). Another helpful link.
The phone receives a call and interrupts the app. Resumes when the call has been dealt with. Requires use of TelephonyManager similar to this.
The screen is locked. (Resumes after screen has been unlocked.) Requires use of ACTION_USER_PRESENT, which seems to be very problematic.
Basically the music pauses whenever the app is not being shown or when the special activity from #1 is being shown to the user.
Above is all of what I need and the information I have pieced together. My current code basically resembles this.
I find it curious that AndEngine manages to have none of these issues with their music, so maybe looking in the source code would help someone looking for an answer. I'm using the last functional GLES1 version from Google Code.
I have taken a look at the following links as well on creating a good music Service:
Stopping Background Service Music
http://www.codeproject.com/Articles/258176/Adding-Background-Music-to-Android-App
Android background music service
Playing BG Music Across Activities in Android
http://www.rbgrn.net/content/307-light-racer-20-days-61-64-completion
I would like the solution Service to:
Minimize the use of BroadcastReceivers and Android Manifest additions/permissions if possible
Self contained and error checking
Other Notes
Currently all the activities that require the background music all extend a common special class.
The music needs to loop but only runs a single track.
Thanks to everyone ahead of time! Best of luck!
Edit - Here are code snippets, feel free to improve or ignore:
Media Player Wrapper
import android.content.SharedPreferences;
import android.media.MediaPlayer;
import android.preference.PreferenceManager;
import android.util.Log;
public class CarefulMediaPlayer {
final SharedPreferences sp;
final MediaPlayer mp;
private boolean isPlaying = false;
public CarefulMediaPlayer(final MediaPlayer mp, final MusicService ms) {
sp = PreferenceManager.getDefaultSharedPreferences(ms.getApplicationContext());
this.mp = mp;
}
public void start() {
if (sp.getBoolean("com.embed.candy.music", true) && !isPlaying) {
mp.start();
isPlaying = true;
}
}
public void pause() {
if (isPlaying) {
mp.pause();
isPlaying = false;
}
}
public void stop() {
isPlaying = false;
try {
mp.stop();
mp.release();
} catch (final Exception e) {}
}
}
Music Service
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
public class MusicService extends Service {
static CarefulMediaPlayer mPlayer = null;
#Override
public IBinder onBind(final Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
final MediaPlayer mp = MediaPlayer.create(this, R.raw.title_music);
mp.setLooping(true);
mPlayer = new CarefulMediaPlayer(mp,this);
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
mPlayer.start();
return 1;
}
#Override
public void onStart(final Intent intent, final int startId) {
}
public IBinder onUnBind(final Intent arg0) {
return null;
}
public static void onStop() {
mPlayer.stop();
}
public static void onPause() {
if (mPlayer!=null) {
mPlayer.pause();
}
}
public static void onResume() {
if (mPlayer!=null) {
mPlayer.start();
}
}
#Override
public void onDestroy() {
mPlayer.stop();
mPlayer = null;
}
#Override
public void onLowMemory() {
}
}
Improved Base Activity Class
import android.app.Activity;
import android.content.Intent;
import android.os.PowerManager;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
public abstract class BetterActivity extends Activity {
private boolean isHome = true;
#Override
protected void onResume() {
System.gc();
super.onResume();
MusicService.onResume();
isHome = true;
}
#Override
protected void onPause() {
if (((TelephonyManager)getSystemService(TELEPHONY_SERVICE)).getCallState()==TelephonyManager.CALL_STATE_RINGING
|| !((PowerManager)getSystemService(POWER_SERVICE)).isScreenOn()) {
MusicService.onPause();
}
super.onPause();
System.gc();
}
#Override
public boolean onKeyDown (final int keyCode, final KeyEvent ke) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
isHome = false;
default:
return super.onKeyDown(keyCode, ke);
}
}
#Override
public void startActivity(final Intent i) {
isHome = false;
super.startActivity(i);
}
#Override
protected void onUserLeaveHint() {
if (isHome) {
MusicService.onPause();
}
super.onUserLeaveHint();
}
}
First here is some code. Below I'll give you an explanation.
public class MusicService extends Service {
// service binder
private final IBinder mBinder = new LocalBinder();
// music player controling game music
private static CarefulMediaPlayer mPlayer = null;
#Override
public void onCreate() {
// load music file and create player
MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.title_music);
mediaPlayer.setLooping(true);
mPlayer = new CarefulMediaPlayer(mediaPlayer, this);
}
#Override
public void onDestroy() {
super.onDestroy();
}
// =========================
// Player methods
// =========================
public void musicStart() {
mPlayer.start();
}
public void musicStop() {
mPlayer.stop();
}
public void musicPause() {
mPlayer.pause();
}
/**
* Class for clients to access. Because we know this service always runs in
* the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
MusicService getService() {
return MusicService.this;
}
}
#Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
}
Activity:
public class StartupActivity extends Activity {
// bounded service
private static MusicService mBoundService;
// whetere service is bounded or not
private boolean mIsBound;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_startup);
doBindService();
// HOW TO WORK WITH THE SERVICE:
// call the following methods whenever
// you want to interact with you
// music player
// ===================================
// call this e.g. in onPause() of your Activities
StartupActivity.getService().musicPause();
// call this e.g. in onStop() of your Activities
StartupActivity.getService().musicStop();
// call this e.g. in onResume() of your Activities
StartupActivity.getService().musicStart();
}
#Override
public void onDestroy() {
super.onDestroy();
doUnbindService();
}
private final ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
setService(((MusicService.LocalBinder) service).getService());
}
#Override
public void onServiceDisconnected(ComponentName className) {
setService(null);
}
};
private void doBindService() {
Intent service = new Intent(getBaseContext(), MusicService.class);
// start service and bound it
startService(service);
bindService(new Intent(this, MusicService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
private void doUnbindService() {
if (mIsBound) {
// Detach existing connection.
unbindService(mServiceConnection);
mIsBound = false;
}
}
public static MusicService getService() {
return mBoundService;
}
private static void setService(MusicService mBoundService) {
StartupActivity.mBoundService = mBoundService;
}
}
First of all you got a Service which runs in background. This service creates the mediaPlayer object as you did. With the localBinder you can bind the Service in your Activity(ies) and access it like a normal Java-Object.
The Activity I've posted bindes the Service. In it's onCreate() method you can find a way how to interact with your mediaPlayer.
You can bind any Activity to your Service.
Another Solution:
public class CarefulMediaPlayer {
final SharedPreferences sp;
final MediaPlayer mp;
private boolean isPlaying = false;
private static CarefulMediaPlayer instance;
public CarefulMediaPlayer(final MediaPlayer mp, final MusicService ms) {
sp = PreferenceManager.getDefaultSharedPreferences(ms.getApplicationContext());
this.mp = mp;
instance = this;
}
public static CarefulMediaPlayer getInstance() {
return instance;
}
public void start() {
if (sp.getBoolean("com.embed.candy.music", true) && !isPlaying) {
mp.start();
isPlaying = true;
}
}
public void pause() {
if (isPlaying) {
mp.pause();
isPlaying = false;
}
}
public void stop() {
isPlaying = false;
try {
mp.stop();
mp.release();
} catch (final Exception e) {}
}
}
Then you can pause, play and stop the music by calling CarefulMediaPlayer.getInstance().play();
I did it this way and I'm pleased with the result:
1st create the service:
public class LocalService extends Service
{
// This is the object that receives interactions from clients. See RemoteService for a more complete example.
private final IBinder mBinder = new LocalBinder();
private MediaPlayer player;
/**
* Class for clients to access. Because we know this service always runs in
* the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder
{
LocalService getService()
{
return LocalService.this;
}
}
#Override
public void onCreate()
{
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// We want this service to continue running until it is explicitly stopped, so return sticky.
return START_STICKY;
}
#Override
public void onDestroy()
{
destroy();
}
#Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
public void play(int res)
{
try
{
player = MediaPlayer.create(this, res);
player.setLooping(true);
player.setVolume(0.1f, 0.1f);
player.start();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public void pause()
{
if(null != player && player.isPlaying())
{
player.pause();
player.seekTo(0);
}
}
public void resume()
{
try
{
if(null != player && !player.isPlaying())
{
player.start();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
public void destroy()
{
if(null != player)
{
if(player.isPlaying())
{
player.stop();
}
player.release();
player = null;
}
}
}
2nd, create a base activity and extend all your activities in witch you wish to play the background music from it:
public class ActivityBase extends Activity
{
private Context context = ActivityBase.this;
private final int [] background_sound = { R.raw.azilum_2, R.raw.bg_sound_5 };
private LocalService mBoundService;
private boolean mIsBound = false;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
doBindService();
}
#Override
protected void onStart()
{
super.onStart();
try
{
if(null != mBoundService)
{
Random rand = new Random();
int what = background_sound[rand.nextInt(background_sound.length)];
mBoundService.play(what);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
#Override
protected void onStop()
{
super.onStop();
basePause();
}
protected void baseResume()
{
try
{
if(null != mBoundService)
{
mBoundService.resume();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
protected void basePause()
{
try
{
if(null != mBoundService)
{
mBoundService.pause();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
private ServiceConnection mConnection = new ServiceConnection()
{
public void onServiceConnected(ComponentName className, IBinder service)
{
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
mBoundService = ((LocalService.LocalBinder) service).getService();
if(null != mBoundService)
{
Random rand = new Random();
int what = background_sound[rand.nextInt(background_sound.length)];
mBoundService.play(what);
}
}
public void onServiceDisconnected(ComponentName className)
{
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
mBoundService = null;
if(null != mBoundService)
{
mBoundService.destroy();
}
}
};
private void doBindService()
{
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
Intent i = new Intent(getApplicationContext(), LocalService.class);
bindService(i, mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
private void doUnbindService()
{
if (mIsBound)
{
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
#Override
protected void onDestroy()
{
super.onDestroy();
doUnbindService();
}
}
And that's it, now you have background sound in all the activities that are extended from ActivityBase.
You can even control the pause / resume functionality by calling basePause() / baseResume().
Don't forget to declare the service in manifest:
<service android:name="com.gga.screaming.speech.LocalService" />
In the startup activity we are binding and Starting Service seperately. This is wrong since service will keep running after activity exits as we haven't called stopService() anywhere. So The part ' startService(service) ' should be removed as bind service is already "Auto-Creating" the service too.
Please correct me if anyone got opposite results
startService(service);// remove this part
bindService(new Intent(this, MusicService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
Am working on certain app XYZ. In that app having a audio player module. I launch a player launch from an activity using some following code:
playButton = (ImageButton) findViewById(R.id.play_image_button);
playButton.refreshDrawableState();
playButton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
if (playCount % 2 == 0) {
if (Data.songProfileArrayList.size() > 0) {
SongProfileBean songData = Data.songProfileArrayList
.get(0);
if ("audio".equals(songData.getSongFileType())) {
playButton
.setBackgroundResource(R.drawable.pause_button);
playBackgroundMP3(Data.URL_BASE
+ songData.getSongName());
} else if ("video".equals(songData.getSongFileType())) {
Intent intent = new Intent(mContext,
VedioViewActivity.class);
intent.putExtra("VideoUrl", Data.URL_BASE
+ songData.getSongName());
startActivity(intent);
}
}
} else {
// stopPlay();
pauseSong();
// playerLayout.setVisibility(View.INVISIBLE);
// playButton.setImageDrawable(getResources().getDrawable(R.drawable.play_button));
playButton.setBackgroundResource(R.drawable.play_button);
}
playCount++;
}
});
To explain more about the code:
Currently I'm using an same Image Button on click on that the audio song starts from Data.songProfileArrayList an Array list and on another click of same button the song is stoped.
Now My Requirement or gist of question:
Actually I'm looking for following:
1.Is there any things available to also get the progress state of audio . I have gone through 1.developer docs 2.android docs on media
If yes Also suggest the way to have that status bar on each activity of application.
2.Should be able to progress the audio ,start,stop from each activity.
Please suggest a valid answer only after going through all details mentioned.
Only suggest answer If you have a deep knowledge over that and have done earlier so far
Valid Help and suggestions are most welcome
Regards,
Arpit
Hello After R & D over the problem I am able to get default controllers and progress state .But still want this to be on each activity and should be custom Controllers
package com.APP_NAME_XYZ.app;
import java.io.IOException;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.MediaController;
import android.widget.TextView;
public class AudioPlayer extends Activity implements OnPreparedListener,
MediaController.MediaPlayerControl {
private static final String TAG = "AudioPlayer";
public static final String AUDIO_FILE_NAME = "audioFileName";
private MediaPlayer mediaPlayer;
private MediaController mediaController;
private String audioFile;
private Handler handler = new Handler();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_player);
// audioFile = this.getIntent().getStringExtra(AUDIO_FILE_NAME);
audioFile = "Provide URL of song";
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnPreparedListener(this);
mediaController = new MediaController(this);
try {
mediaPlayer.setDataSource(audioFile);
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
Log.e(TAG, "Could not open file " + audioFile + " for playback.", e);
}
}
#Override
protected void onStop() {
super.onStop();
mediaPlayer.stop();
mediaPlayer.release();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// the MediaController will hide after 3 seconds - tap the screen to
// make it appear again
mediaController.show();
return false;
}
// --MediaPlayerControl
// methods----------------------------------------------------
public void start() {
mediaPlayer.start();
}
public void pause() {
mediaPlayer.pause();
}
public int getDuration() {
return mediaPlayer.getDuration();
}
public int getCurrentPosition() {
return mediaPlayer.getCurrentPosition();
}
public void seekTo(int i) {
mediaPlayer.seekTo(i);
}
public boolean isPlaying() {
return mediaPlayer.isPlaying();
}
public int getBufferPercentage() {
return 0;
}
public boolean canPause() {
return true;
}
public boolean canSeekBackward() {
return true;
}
public boolean canSeekForward() {
return true;
}
// --------------------------------------------------------------------------------
public void onPrepared(MediaPlayer mediaPlayer) {
Log.d(TAG, "onPrepared");
mediaController.setMediaPlayer(this);
mediaController.setAnchorView(findViewById(R.id.main_audio_view));
handler.post(new Runnable() {
public void run() {
mediaController.setEnabled(true);
mediaController.show();
}
});
}
}
The audio_player layout is as follows:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main_audio_view" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:orientation="vertical">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_gravity="center"
android:text="Now playing:" android:textSize="25sp" android:textStyle="bold" />
<TextView android:id="#+id/now_playing_text"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dip" android:layout_marginLeft="10dip"
android:layout_marginRight="10dip" android:layout_gravity="center"
android:text="Now playing.." android:textSize="16sp"
android:textStyle="italic" />
</LinearLayout>
You will need to change your approach. Use fragments and have one fragment show the progressbar and remain visible throughout. You can have just one activity and change the UI using fragments depending on different states of the application. When the application goes into background use Notification bar to show the media progress bar and allow user to relaunch the application from there.
Please refer how google music app works and follow the recommended approach.
This is a pretty strange problem. I have a book that I have been going through (The Android Developer's Cookbook)
I am making a pretty basic app (at least, I think it is basic)
The problem I am having is this. When the service I create is stopped, the activity does the service's work.
I want to monitor the Z Axis reading on my phone (in the background, always)
Here is my project structure:
TiltMonitorActivity
TiltMonitorService
From TiltMonitorActivity:
#Override
public void onClick(View v) {
boolean error = false;
if (tbService.isChecked()) {
try {
startService(backgroundService);
}
catch (Exception e) {
error = true;
}
finally {
if (error)
{
errorToast();
}
}
}
else if (!tbService.isChecked()) {
try {
stopService(backgroundService);
}
catch (Exception e){
error = true;
}
finally {
if (error)
{
errorToast();
}
}
}
}
});
I also put this in onBackPressed(), onPause, onDestroy
finish();
The intent I create when starting/stopping service
final Intent backgroundService = new Intent(this, TiltMonitorService.class);
From TiltMonitorService:
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.IBinder;
import android.os.Vibrator;
import android.widget.Toast;
public class TiltMonitorService extends Service {
private SensorManager sensorManager = null;
private Vibrator vibrator = null;
private float[] accData = new float[3];
// These are for manual config
private float FORWARD_THRESHOLD = 5f;
private float BACKWARD_THRESHOLD = -5f;
// These are for auto config
// The phone will face forward, so a positive Z Axis means user is leaning backwards
// and that a negative Z Axis the user is leaning forward
private float AUTO_FORWARD_THRESHOLD = -1.5f;
private float AUTO_BACKWARD_THRESHOLD = 3.5f;
public static TiltMonitorActivity MAIN_ACTIVITY = null;
public static void setMainActivity(TiltMonitorActivity activity)
{
MAIN_ACTIVITY = activity;
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
super.onCreate();
sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
sensorManager.registerListener( tiltListener,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_NORMAL);
Toast.makeText(this, "Service created", Toast.LENGTH_LONG).show();
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Service destroyed", Toast.LENGTH_LONG).show();
}
private SensorEventListener tiltListener = new SensorEventListener() {
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
#Override
public void onSensorChanged(SensorEvent event) {
accData[0] = event.values[0];
accData[1] = event.values[1];
accData[2] = event.values[2];
checkData(accData);
}
};
private void checkData(float[] accData)
{
if ((accData[2] < AUTO_FORWARD_THRESHOLD) || (accData[2] > AUTO_BACKWARD_THRESHOLD))
{
vibrator.vibrate(100);
}
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
}
Again, I get the Toast that the service has been started and stopped when hitting the ToggleButton in the activity.
But the task still happens. (Vibrates when below or above set thresholds)
When I open up a task killer, the service isn't running. Only the activity is (and killing it kills the vibration)
I'm not sure what words to search for, I couldn't find any one else with the same problem. I tried to post only relevant code in the Activity to avoid clutter. The service is posted in its entirety. If more is needed, I will put it up promptly.
Thanks you guys for any insight given
at a guess, because you register the listener in onCreate without unregistering it in onDestroy
in the service, that is.