How to destroy the a media player from a fragment? - java

I am a solo developer so I am still learning. I am trying to use Fragments for a game's menu. In the menu there is an option to preview and set background music. So in the MusicFragment I have this code:
public class MusicFragment extends Fragment {
Music music = new Music();
// more code //
private void previewSong() {
music.playSong(getActivity(), "Song_1", milliseconds)
}
private void stopSong() {
music.stopPlayer();
}
}
I have a separate class - "Music" for the music, the code is like so:
public class Music extends Activity {
MediaPlayer player;
public void playSong(Context context, String songName, int milliseconds) {
if(player == null) {
player = MediaPlayer.create(context, R.raw.song1);
player.seekTo(milliseconds);
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
}
});
}
player.start();
}
public void stopPlayer() {
if(player !=null) {
player.stop();
player.reset();
player.release();
player = null;
}
}
public String playerStatus() {
if(player == null) {
return "player is null";
} else {
return "player is NOT null";
}
}
}
I can start and stop the player from the fragment with no problems. What I am trying to solve for is the occasion where the user previews the music and then closes/minimizes the app - without turning the music off (stopPlayer()) in the MusicFragment (the music still playing).
In that case the app closes but the music is still ongoing. My understanding is that fragments (in this case MusicFragment) do not have onPause(). The Main_Activity does have onPause - but when I read the MediaPlayer's status via the Main_Activity's onPause() it displays the playerStatus as "NULL" even though song1 is still playing on the device while the app is closed.
I've tried to exclude the additional Music class and just create the MediaPlayer in the MusicFragment but the result is the same. Obviously if the Fragment had an OnPause I would call the stopSong() method from there. I think I'm not understanding a fundamental concept. Any assist would be greatly appreciated.

You can make the stopSong() method in MusicFragment public. Create an instance of MusicFragment in your activity and then call the stopSong() method.
public class MusicFragment extends Fragment {
Music music = new Music();
// more code //
private void previewSong() {
music.playSong(getActivity(), "Song_1", milliseconds)
}
public void stopSong() {
music.stopPlayer();
}
}
In your activity:
public class MainActivity extends appCompatActivity{
MusicFragment musicFragment;
...
#Override
protected void onPause() {
super.onStop();
musicFragment=new MusicFragment();
musicFragment.stopSong();
}
}

Related

Adding music in android studio (java, MediaPlayer)

