I have a MediaPlayer and an audio file, which I need to play, also I have an ArrayList with the certain seconds of this audio file, on this seconds while audio is playing, I need to switch pages in the ViewPager. How to get a CallBack from the MediaPlayer in these certain moments of the time? or maybe you will recommend some other way how is possible to do it.
What I have right now is the method, where I use CountDownTimer, which change pages in ViewPager, but it works wrong and it doesn't take into the account fact that an audio track can be stop and then resume.
public void startPlayingLabels() {
mPlayer = new MediaPlayer();
try {
String mFileName = CameraFragment.mAudioFolder + "/" + ViewActivity.parentName + ".3gp";
mPlayer.setDataSource(mFileName);
mPlayer.prepare();
mPlayer.start();
for (int i=0; i<zeroLabelPosition.size()-1; i++) {
final int finalI = i;
new CountDownTimer(Integer.parseInt(String.valueOf(Integer.parseInt((String) zeroTime.get(finalI + 1)) - Integer.parseInt((String) zeroTime.get(finalI)))), 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
mPager.setCurrentItem(Integer.parseInt(String.valueOf(zeroLabelPosition.get(finalI))));
}
}.start();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
Android madiaplayer does not have any timeline update, thats why you can write your own, or you can use something like this.
CountDownTimer timer;
int timeStampIterator;
int[] timeStamp = new int[5]; // your own time stamps when view must be switched
void play() {
mPlayer.start();
timer = new CountDownTimer(mPlayer.getDuration() - mPlayer.getCurrentPosition(), 1000) {
#Override
public void onTick(long millisUntilFinished) {
int timeSpends = mPlayer.getDuration() - mPlayer.getCurrentPosition();
if (timeSpends == timeStamp[timeStampIterator]) {
//switchToNExtSlide();
timeStampIterator++;
}
}
#Override
public void onFinish() {
}
}.start();
}
void pause() {
mPlayer.pause();
timer.cancel();
timer = null;
}
void stop() {
mPlayer.stop();
timer.cancel();
timer = null;
timeStampIterator = 0;
}
you can also use handler for it like following
Handler handler = new Handler();
Runnable notification = new Runnable() {
public void run() {
startPlayProgressUpdater();// change hear page for view pager, because it call after every 1 second
}
};
handler.postDelayed(notification, 1000);
Related
I want to show a custom XML dialog dialogue that will appear after a specific time in the first run, let's say after a min
how can I do it
but I'm confused about what should I do in a situation like below
if the user open the app for the first time and just spent 30 sec and just pause the app(screen lock or in onPause) or just close the app completely
Just as a note - I have already implemented a one time show dialog(directly in main activity without any layout file ) when the app runs for the first time already
Code
To view the already implemented dialog(shows up on the first run) please
go to the // Caution dialog (showDialog method)
MainActivity.java
public class MainActivity extends AppCompatActivity {
MediaPlayer player1;
MediaPlayer player2;
SeekBar seekBar1;
SeekBar seekBar2;
TextView elapsedTimeLable1;
TextView elapsedTimeLable2;
TextView remainingTimeLable1;
TextView remainingTimeLable2;
ImageView play1;
ImageView play2;
int totalTime1;
#SuppressLint("HandlerLeak")
private final Handler handler1 = new Handler() {
#SuppressLint("SetTextI18n")
#Override
public void handleMessage(#NonNull Message msg) {
int currentPosition1 = msg.what;
//Update SeekBar
seekBar1.setProgress(currentPosition1);
// Update Timelable
String elapsedTime1 = createTimerLable1(currentPosition1);
elapsedTimeLable1.setText(elapsedTime1);
String remainingTime1 = createTimerLable1(totalTime1 - currentPosition1);
remainingTimeLable1.setText("- " + remainingTime1);
}
};
int totalTime2;
#SuppressLint("HandlerLeak")
private final Handler handler2 = new Handler() {
#SuppressLint("SetTextI18n")
#Override
public void handleMessage(#NonNull Message msg) {
int currentPosition2 = msg.what;
// Update SeekBar
seekBar2.setProgress(currentPosition2);
// Update Timelable
String elapsedTime2 = createTimerLable2(currentPosition2);
elapsedTimeLable2.setText(elapsedTime2);
String remainingTime2 = createTimerLable2(totalTime2 - currentPosition2);
remainingTimeLable2.setText("- " + remainingTime2);
}
};
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#SuppressLint("ObsoleteSdkInt")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window w = getWindow();
// clear FLAG_TRANSLUCENT_STATUS flag:
w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
w.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// finally change the color
w.setStatusBarColor(ContextCompat.getColor(this, R.color.Card_Elevation_Color));
}
// PlayButton * The ButtonClick is in the last if you want to jump directly there *
play1 = findViewById(R.id.playbtn1);
play2 = findViewById(R.id.playbtn2);
// TimeLables
elapsedTimeLable1 = findViewById(R.id.cTime1);
elapsedTimeLable2 = findViewById(R.id.cTime2);
remainingTimeLable1 = findViewById(R.id.tTime1);
remainingTimeLable2 = findViewById(R.id.tTime2);
// MediaPlayer
player1 = MediaPlayer.create(this, R.raw.dog_howl);
player1.setLooping(true);
player1.seekTo(0);
totalTime1 = player1.getDuration();
player2 = MediaPlayer.create(this, R.raw.dog_bark);
player2.setLooping(true);
player2.seekTo(0);
totalTime2 = player2.getDuration();
//SeekBar
seekBar1 = findViewById(R.id.seekbar1);
seekBar2 = findViewById(R.id.seekbar2);
seekBar1.setMax(totalTime1);
seekBar2.setMax(totalTime2);
seekBar1.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress1, boolean fromUser1) {
if (fromUser1) {
player1.seekTo(progress1);
seekBar1.setProgress(progress1);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
seekBar2.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress2, boolean fromUser2) {
if (fromUser2) {
player2.seekTo(progress2);
seekBar2.setProgress(progress2);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
// Thread (Update SeekBar & TimeLabel)
new Thread(() -> {
while (player1 != null) {
try {
Message msg = new Message();
msg.what = player1.getCurrentPosition();
handler1.sendMessage(msg);
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
}
}).start();
new Thread(() -> {
while (player2 != null) {
try {
Message msg = new Message();
msg.what = player2.getCurrentPosition();
handler2.sendMessage(msg);
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
}
}).start();
// Admob Banner Ad
MobileAds.initialize(this, initializationStatus -> {
});
AdView mAdView = findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder().build();
mAdView.loadAd(adRequest);
// Caution dialog
SharedPreferences preferences = getSharedPreferences("prefs", MODE_PRIVATE);
boolean firstStart = preferences.getBoolean("firstStart", true);
if (firstStart) {
showDialog();
}
}
// Caution dialog
private void showDialog() {
new AlertDialog.Builder(this)
.setTitle("Caution!")
.setMessage("In case you're wearing any kind of headphones please remove it before playing the ' Howl ' audio")
.setPositiveButton("ok", (dialogInterface, i) -> dialogInterface.dismiss())
.create().show();
SharedPreferences preferences = getSharedPreferences("prefs", MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("firstStart", false);
editor.apply();
}
public String createTimerLable1(int duration) {
String timerLabel1 = "";
int min = duration / 1000 / 60;
int sec = duration / 1000 % 60;
timerLabel1 += min + ":";
if (sec < 10) timerLabel1 += "0";
timerLabel1 += sec;
return timerLabel1;
}
public String createTimerLable2(int duration) {
String timerLabel2 = "";
int min = duration / 1000 / 60;
int sec = duration / 1000 % 60;
timerLabel2 += min + ":";
if (sec < 10) timerLabel2 += "0";
timerLabel2 += sec;
return timerLabel2;
}
public void playBtnClick1(View view) {
if (player2.isPlaying()) {
player2.pause();
play2.setImageResource(R.drawable.ic_baseline_play_circle_filled_24);
}
if (!player1.isPlaying()) {
// Stoping
player1.start();
play1.setImageResource(R.drawable.ic_baseline_pause_circle_filled_24);
} else {
// Playing
player1.pause();
play1.setImageResource(R.drawable.ic_baseline_play_circle_filled_24);
}
}
public void playBtnClick2(View view) {
if (player1.isPlaying()) {
player1.pause();
play1.setImageResource(R.drawable.ic_baseline_play_circle_filled_24);
}
if (!player2.isPlaying()) {
// Stoping
player2.start();
play2.setImageResource(R.drawable.ic_baseline_pause_circle_filled_24);
} else {
// Playing
player2.pause();
play2.setImageResource(R.drawable.ic_baseline_play_circle_filled_24);
}
}
#Override
protected void onPause() {
super.onPause();
if (player1 != null) {
player1.pause();
play1.setImageResource(R.drawable.ic_baseline_play_circle_filled_24);
}
if (player2 != null) {
player2.pause();
play2.setImageResource(R.drawable.ic_baseline_play_circle_filled_24);
}
}
}
but I'm confused about what should I do in a situation like below
if the user open the app for the first time and just spent 30 sec and just pause the app(screen lock or in onPause) or just close the
app completely
This is impossible to do if your app is closed.My suggestion would be to create a service on another process that does this dialog such that even if the app process is closed,the service process will still be running unless it is stopped explicitly.
Defining a Process of a Service
The android:process field defines the name of the process where the
service is to run. Normally, all components of an application run in
the default process created for the application. However, a component
can override the default with its own process attribute, allowing you
to spread your application across multiple processes.
If the name assigned to this attribute begins with a colon (':'), the
service will run in its own separate process.
<service android:name="com.example.appName" android:process=":externalProcess" />
This is of course in the manifest file .
You might also need to show a system dialog thus you will need a system Alert Window permission i your manifest and request for the permision on runtime.
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Then on runtime request like this:
public static void openOverlaySettings(Activity activity) {
final Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + activity.getPackageName()));
try {
activity.startActivityForResult(intent, 6);
} catch (ActivityNotFoundException e) {
Log.e("Drawers permission :", e.getMessage());
}
}
To check if granted use :
if(!Settings.canDrawOverlays(context)) {
openOverlaySettings(context);
ok=false;
}
Then in your service you should create the dialog like below
View aldv= LayoutInflater.from(act).inflate(R.layout.your_layout,null);
ald=new AlertDialog.Builder(act,R.style.AppTheme)
.setView(aldv)
.setCancelable(true)
.create();
ald.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
I am programming an app which will send an SMS message when a set time runs out. When I change the orientation of the phone or temporary leave this activity, the timer still keeps counting, but the Stop button does not work, and the Start button is visible. I don't know how to save the settings; I know I have to use onSaveInstanceState, but I don't know exactly what to put in there.
Code:
public void startThread(View view) {
stopThread = false;
sum = sec + mins;
if (sum == 0) {
Toast.makeText(Background.this, "Please enter a positive number", Toast.LENGTH_SHORT).show();
return;
}
ExampleRunnable runnable = new ExampleRunnable(sum);
new Thread(runnable).start();
btnStartThread.setEnabled(false);
}
public void stopThread(View view) {
stopThread = true;
btnStartThread.setEnabled(true);
}
class ExampleRunnable implements Runnable {
int seconds;
ExampleRunnable(int seconds) {
this.seconds = seconds;
}
#Override
public void run() {
for (; ; ) {
for (int i = 0; i < seconds; i++) {
if (stopThread) {
return;
}
if (i == sum-1) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(check_sms.isChecked() && edit_phone_number.length() > 0) {
sendSMS();
}
}
});
}
Log.d(TAG, "startThread: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
I would persist a flag like "hasTimerStarted" and set it true/false depending if the timer has started or not and, if the timer has indeed started, I would save some other flags like "timerStartedEpoch" (the epoch number when time has started), "timerTimeoutMs" (the timeout ms).
This way, whenever you leave the activity and come back to it, you can load the flags and update the state based on them.
I'm making a timer which has a button I can control either pick the count-up mode or countdown mode. The count down mode is working perfectly, start, pause, reset. The count-up timer is made by reversing the countdown timer in Android studio. the problem is I can't pause the count-up timer and resume from where it stops.
What's the proper way to do that? I know it could be done by chronometer for count-up mode, but I'm trying to make a customized timer widget that as I mentioned before, a button could be pressed to let the user decide which mode he wants. So if possible, I don't want to use another widget in my current set up.
public void startBuiltInTimer(int millisecondCountingInterval) {
if (getTimeInMillis() > 0) { //getTimeInMillis will read the time value in the current Timer widget, which is a textView
builtInTimerIsCounting = true; //set the timer is running flag
showAsCounting(true);
if (getTimerCountUpOrDown() == TIMER_COUNT_DOWN) { //countdown mode
countDownTimer = new CountDownTimer(getTimeInMillis(), millisecondCountingInterval) {
#Override
public void onTick(long millisUntilFinished) {
setTime(millisUntilFinished);
}
}
#Override
public void onFinish() {
setTime(0);
builtInTimerIsCounting = false;
showAsCounting(false);
}
}
}.start();
} else if (getTimerCountUpOrDown() == TIMER_COUNT_UP) { //count up mode
long tempTimeInMillis = getTimeInMillis() + lastTimeMillisUntilFinished; //trying to save the last time value in millis and keep the total time value as user defined.
setTime(0); //set initial value of the textView to show the count-up timer start from zero, initially.
countDownTimer = new CountDownTimer(tempTimeInMillis, millisecondCountingInterval) {
#Override
public void onTick(long millisUntilFinished) {
long temp = tempTimeInMillis - millisUntilFinished;
setTime(temp); //reverse the countdown to count-up
lastTimeMillisUntilFinished = millisUntilFinished;//keep the millisUntilFinished for next start.
}
#Override
public void onFinish() {
setTime(tempTimeInMillis);
builtInTimerIsCounting = false;
showAsCounting(false);
}
}
}.start();
}
}
}
Edit:
I end up to use SystemClock to keep tracking and record the count up timer. Here is the code.
else if (getTimerCountUpOrDown() == TIMER_COUNT_UP) {
startCountUpTimer(millisecondCountingInterval);
}
public void startCountUpTimer(int millisecInterval) {
if (!countUpTimerIsRunning) {
handler = new Handler();
myRunnable = new MyRunnable(millisecInterval); //MyRunnable can pass a variable thru to let handler have more flexibility with postDelay method. You can still use regular Runnable with hard coded time interval in millisec.
startTime = SystemClock.uptimeMillis(); //use startTime to mark the time when the count up timer start ticking
handler.postDelayed(myRunnable, millisecInterval);
countUpTimerIsRunning = true;
}
}
public class MyRunnable implements Runnable {
private int interval;
public MyRunnable(int t) {
this.interval = t;
}
#Override
public void run() {
millisecondTime = SystemClock.uptimeMillis() - startTime; //this is the time value measured by the count up timer
updateTime = timeBuff + millisecondTime;
setTime(updateTime);
if (updateTime <= countUpMode_Threshold) {
handler.postDelayed(this, interval);
} else {
setTime(countUpMode_Threshold);
handler.removeCallbacks(myRunnable);
builtInTimerIsCounting = false;
showAsCounting(false);
if (onBuiltInTimerStatusChangeListener != null) {
onBuiltInTimerStatusChangeListener.onStatusChange(OnBuiltInTimerStatusChangeListener.STATUS_FINISHED);
}
}
}
}
public void pauseCountUpTimer() {
timeBuff += millisecondTime;
handler.removeCallbacks(myRunnable);
countUpTimerIsRunning = false;
}
One way is that you should keep track of the pause time manually and start timer from that time again. Or you can try using the utility at this link It has the functionality you are looking for.
Pretty helpful
Maybe something like this:
private CountDownTimer timer;
private long COUNT_DOWN = -1;
private long COUNT_UP = 1;
public void startBuiltInTimer(boolean isDown) {
if (isdown) {
launchTimer(COUNT_DOWN);
} else {
launchTimer(COUNT_UP);
}
}
public void launchTimer(long interval) {
builtInTimerIsCounting = true; //set the timer is running flag
showAsCounting(true);
timer = new CountDownTimer(getTimeInMillis(), TimeUnit.SECONDS.toMillis(interval) {
#Override
public void onTick(long time) {
setCountdownText(time.intValue());
setTimeInMillis(time);
}
#Override
public void onFinish() {
setCountdownText(time.intValue());
builtInTimerIsCounting = false;
showAsCounting(false);
}
}.start();
public void stopTimer() {
timer.cancel();
}
...
I am trying to simulate an sending msg progress .
The main idea is to colour the members who message sent to them, promote the status bar etc.. (gui changes ) all this with delay for each loop iteration.
The main problem is that everything is going inside the onClick listener and runing thread inside it wont help ): cause gui is changing only after delay is completed.
Any one may advice me how to do this simulation ?
send.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
for(int i=0; i<phoneNumbers.size(); i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
members.get(i).setBackgroundColor(Color.GREEN);
members.get(i).invalidate();
mProgressDialog.setProgress(i+1);
sentCount.setText(getString(R.string.msgSentCount) + (i+1));
if(i==12){
scroolView.animate().translationYBy(-50);}
}
}
});
Thanks you very much !
If I unserstood you right - fake progress + waiting time. First of all handlers a perferct to delay an output:
//Fake Progress
progress = new ProgressDialog(this);
open(this);
// Delay output
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
setData();
}
}, 6000);
in the function setData(), do the task you wanna run after
and progress bar for fake:
public void open(Activity activity) {
progress.setMessage("Fakeprogress, Data Mining...");
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progress.setIndeterminate(false);
progress.setCancelable(false);
progress.setMax(100);
progress.setProgress(10);
progress.show();
final int totalProgressTime = 100;
final Thread t = new Thread() {
#Override
public void run() {
int jumpTime = 0;
while (jumpTime < totalProgressTime) {
try {
sleep(60);
//System.out.println("********" + jumpTime);
jumpTime += 1;
progress.setProgress(0);
progress.setProgress(jumpTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
progress.dismiss();
}
};
t.start();
}
Finaly got it works with recursion !!!!
public static int counter=0;
public void runMother(final Handler handler)
{
if(counter==3)return;
handler.postDelayed(new Runnable() {
#Override
public void run() {
members.get(counter).setBackgroundColor(Color.GREEN);
counter++;
runMother(handler);
}
}, 1000*counter);
I need to stop a Runnable from running when an image is clicked in my Android app. I'm running this Runnable repeatedly using ImageView.postDelayed():
r = new Runnable() {
public void run() {
imgview.setImageResource(imageArray[i]);
i++;
if (i >= imageArray.length) {
i = 0;
}
imgview.postDelayed(r, 20); // set to go off again in 3 seconds.
// imgview.setOnClickListener(this);
}
};
imgview.postDelayed(r, 20); // set first time for 3 seconds
But under certain conditions I want to stop it from running, after it's already started. Here's the full code for my activity:
public class MainActivity extends Activity {
int i = 0;
ImageView imgview, imgview2;
Handler handler = new Handler();
Runnable r;
MediaPlayer mMediaPlayer;
int[] imageArray = { R.drawable.f1, R.drawable.f2, R.drawable.f3,
R.drawable.f4, R.drawable.f5, R.drawable.f6, R.drawable.f7,
R.drawable.f8, R.drawable.f9, R.drawable.f10, R.drawable.f11,
R.drawable.f12, R.drawable.f13, R.drawable.f14, R.drawable.f15,
R.drawable.f16, R.drawable.f17, R.drawable.f18, R.drawable.f19,
R.drawable.f20, R.drawable.f21, R.drawable.f22, R.drawable.f23,
R.drawable.f24, R.drawable.f25, R.drawable.f26, R.drawable.f27,
R.drawable.f28 };
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tapp_activity);
imgview = (ImageView) findViewById(R.id.imageView1);
mMediaPlayer = new MediaPlayer();
mMediaPlayer = MediaPlayer.create(MainActivity.this, R.raw.water);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
imgview2 = (ImageView) findViewById(R.id.imageView2);
imgview2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(i==0)
{
mMediaPlayer.setLooping(true);
mMediaPlayer.start();
i=1;
r = new Runnable() {
public void run() {
imgview.setImageResource(imageArray[i]);
i++;
if (i >= imageArray.length) {
i = 0;
}
imgview.postDelayed(r, 20); // set to go off again in 3 seconds.
// imgview.setOnClickListener(this);
}
};
imgview.postDelayed(r, 20); // set first time for 3 seconds
}
else
{
i=0;
mMediaPlayer.stop();
imgview.setBackgroundResource(R.drawable.tapstill);
}
}
});
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
mMediaPlayer.stop();
}
}
What can I change in my code so that my Runnable stops running in the else condition of my onClick() method?
You might try removing the callback.
imgview.removeCallbacks(r);
In order for this to work, though. you'd have to ensure that r is the same Runnable as the one you posted. You could do this by creating it once, possibly in onCreate. Since the Runnable doesn't have any dependency on the ClickListener anyway, this shouldn't be a problem.
You might also need to do some synchronization in order to prevent the case where you're removing a currently running callback, though, now that i think about it. The volatile boolean running idea is probably less complex overall.
You can control your run() method with a boolean flag:
boolean running = true;
...
r = new Runnable()
{
public void run()
{
if(running)
{
imgview.setImageResource(imageArray[i]);
i++;
if (i >= imageArray.length) {
i = 0;
}
imgview.postDelayed(r, 20);
}
};
}
If you set running = false later on, your thread will be idle and that's what you want.
You can do it by
Thread.stop();