I made a simple class that handles everything related to sound. Has an add, play, stop, release and releaseAll. How it works is that you have to add a song and then call play passing the name of the song you added. Anytime you need to stop, just call the stop function and pass the song's name as parameter and it should stop. My issue is that it isn't stopping even though it goes through stop().
Sound class:
public class Sound
{
private Map<String, MediaPlayer> songs = new HashMap<String, MediaPlayer>();
private MediaPlayer currentlyPlayingSong;
public Sound() {}
public void Add(int songId, String songName, Context context)
{
MediaPlayer song = MediaPlayer.create(context, songId);
songs.put(songName, song);
}
public void Play(String name, boolean shouldLoop)
{
MediaPlayer songToPlay = songs.get(name);
if ( songToPlay != currentlyPlayingSong && songToPlay != null)
{
currentlyPlayingSong = songToPlay;
currentlyPlayingSong.start();
currentlyPlayingSong.setLooping(shouldLoop);
}
}
public void Stop(String name)
{
MediaPlayer songToStop = songs.get(name);
if (songToStop != null)
{
songToStop.setLooping(false);
songToStop.stop();
}
}
public void Release(String name)
{
songs.get(name).release();
}
public void ReleaseAll()
{
LinkedList<MediaPlayer> _songs;
_songs = (LinkedList)songs.values();
for (int i = 0; i < _songs.size(); i++)
{
_songs.get(i).release();
}
}
}
On the activity's OnCreate I call Add then Play. Everything is fine until I try to call Stop from a fragment. Runs without any errors or exceptions, it simply doesn't stop.
Activity:
public class Main extends ActionBarActivity
{
private Sound sound = new Sound();
private static boolean isSoundOn = true;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
isSoundOn = true;
sound.Add(R.raw.drajamainmenueddited, "mainMenuSong", this);
//endregion
//Hide upper action bar
getSupportActionBar().hide();
if (isSoundOn)
sound.Play("mainMenuSong", true);
}
public void SetIsSoundOn(Boolean isOn)
{
isSoundOn = isOn;
}
public boolean GetIsSoundOn()
{
return isSoundOn;
}
public Sound GetSoundObj()
{
return sound;
}
}
Fragment:
public class MainMenuFragment extends Fragment {
private ImageButton soundImgBtn;
private FragmentConfig fragmentConfig;
public MainMenuFragment()
{
fragmentConfig = new FragmentConfig();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
//region Initiators
View view = inflater.inflate(R.layout.fragment_main_menu, container, false);
soundImgBtn = (ImageButton)view.findViewById(R.id.soundImgBtn);
//endregion
//region Listeners
soundImgBtn.setOnClickListener(
new View.OnClickListener()
{
#Override
public void onClick(View v)
{
SoundImgBtnClick(v);
}
}
);
//endregion
//Changes audio img
if (((Main)getActivity()).GetIsSoundOn())
soundImgBtn.setImageResource(android.R.drawable.ic_lock_silent_mode_off);
else
soundImgBtn.setImageResource(android.R.drawable.ic_lock_silent_mode);
// Inflate the layout for this fragment
return view;
}
private void SoundImgBtnClick(View v)
{
//if sound is on and clicked, turn off
if (((Main)getActivity()).GetIsSoundOn())
{
((Main)getActivity()).SetIsSoundOn(false);
((Main)getActivity()).GetSoundObj().Stop("mainMenuSong");
soundImgBtn.setImageResource(android.R.drawable.ic_lock_silent_mode);
}
else
{
((Main)getActivity()).SetIsSoundOn(true);
((Main)getActivity()).GetSoundObj().Play("mainMenuSong", true);
soundImgBtn.setImageResource(android.R.drawable.ic_lock_silent_mode_off);
}
}
}
What I'm trying to do is emulate a mute button. Once clicked all sounds should be muted.
This is pretty much all I've coded, so far.
Cheers.
I suspect you're using different instances of MediaPlayer. You are allowed to do that BUT you must stop the song within the same instance.
About the code in Add():
MediaPlayer song = MediaPlayer.create(context, songId);
In Stop():
MediaPlayer songToStop = songs.get(name)
Note:
The above codes tell me you're using different instances of the MediaPlayer for one same song. The object song needs to be declared on a higher scope for you to access it and to stop the song.
Need to call release() method after stop() to free up resources.
try songToStop.release() instead
Got it to stop. My class had to be able to handle one song at a time and many fx at the same time. This is what I came up with.
Sound:
public class Sound
{
private static MediaPlayer currentlyPlayingSong,
currentlyPlayingFX;
public Sound() {}
public void PlayFX(int fxId, Context context, boolean shouldLoop)
{
MediaPlayer fx = MediaPlayer.create(context, fxId);
if (currentlyPlayingFX != fx)
{
StopFX();
currentlyPlayingFX = fx;
currentlyPlayingFX.start();
currentlyPlayingFX.setLooping(shouldLoop);
}
}
public void PlaySong(int songId, boolean shouldLoop, Context context)
{
MediaPlayer song = MediaPlayer.create(context, songId);
if (currentlyPlayingSong != song)
{
StopSong();
currentlyPlayingSong = song;
currentlyPlayingSong.start();
currentlyPlayingSong.setLooping(shouldLoop);
}
}
public void StopFX()
{
if (currentlyPlayingFX != null)
{
currentlyPlayingFX.stop();
currentlyPlayingFX.release();
currentlyPlayingFX = null;
}
}
public void StopSong()
{
if (currentlyPlayingSong != null)
{
currentlyPlayingSong.stop();
currentlyPlayingSong.release();
currentlyPlayingSong = null;
}
}
}
This is was based of what #The Original Android answered. Keep it on a single instance.
Thanks for the help.
Related
I want to make a simple music player app that plays songs on selected playlists. I successfully retrieve the details of the songs of the device.
I have a playlist of multiple songs. I want to play only one song at a time. but when I click on other songs already playing song didn't stop or pause. please give a complete solution with reason.
ArrayList<SongModel> list;
SongModel Model Class
public class SongModel {
String songImg,songName,songPath;
Boolean isClick;
public SongModel(String songImg, String songName, String songPath, Boolean isClick) {
this.songImg = songImg;
this.songName = songName;
this.songPath = songPath;
this.isClick = isClick;
}
public Boolean getClick() {
return isClick;
}
public void setClick(Boolean click) {
isClick = click;
}
public String getSongPath() {
return songPath;
}
public void setSongPath(String songPath) {
this.songPath = songPath;
}
public String getSongImg() {
return songImg;
}
public void setSongImg(String songImg) {
this.songImg = songImg;
}
public String getSongName() {
return songName;
}
public void setSongName(String songName) {
this.songName = songName;
}
}
MediaPlayer used in Adapter
MediaPlayer mediaPlayer = MediaPlayer.create(context, Uri.parse(list.get(position).getSongPath()));
if(list.get(position).getClick()){
holder.imgv_pause.setVisibility(View.VISIBLE);
holder.imgv_play.setVisibility(View.GONE);
try {
mediaPlayer.start();
} catch (Exception e) {
e.printStackTrace();
}
} else {
list.get(position).setClick(false);
holder.imgv_pause.setVisibility(View.GONE);
holder.imgv_play.setVisibility(View.VISIBLE);
try {
mediaPlayer.pause();
} catch (Exception e) {
e.printStackTrace();
}
}
holder.imgv_play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
for (int i=0;i<list.size();i++){
list.get(i).setClick(false);
}
list.get(position).setClick(true);
notifyDataSetChanged();
}
});
holder.imgv_pause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
list.get(position).setClick(false);
notifyDataSetChanged();
}
});
}
You need to stop the pervious song when the user scroll in onViewRecycled this function is called when the user scroll the recycler-view.
#Override
public void onViewRecycled(#NonNull RecyclerView.ViewHolder holder) {
super.onViewRecycled(holder);
//stop song by its holder here
}
only one song at a time in Android Studio
use this
static MediaPlayer mediaPlayer;
dont use Privet MediaPlayer mediaPlayer
public class PlayerActivity<mgr, result, audioSession, getApplicationContext, phoneStateListener> extends AppCompatActivity implements MediaPlayer.OnCompletionListener, AudioManager.OnAudioFocusChangeListener {
TextView songname, artistname, playduration, totalduration;
SeekBar seekBar;
ImageView backbutton, menu, playprevious, playnext, shufful;
ImageView play, repeat;
ImageView playalbum;
private Thread playThreads, previousThread, nextThread;
static ArrayList<Song> songlists = new ArrayList<>();
static MediaPlayer mediaPlayer;
I am using FirestorePagingAdapter for my RecyclerView.
when fragment close I try to stop exoplayer in onStop, onPasue, onDestroy method but
it not work properly, It stop only last video in Recycler view but I didn't understand
what is the main problem here.
#Override
public void onStop() {
super.onStop();
adapter.stopListening();
if (HomeVideoHolder.simpleExoPlayer != null) {
HomeVideoHolder.simpleExoPlayer.stop();
}
}
#Override
public void onDestroy() {
super.onDestroy();
adapter.stopListening();
if (HomeVideoHolder.simpleExoPlayer != null) {
HomeVideoHolder.simpleExoPlayer.stop();
}
}
#Override
public void onPause() {
super.onPause();
adapter.stopListening();
if (HomeVideoHolder.simpleExoPlayer != null) {
HomeVideoHolder.simpleExoPlayer.stop();
}
I declared simpleExoplayer as a public static in HomeVideoHolder.
public static PlayerView videoViewpath;
public static SimpleExoPlayer simpleExoPlayer;
I also try stop, release, setPlayWhenReady(false) and seek to end of the video
to stop exoplayer.
but every method gave same result for me. They only stop last video of recyclerView.
please give me a solution for this problem......
my Adapter code...
adapter=new FirestorePagingAdapter<HomeClass, HomeVideoHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull HomeVideoHolder holder, int position, #NonNull HomeClass model) {
holder.setVideoView(getActivity(),model.getAd(),model.getpId(),model.getcUid(),model.getUid(),model.getPic(),model.getVideo(),model.getcT());
}
#NonNull
#Override
public HomeVideoHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new HomeVideoHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_video_item,parent,false));
}
#Override
protected void onLoadingStateChanged(#NonNull LoadingState state) {
switch (state) {
case LOADING_INITIAL:
// The initial load has
swipeRefreshLayout.setRefreshing(true);
break;
// ...
case LOADING_MORE:
// The adapter has started to load an additional page
vLoadMore.setVisibility(View.VISIBLE);
break;
// ...
case LOADED:
// The previous load (either initial or additional) completed
swipeRefreshLayout.setRefreshing(false);
vLoadMore.setVisibility(View.INVISIBLE);
break;
// ...
case FINISHED:
vLoadMore.setVisibility(View.INVISIBLE);
break;
case ERROR:
// The previous load (either initial or additional) failed. Call
adapter.retry();
swipeRefreshLayout.setRefreshing(true);
// the retry() method in order to retry the load operation.
break;
// ...
}
}
};
my viewHolder code....
public void setVideoView(Activity activity,String ad, String pId, String cUid, String uid, String pic, String video, String cT) {
final CircleImageView chanelProfile=view.findViewById(R.id.cPicH);
final TextView chanelName=view.findViewById(R.id.cNameH);
final TextView videoCaption=view.findViewById(R.id.vDesH);
final ImageView videoThreeDot=view.findViewById(R.id.threeDotHome);
Pkey=pId;
user=FirebaseAuth.getInstance().getCurrentUser().getUid();
videoCaption.setText(cT);
setPlayer(video);
}
private void setPlayer(String video) {
if (playCode.equals("true")) {
LoadControl loadControl = new DefaultLoadControl();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelector trackSelector = new DefaultTrackSelector(
new AdaptiveTrackSelection.Factory(bandwidthMeter)
);
simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(
view.getContext(), trackSelector, loadControl
);
DefaultHttpDataSourceFactory factory = new DefaultHttpDataSourceFactory(
"Video"
);
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
MediaSource mediaSource = new ExtractorMediaSource(Uri.parse(Video),
factory, extractorsFactory, null, null
);
videoViewpath.setPlayer(simpleExoPlayer);
videoViewpath.setKeepScreenOn(true);
simpleExoPlayer.prepare(mediaSource);
simpleExoPlayer.setPlayWhenReady(false);
simpleExoPlayer.addListener(new Player.DefaultEventListener() {
#Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
super.onTimelineChanged(timeline, manifest, reason);
}
#Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
super.onTracksChanged(trackGroups, trackSelections);
}
#Override
public void onLoadingChanged(boolean isLoading) {
super.onLoadingChanged(isLoading);
}
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
super.onPlayerStateChanged(playWhenReady, playbackState);
switch (playbackState) {
case Player.STATE_BUFFERING:
progressBar.setVisibility(View.VISIBLE);
break;
case Player.STATE_ENDED:
break;
case Player.STATE_IDLE:
break;
case Player.STATE_READY:
addViwes();
progressBar.setVisibility(View.INVISIBLE);
break;
default:
break;
}
}
#Override
public void onRepeatModeChanged(int repeatMode) {
super.onRepeatModeChanged(repeatMode);
}
#Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
super.onShuffleModeEnabledChanged(shuffleModeEnabled);
}
#Override
public void onPlayerError(ExoPlaybackException error) {
super.onPlayerError(error);
// simpleExoPlayer.prepare(mediaSource);
// simpleExoPlayer.setPlayWhenReady(false);
}
#Override
public void onPositionDiscontinuity(int reason) {
super.onPositionDiscontinuity(reason);
}
#Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
super.onPlaybackParametersChanged(playbackParameters);
}
#Override
public void onSeekProcessed() {
super.onSeekProcessed();
}
});
}else {
simpleExoPlayer.stop();
simpleExoPlayer.release();
simpleExoPlayer.clearVideoSurface();
videoViewpath.setPlayer(null);
}
}
don't declare PlayerView and SimpleExoPlayer as static, this is wrong approach... you are keeping reference only to last video player instance and you want all of them to pause/stop all. every HomeViewHolder should keep only own player reference, static field keeps only one instance across all instances of reference-keeping class ("owner", HomeViewHolder)
remove all your static player stopping code from onPause, onStop and onDestroy, player instance (non-static) won't be accessible anyway
override onViewDetachedFromWindow(HomeVideoHolder holder) and/or onViewRecycled(HomeVideoHolder holder) method(s) in adapter, in this place pause/stop this one player instance attached to single HomeVideoHolder
let adapter stop every player when single item gets recycled/detached, not only when destroying Activity/Fragment. currently you have probably some memory leaks, as started players may stay in memory playing some video and you don't have reference to it (only to last one, static field as above) for releasing resources... when you destroy RecyclerView then adapter attached to it will recycle/destroy all currently existing HomeViewPagers, to be shure you may recyclerView.setAdapter(null) in onDestroy (before super call). also check how this pattern (recycling views) works with some Log calls in all overriden-able methods of RecyclerView.Adapter starting with on... (e.g. like mentioned in 3.)
You should be keeping ONE instance of ExoPlayer. Create it in Activity and then pass to adapter through the constuctor. Here is my code:
Adapter:
class VideoAdapter( private val mContext: Context, val exoPlayer: ExoPlayer, options: FirestoreRecyclerOptions<VideoModel?>) :[...]
exoPlayer
.also { exoPlayer ->
styledPlayerView.player = exoPlayer
exoPlayer.seekTo(currentWindow, playbackPosition)
styledPlayerView.setShowBuffering(StyledPlayerView.SHOW_BUFFERING_WHEN_PLAYING)
styledPlayerView.useController = false
exoPlayer.repeatMode = Player.REPEAT_MODE_ONE
}
Activity:
private lateinit var exoPlayer: ExoPlayer [...]
exoPlayer = ExoPlayer.Builder(requireContext())
.build() [...]
videoAdapter = VideoAdapter(requireContext(), exoPlayer, options)
then in my Activities onStop() method:
override fun onStop() {
super.onStop()
videoAdapter.stopListening()
exoPlayer.release()
}
I have a custom ActivityIndicator defined as this
public class ActivityIndicator extends Dialog
{
private ImageView progress;
private ImageView bottomProgress;
private int type = INDICATOR_SIMPLE;
public static final int INDICATOR_SIMPLE = 0;
public static final int INDICATOR_BOTTOM = 1;
public ActivityIndicator(Context context, int theme, int type)
{
super(context, theme);
this.type = type;
onCreate(null);
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_indicator);
progress = (ImageView) findViewById(R.id.progress);
bottomProgress = (ImageView) findViewById(R.id.bottomProgress);
if(type == INDICATOR_BOTTOM)
{
progress.setVisibility(View.INVISIBLE);
}
else if(type == INDICATOR_SIMPLE)
{
bottomProgress.setVisibility(View.INVISIBLE);
}
this.setCancelable(false);
}
#Override
public void show()
{
progress.clearAnimation();
bottomProgress.clearAnimation();
if(type == INDICATOR_BOTTOM)
{
progress.setVisibility(View.INVISIBLE);
new Handler().postDelayed(new Runnable()
{
#Override
public void run()
{
Animation anim = AnimationUtils.loadAnimation(getContext(), R.anim.rotating_img);
bottomProgress.startAnimation(anim);
}
},400);
}
if(type == INDICATOR_SIMPLE)
{
bottomProgress.setVisibility(View.INVISIBLE);
new Handler().postDelayed(new Runnable()
{
#Override
public void run()
{
Animation anim = AnimationUtils.loadAnimation(getContext(), R.anim.rotating_img);
progress.startAnimation(anim);
}
},400);
}
super.show();
}
#Override
public void dismiss()
{
super.dismiss();
progress.clearAnimation();
bottomProgress.clearAnimation();
}
}
In my activity I initialize it as:
indicator = new ActivityIndicator(this, android.R.style.Theme_Translucent_NoTitleBar_Fullscreen, ActivityIndicator.INDICATOR_SIMPLE);
Now as seen in code , default style cancelable is false.
However at some point i do want to put it cancelable , here is my code:
indicator.setCancelable(true);
indicator.setOnCancelListener(new DialogInterface.OnCancelListener()
{
#Override
public void onCancel(DialogInterface dialog)
{
finish();
}
});
indicator.show();
When I try to press the back button, nothing happens, the dialog doesn't cancel nor the cancel listener. What is wrong here? Why is it not cancelling automatically on back key pressed
Don't Override onCreate(). That onCreate(null) method that you invoke is what's screwing up your code. Rather use an initializer pattern to initialize the Dialog.
If you change your onCreate to an initialize() and invoke that from the constructor the code will work.
Look at the following.
public ActivityIndicator(Context context, int theme, int type)
{
super(context, theme);
this.type = type;
initialize();
}
protected void initialize()
{
setContentView(R.layout.dialog_indicator);
setCancelable(false);
progress = (ImageView) findViewById(R.id.progress);
bottomProgress = (ImageView) findViewById(R.id.bottomProgress);
if(type == INDICATOR_BOTTOM)
{
progress.setVisibility(View.INVISIBLE);
}
else if(type == INDICATOR_SIMPLE)
{
bottomProgress.setVisibility(View.INVISIBLE);
}
}
Please comment your seton cancellabel and use below code and check.
indicator.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
finish();
}
}
}
When you are creating an instance of ActivityIndicator, in the OnCreate method, setCancelable is set to false.
Try removing that..
Got you problem just change your constructor like below and you would get your cancel listner called:
public ActivityIndicator(Context context, int theme, int type, boolean isCancelable)
{
super(context, theme);
this.type = type;
onCreate(null);
this.setCancelable(isCancelable); //setcancelable here on the basis of boolean value and remove setcancelable from onCreate()
}
Call the constructor with one more argument which is boolean true/false
Note: Don't forget to remove setCancelable() from onCreate() method.
I am having a strange issue playing a VideoView. I have done my best to simplify the code as much as possible. Below are 4 classes: A, MirrorActivity, Replay, and MyTask. Assume the following occurs in this order:
A is created.
MirrorActivity() is created.
Inside MirrorActivity()'s onCreate(), that instance calls A's setMirrorActivity() to allow A to have a reference to it.
A's doThis() method is called, which executes mirrorActivity.playVideo().
playVideo() is executed.
Replay's executeVideo() is called.
MyTask is executed.
For some strange reason, when the above is executed, the video does not play. However, when the myButton ImageButton is pressed inside MirrorActivity, it plays the video on command. Both of these seem to be doing the same thing by calling MirrorActivity's playVideo(). Do you know why the above does not execute?
A
public class A{
private static final A instance = new A();
private MirrorActivity mirrorActivity;
public static A getInstance() {
return instance;
}
public void setMirrorActivity(MirrorActivity mirrorActivity) {
this.mirrorActivity = mirrorActivity;
}
public void doThis(String url){
mirrorActivity.playVideo(String url);
}
}
MirrorActivity
public class MirrorActivity extends Activity {
public static String VIDEO_URL = "example.mp4";
public VideoView mVideoView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_overlay_gradient);
// Set Mirror Activity
A.getInstance().setMirrorActivity(this);
mVideoView = (VideoView) findViewById(R.id.mirrorVideoView);
MyTask vTask = new MyTask(mVideoView);
ImageButton myButton = (ImageButton) findViewById(R.id.myButton);
myButton.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v)
{
MirrorActivity.this.playVideo(MirrorActivity.VIDEO_URL);
}
});
}
public void playVideo(String videoURL)
{
MyTask mt = new MyTask(mVideoView);
Replay.executeVideo(MirrorActivity.VIDEO_URL,
this,
mVideoView,
mt);
}
}
Replay
public class Replay{
public static void executeVideo(String uri, Activity activity, VideoView vid, MyTask mt)
{
vid.setMediaController(new MediaController(activity););
vid.setVideoURI(Uri.parse(uri));
mt.execute();
}
}
MyTask
public class MyTask extends AsyncTask<Void, Integer, Void> {
private VideoView video;
private int duration = 0; // in milliseconds
public MyTask(VideoView vid) {
video = vid;
}
#Override
protected Void doInBackground(Void... params) {
video.start();
video.requestFocus();
video.setOnPreparedListener(new OnPreparedListener(){
#Override
public void onPrepared(MediaPlayer mp) {
duration = video.getDuration();
}
});
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
}
I think UI element cannot access in doInBackground . And also start() after every declaration.
Try this method too.
private MediaController ctlr;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_overlay_gradient);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
// Set Mirror Activity
A.getInstance().setMirrorActivity(this);
mVideoView = (VideoView) findViewById(R.id.mirrorVideoView);
Uri uri=Uri.parse(videourl or video path);
mVideoView .setVideoURI(uri);
mVideoView .setVideoPath(videourl);
ctlr=new MediaController(this);
ctlr.setAnchorView(video);
ctlr.setMediaPlayer(video);
mVideoView .setMediaController(ctlr);
mVideoView .requestFocus();
mVideoView .start();
}
Omg. I believe I found the solution. I'm sorry to those who were investigating, but here is what I did to get it to work both ways. Pressing imageButton and calling A.doThis() will be able to play the video now.
I modified MirrorActivity's playVideo() function to use the following:
public void playVideo(final String videoURL)
{
Runnable runnable = new Runnable(){
#Override
public void run()
{
MyTask mt = new MyTask(mVideoView);
Replay.executeVideo(videoURL,
MirrorActivity.this,
mVideoView,
mt);
}
});
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(runnable);
}
I am creating a sound board and after clicking about 30 different sounds it stops working; I believe android is running out of memory. Below is my code. How can I implement .release() so that when the sound is done playing it is released? I don't really care if two things play at the same time; the clips are t0o short for this to be possible. I would just like to get my code set.
public class soundPageOne extends Activity {
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.soundsone);
final MediaPlayer pg1 = MediaPlayer.create(this, R.raw.peter1);
Button playSound1 = (Button) this.findViewById(R.id.peter1Button);
playSound1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
pg1.start();
}
});
I have done a lot of searching around but due to my lack of java/android knowledge I have not been able to get anything to work. Thanks in advance, let me know if anyone needs anymore code.
I left a comment, but I'll post an answer to show what I mean anyway...
The idea is that you have a set number of MediaPlayer instances that you can use. That way you never exceed the maximum number of instances. The array should be the length of the number of concurrent sounds you expect to be able to hear. If the sounds are local files, the length of time it takes to prepare the sounds should be almost negligible, so calling create inside the click handler should not result in terrible performance. Each of your buttons is associated with a particular resource, I suppose, so I set up a helper method to create and play the sounds for each button in the same way.
public class soundPageOne extends Activity {
private MediaPlayer[] mPlayers = new MediaPlayer[2];
private int mNextPlayer = 0;
#Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.soundsone);
Button playSound1 = (Button)this.findViewById(R.id.peter1Button);
playSound1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startSound(R.raw.peter1);
}
});
}
public void onDestroy() {
super.onDestroy(); // <---------------------- This needed to be there
for (int i = 0; i < mPlayers.length; ++i)
if (mPlayers[i] != null)
try {
mPlayers[i].release();
mPlayers[i] = null;
}
catch (Exception ex) {
// handle...
}
}
private void startSound(int id) {
try {
if (mPlayers[mNextPlayer] != null) {
mPlayers[mNextPlayer].release();
mPlayers[mNextPlayer] = null;
}
mPlayers[mNextPlayer] = MediaPlayer.create(this, id);
mPlayers[mNextPlayer].start();
}
catch (Exception ex) {
// handle
}
finally {
++mNextPlayer;
mNextPlayer %= mPlayers.length;
}
}
}
Create a class, say AudioPlayer with a SoundPool variable. Setup a constructor to initialise the AudioPlayer object and create a Play method. SoundPool works better for short sounds played many times and does not require you to release.
public class AudioPlayer {
private SoundPool sPool = new SoundPool(Integer.MAX_VALUE, AudioManager.STREAM_MUSIC, 0);
public AudioPlayer(Context c, int id){
sounds.put("1",sPool.load(c, id, 1));
}
public void play(Context c) {
sPool.play("1", 1, 1, 1, 0, 1f);
}
}
So your class should look like
public class soundPageOne extends Activity {
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.soundsone);
final AudioPlayer ap = new AudioPlayer(this, R.raw.sound);
Button playSound1 = (Button) this.findViewById(R.id.peter1Button);
playSound1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ap.play();
}
});
Could you use a MediaPlayer.OnCompletionListener?
Something like:
public class soundPageOne extends Activity implements MediaPlayer.OnCompletionListener {
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.soundsone);
final MediaPlayer pg1 = MediaPlayer.create(this, R.raw.peter1);
//***set the listener here***
pg1.setOnCompletionListener(this);
Button playSound1 = (Button) this.findViewById(R.id.peter1Button);
playSound1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
pg1.start();
}
});
}
//***this code will be executed once the sound finishes playing***
#Override
public void onCompletion(MediaPlayer mp) {
//log messages, other things can go here
mp.release();
}
Try something like this
Your activity class:
public class soundPageOne extends Activity {
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.soundsone);
final AudioPlayer pg1 = new AudioPlayer();
Button playSound1 = (Button) this.findViewById(R.id.peter1Button);
playSound1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
pg1.play(this, R.raw.sound);
}
});
}
This is another Java Class:
public class AudioPlayer {
private MediaPlayer mPlayer;
public void stop() {
if (mPlayer != null) {
mPlayer.release();
mPlayer = null;
}
}
public void play(Context c, int sound) {
stop();
mPlayer = MediaPlayer.create(c, sound);
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
stop();
}
});
mPlayer.start();
}
public boolean isPlaying() {
return mPlayer != null;
}
}