buttonMusic = findViewById(R.id.buttonMus);
musicSound = MediaPlayer.create(this, R.raw.music);
buttonClick();
}
private Button buttonMusic;
private MediaPlayer musicSound;
public void buttonClick() {
buttonMusic.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
soundPlay(musicSound);
}
}
);
}
public void soundPlay(MediaPlayer sound) {
if (sound.isPlaying()) {
sound.stop();
}else {
sound.start();
sound.setLooping(true);
} }
Hello.
The code launches the music, is able to stop it, but it wont play again after pressing the play button, after pausing the song that is.
you aren't pausing song, you are stop()ing it. use sound.pause() for pausing :)
after stop() MediaPlayer you have to prepare it again, use prepare() or prepareAsync(). MediaPlayer.create( makes that for you at the beginning, also if you would create MediaPlayer using its constructor you would also want to prepare()/preapreAsync() before calling start() in onClick
check out state diagram of MediaPlayer

ExoPlayer not starting the video from a specific position (android studio) [duplicate]

I'm using ExoPlayer to play some mp4's from a URL. When the user click's the home button, or anything that causes the app to be removed from the user's view and then comes back to my app (and video activity) I want the video to resume where it left off. I tried implementing this by saving the video position in onStop() and then rebuilding the player and using seekTo() in onStart(). I have a check to see if my current exoplayer is null in onStart(), this check never passes however, so I think this is where the problem lies. How it's coded now my video never resumes. If I leave the app by pressing the home button onStop() gets called and then onStart() will get called when I go back into my app, however the video player remains black and never plays the video. If I remove the null check I get two instances of the video playing whenever I start a video from the main activity because it gets called both in onCreate() and onStart(). Is there a better method for getting the functionality that I want? Any help is appreciated!
public class VideoActivity extends AppCompatActivity {
private SimpleExoPlayer exoPlayer;
private SimpleExoPlayerView simpleExoPlayerView;
private long playerPosition;
private String mp4Url;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.player_activity);
// Get the Intent that started this activity and extract the video url
Intent intent = getIntent();
mp4Url = intent.getStringExtra(MainActivity.VIDEO_URL);
// Create an exoplayer instance and start playing video
buildPlayer(mp4Url);
}
private void buildPlayer(String mp4Url) {
// Create a default TrackSelector
Handler mainHandler = new Handler();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
// Create the player
exoPlayer = ExoPlayerFactory.newSimpleInstance(this, trackSelector); // no LoadControl?
simpleExoPlayerView = new SimpleExoPlayerView(this);
simpleExoPlayerView = (SimpleExoPlayerView) findViewById(R.id.player_view);
// Set media controller
simpleExoPlayerView.setUseController(true);
simpleExoPlayerView.requestFocus();
// Bind player to the view
simpleExoPlayerView.setPlayer(exoPlayer);
// Create Uri from video location
// TODO: should this be in some network class? Should I be appending APIKEY here?
Uri mp4Uri = Uri.parse(mp4Url + "?api_key=" + BuildConfig.GIANTBOMB_API_KEY);
Timber.v("Video url with api key: " + mp4Uri.toString());
// Create another bandwidth meter for bandwidth during playback (not strictly necessary)
DefaultBandwidthMeter playbackBandwidthMeter = new DefaultBandwidthMeter();
// DataSourceFactory to produce DataSource instances through which media data is loaded
DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(this,
Util.getUserAgent(this, "GiantBombForAndroid"),
playbackBandwidthMeter);
// Produces Extractor instances for parsing the media data
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
ExtractorMediaSource.EventListener eventListener = new ExtractorMediaSource.EventListener() {
#Override
public void onLoadError(IOException error) {
Timber.e("Error loading video from source");
}
};
final MediaSource videoSource = new ExtractorMediaSource(mp4Uri,
dataSourceFactory,
extractorsFactory,
mainHandler,
eventListener);
exoPlayer.prepare(videoSource);
exoPlayer.addListener(new ExoPlayer.EventListener() {
#Override
public void onLoadingChanged(boolean isLoading) {
Timber.v("Listener-onLoadingChanged...");
}
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
Timber.v("Listener-onPlayerStateChanged...");
}
#Override
public void onTimelineChanged(Timeline timeline, Object manifest) {
Timber.v("Listener-onTimelineChanged...");
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
// TODO: Do I need anything here?
}
#Override
public void onPlayerError(ExoPlaybackException error) {
Timber.v("Listener-onPlayerError...");
exoPlayer.stop();
exoPlayer.prepare(videoSource);
exoPlayer.setPlayWhenReady(true);
}
#Override
public void onPositionDiscontinuity() {
Timber.v("Listener-onPositionDiscontinuity...");
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
// TODO: Do I need anything here?
}
});
exoPlayer.setPlayWhenReady(true);
}
#Override
protected void onStart() {
super.onStart();
Timber.v("onStart()...");
if (exoPlayer == null) {
Timber.v("No exoplayer instance, recreating...");
buildPlayer(mp4Url);
exoPlayer.seekTo(playerPosition);
}
}
#Override
protected void onStop(){
super.onStop();
Timber.v("onStop()...");
//TODO: pull player creation code into it's own method so it can be called here as well
playerPosition = exoPlayer.getCurrentPosition();
exoPlayer.release();
}
#Override
protected void onDestroy() {
super.onDestroy();
Timber.v("onDestroy()...");
exoPlayer.release();
}
}
Currently you're calling release in onStop() which will null out all the important pieces of the player, but not the exoPlayer field (and destroy any state that you aren't keeping track of yourself).
There are a few different approaches. But no matter what you do, you'll likely want to keep track of some state yourself. Below I use them as fields, but they could also be placed in onSavedInstanceState(). In onStop() we're saving off two pieces of information and then pulling them out in onStart(). 1) The last position our player was in when pausing and 2) whether we should play when resumed. You can likely move your seekTo call out of the if == null block since you'll probably always want to resume from where you left off.:
#Override
public void onStart() {
// ...
if (exoPlayer == null) {
// init player
}
// Seek to the last position of the player.
exoPlayer.seekTo(mLastPosition);
// Put the player into the last state we were in.
exoPlayer.setPlayWhenReady(mPlayVideoWhenForegrounded);
// ...
}
#Override
public void onStop() {
// ...
// Store off if we were playing so we know if we should start when we're foregrounded again.
mPlayVideoWhenForegrounded = exoPlayer.getPlayWhenReady();
// Store off the last position our player was in before we paused it.
mLastPosition = exoPlayer.getCurrentPosition();
// Pause the player
exoPlayer.setPlayWhenReady(false);
// ...
}
Now the other issue I see with your code sample is that exoPlayer.release() won't null out the field exoPlayer. So you could additionally add the line exoPlayer = null after exoPlayer.release() which should hopefully fix your issue of multiple exoPlayers. You could also move the release() call to onDestroy() but only if you know you're reinstantiating everything correctly.
There is no need to reinit player again. The following code is pretty enough:
#Override
protected void onPause() {
super.onPause();
if (player!=null) {
player.stop();
mLastPosition = player.getCurrentPosition();
}
}
#Override
protected void onResume() {
super.onResume();
//initiatePlayer();
if(mLastPosition!=0 && player!=null){
player.seekTo(mLastPosition);
}
}
Try the following
Get the player position in onPause.
initiate the palyer again in onResume and set seek to the last position of the player
private void initiatePlayer() {
try {
exoPlayer = ExoPlayerFactory.newSimpleInstance(this);
DataSource.Factory dataSourceFactory =
new DefaultDataSourceFactory(this, Util.getUserAgent(this, this.getResources().getString(R.string.app_name)));
DefaultExtractorsFactory extractorsFactory =
new DefaultExtractorsFactory()
.setMp4ExtractorFlags(Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS);
ProgressiveMediaSource progressiveMediaSource =
new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory)
.createMediaSource(videoUri);
// playerView = new PlayerView(this);
playerView.setPlayer(exoPlayer);
exoPlayer.prepare(progressiveMediaSource);
exoPlayer.setPlayWhenReady(true);
PlayerControlView controlView = playerView.findViewById(R.id.exo_controller);
mFullScreenIcon = controlView.findViewById(R.id.exo_fullscreen_icon);
ImageView volumeControl = controlView.findViewById(R.id.exo_volume);
mFullScreenIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(ORIENTATION ==Configuration.ORIENTATION_LANDSCAPE){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
Timer mRestoreOrientation = new Timer();
mRestoreOrientation.schedule(new TimerTask() {
#Override
public void run() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
}, 2000);
}
});
volumeControl.setOnClickListener(view -> {
float currentvolume = exoPlayer.getVolume();
if (currentvolume > 0f) {
previousVolume = currentvolume;
exoPlayer.setVolume(0f);
volumeControl.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_volume_off_white_24dp));
} else {
exoPlayer.setVolume(previousVolume);
volumeControl.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_volume_up_white_24dp));
}
});
} catch (Exception e) {
e.printStackTrace();
Log.d("MainExcep", e.getMessage());
}
}
#Override
protected void onPause() {
super.onPause();
if (exoPlayer!=null) {
exoPlayer.stop();
mLastPosition = exoPlayer.getCurrentPosition();
}
}
#Override
protected void onResume() {
super.onResume();
initiatePlayer();
if(mLastPosition!=0){
exoPlayer.seekTo(mLastPosition);
}
}
In my case, if i minimize or try to move from the this player video app to another app. The video player app always starting play from 0. I was try and succesfully, the video player app playing video from the last current position when you minimalize or move to another app, and this it.
go to
private void buildPlayer(String mp4Url)
add
player.seekTo(playbackPosition);
and then
#Override
public void onResume() {
super.onResume();
if(playbackPosition!=0 && player!=null){
player.seekTo(playbackPosition);
initializePlayer();
}
}
#Override
public void onPause() {
super.onPause();
player.stop();
if(player != null && player.getPlayWhenReady()) {
player.stop();
playbackPosition = player.getCurrentPosition();
player.setPlayWhenReady(true);
}
}
#Override
public void onStop() {
super.onStop();
player.setPlayWhenReady(false);
player.stop();
player.seekTo(0);
}
And you can try remove the
exoPlayer.setPlayWhenReady(true);
from
private void buildPlayer(String mp4Url)

show app lock pattern dialog when unlocking the phone

I have MainActivity and on its onResume method I call pattern lock to create and confirm user identity. User visits and leave this MainActivity back and forth while active on the app as well as when phone is in sleep mode and user unlocks it. These both scenarios will call onRestart, onStart and onResume methods, but I only want to revoke the pattern in unlock scenario.
handlePattern() method needs a proper distinguishing to be called.
How to distinguish this when I call the handlePattern method ?
MainActivity.class
onCreate(){}
onResume(){
//help needed to know that user is just visiting activity in app back and forth
or came back after unlocking the screen.
if(isPatternCallRequired){
handlePattern()
}
}
In your onStop() method call you can check if the player is in sleep mode and cache the boolean.
PowerManager pm = (PowerManager)
_context.getSystemService(Context.POWER_SERVICE);
boolean isInSleepMode = !pm.isScreenOn();
Check for the build version
if( Build.VERSION.SDK_INT >= 20)
// use isInteractive()
else
// use isScreenOn()
in onRestart which will get called when you resume from sleep - based on the cached value you can show the pattern to unlock.
You may need to reset the cached value once you are done using it.
onResume may not be a right API for the call as it will be called even when your activity loads.
Edited answer based on your comment
You can try ActivityLifecycleCallbacks too like this,
First, Register your Application in your Application class.
public class StackApp extends Application {
private static final String TAG = StackApp.class.getSimpleName();
public static final String INTENT_ACTION_APP_STATE_CHANGE = "intent_action_app_state_change";
public static final String INTENT_DATA_IS_IN_BACKGROUND = "intent_data_is_in_background";
private static int mNumRunningActivities = 0;
private static AtomicBoolean mIsAppInForeground = new AtomicBoolean();
#Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= 14) {
// registerActivityLifecycleCallbacks is supported only from the SDK version 14.
registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityStarted(Activity activity) {
mNumRunningActivities++;
if (mNumRunningActivities == 1) {
notifyAppState(false);
Log.i(TAG, "APP IN FOREGROUND");
}
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
mNumRunningActivities--;
if (mNumRunningActivities == 0) {
notifyAppState(true);
}
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
/**
* To notify App state whether its in ForeGround or in Background
*
* #param isInBackground
*/
private void notifyAppState(boolean isInBackground) {
if (isInBackground) {
mIsAppInForeground.set(false);
} else {
mIsAppInForeground.set(true);
}
sendAppStateChangeBroadcast(isInBackground);
}
public static boolean isInForeground() {
return mIsAppInForeground.get();
}
private void sendAppStateChangeBroadcast(boolean isInBackground) {
Log.i(TAG, "sendAppStateChangeBroadcast - isInBackground : " + isInBackground);
Intent intent = new Intent();
intent.setAction(INTENT_ACTION_APP_STATE_CHANGE);
intent.putExtra(INTENT_DATA_IS_IN_BACKGROUND, isInBackground);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
And register the broadcast and listen whether the App is going background or foreground like this Sample Activity example
public class SampleMyActivity extends AppCompatActivity {
private OnAppStateReceiver mAppStateReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample_my);
mAppStateReceiver = new OnAppStateReceiver();
IntentFilter filter = new IntentFilter(StackApp.INTENT_ACTION_APP_STATE_CHANGE);
LocalBroadcastManager.getInstance(this).registerReceiver(mAppStateReceiver, filter);
}
#Override
protected void onDestroy() {
super.onDestroy();
if (mAppStateReceiver != null) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mAppStateReceiver);
}
}
private class OnAppStateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (!TextUtils.isEmpty(action) && StackApp.INTENT_ACTION_APP_STATE_CHANGE.equalsIgnoreCase(action)) {
boolean isGoingBackground = intent.getBooleanExtra(StackApp.INTENT_DATA_IS_IN_BACKGROUND, false);
if (isGoingBackground) {
//Your app is not vissible to the use
} else {
// App is visible to the user.
}
}
}
}
}
Note: If you want to listen in Multiple Activity you can create a base
class and add the listener there and you can do the operation, In that
case you can reduce a lot of code.

how can you stop a service outside of an activity?

I'm making a litle game in android studio where I want background music to play when the user is interacting with the application (and have it turned on in the settings). For this I used a service for playing backgroundmusic. As you can see below
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
public class BackgroundMusicService extends Service {
MediaPlayer player;
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this, R.raw.backgroundmusic);
player.setLooping(true); // Set looping
}
public int onStartCommand(Intent intent, int flags, int startId) {
player.start();
return 1;
}
#Override
public void onDestroy() {
player.stop();
player.release();
}
#Override
public void onLowMemory() {
}
}
I want my backgroundmusic to play on all my activities, but stop when the user leaves the application, as it is impossible to detect a menu button press or a home button press in android. I tried to solve it like this.
import java.util.Timer;
import java.util.TimerTask;
public class LeaveAppDetector extends BackgroundMusicService{
private boolean Deactivated;
private int Time = 700;
private int wait = 0;
private Timer timer = new Timer();
public void Activate() {
Deactivated = false;
timer.schedule(new TimerTask() {
#Override
public void run() {
if (wait >= 1) {
if (!getDeactivate()) {
//app has been closed
StopPlaying();
StopTimer();
} else {StopTimer();} //app has not been closed
}
wait++;
}
}, Time);
}
public void Deactivate() { //call at the start of each activity
Deactivated = true;
}
private boolean getDeactivate() {
return Deactivated;
}
private void StopTimer() {
if (timer != null) {
timer.cancel();
timer.purge();
}
}
}
The idea is that when an activity starts, it calls Deactivate(); and when it closes, it calls Activate(); so that the value of Activated is updated after a short period of time. I added the following method to my BackgrondMusicService class in order to be able to turn it off remotely
public void StopPlaying() {
player.stop();
player.release();
}
Now the problem, it gives me the error that the reference to the mediaplayers in the method I just showed, is one referring to a null object, and I don't know why or why it won't work. Can someone help?
You need to use a Bound Service like this answer suggest:
https://stackoverflow.com/a/2660696/3742122
If you only want the service running while activities are using it, consider getting rid of startService(). Instead, use bindService() in the onStart() methods of the activities that need the service, and call unbindService() in their corresponding onStop() methods. You can use BIND_AUTO_CREATE to have the service be lazy-started when needed, and Android will automatically stop the service after all connections have been unbound.

How do i get a .wav sound to play?

Im making an app, and i want it to make a sound when a activity is opened , the sound file is in R.raw.sound_file , if someone could do some example code to make my app play a sound that would be great.
doesn't the android.media.MediaPlayer class do this?
Reference: http://developer.android.com/reference/android/media/MediaPlayer.html
Example: http://developer.android.com/guide/topics/media/index.html
Step 2 of the example says:
MediaPlayer mp = MediaPlayer.create(context, R.raw.sound_file_1);
mp.start();
In your case, I'd use the onStart() inside your Activity class:
public class MyActivity extends Activity {
...
protected void onStart() {
super.onStart();
MediaPlayer mp = MediaPlayer.create(this, R.raw.sound_file_1);
mp.start();
}
...
}
I have experience using the MediaPlayer object for an android app I created, and I discovered the following:
Wav files have problems in MediaPlayer if they have a bitrate of 32kbps, but wav files of higher bit rates seem to play ok, even if it is a large wav file it will play ok as long as it has the higher bitrate.
If at all possible, use mp3 files for your audio, I encountered no problems whatsoever with mp3 audio files using the MediaPlayer object, so that is the best way to go, using google there are lots of different kinds of mp3 sounds available free from rings and dings, to dog barks, and cat meows, or whatever kind of sound you are looking for.
Try with my code, It's works perfectly. Also you need to have the sound file .wav en res/raw
public class PianoActivity extends Activity {
private MediaPlayer mediaPlayer = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_piano);
setupUI();
}
#Override
protected void onPause() {
super.onPause();
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
private void setupUI() {
findViewById(R.id.doo).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
managerOfSound();
}
});
}
private void managerOfSound() {
mediaPlayer = MediaPlayer.create(this, R.raw.doo);
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
} else {
mediaPlayer.stop();
}
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.reset();
mp.release();
}
});
}
}
I had the same problem.
this worked for me by using the application context as follows:
public class MyActivity extends Activity {
...
protected void onStart() {
super.onStart();
Context appContext = getApplicationContext();
MediaPlayer mp = MediaPlayer.create(appContext , R.raw.sound_file_1);
mp.start();
}
...
}
Also, don't forget to call mp.release() once you're done
Another,preferred option is to use the SoundPool class

Categories

Resources