I have coded an android app using android download manager, and I try to show downloading progress using below code.
myTimer.schedule(new TimerTask() {
public void run() {
try {
DownloadManager.Query q;
q = new DownloadManager.Query();
q.setFilterById(preferenceManager.getLong(strPref_Download_ID, 0));
cursorTimer = downloadManager.query(q);
cursorTimer.moveToFirst();
int bytes_downloaded = cursorTimer.getInt(cursorTimer.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
bytes_total = cursorTimer.getInt(cursorTimer.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
final int dl_progress = (int) ((double) bytes_downloaded * 100f / (double) bytes_total);
mProgressDialog.setProgress((int) dl_progress);
} catch (Exception e) {
} finally {
}
}
}, 0, 10);
Everthing is working fine, but progress dialog is not showing smooth pregress that means I wish to show 1,2,3,4,5,6,.....100.
It's show initially 0, and suddenly change to 12% then 31% etc 100%.
My file Total size is 26246026 bytes, at the time of 0% my downloaded file size is 6668 bytes,
at the time of 12% my downloaded file size is 3197660 bytes, and etc...
First of all don't query too frequent it may hang your UI and use ValueAnimator for changing progress smoothly.
myTimer.schedule(new TimerTask() {
public void run() {
try {
DownloadManager.Query q;
q = new DownloadManager.Query();
q.setFilterById(preferenceManager.getLong(strPref_Download_ID, 0));
cursorTimer = downloadManager.query(q);
cursorTimer.moveToFirst();
int bytes_downloaded = cursorTimer.getInt(cursorTimer.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
bytes_total = cursorTimer.getInt(cursorTimer.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
final int dl_progress = (int) ((double) bytes_downloaded * 100f / (double) bytes_total);
changeProgressSmoothly((int) dl_progress);
} catch (Exception e) {
} finally {
}
}
}, 0, 5000);
private void changeProgressSmoothly(int progress) {
ValueAnimator va = ValueAnimator.ofInt(mProgressDialog.getProgress(), progress);
int mDuration = 2000; //in millis
va.setDuration(mDuration);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
mProgressDialog.setProgress((int) animation.getAnimatedValue());
}
});
va.start();
}
From documentation,
public void schedule (TimerTask task, long delay, long period)
Schedule a task for repeated fixed-delay execution
after a specific delay.
Parameters
task - the task to schedule.
delay - amount of time in milliseconds before first execution.
period - amount of time in milliseconds between subsequent executions.
Here, you have a period of 10 millis in your code. That might be the problem. Try 1 millis instead.
myTimer.schedule(new TimerTask() {
}, 0, 1);
My app loads two audio files from database and stores them in an array. User can play any of them by selecting one from radio button group. both are mp3. One is playing fine and it's elapsed and total duration is displaying correctly. But the same functions display 00:00 total duration for other. Seek bar also updates its progress to 100% in this case but the elapsed time is correctly displaying and audio is playing fine. Someone please tell what is the problem? Why this is happening.. and how can I resolve it??
audio_urdu's time is fine.. error is with audio_eng.
private void updateView(int i) throws JSONException
{
idx=0;
_imgBtnPlay.setClickable(false);
_imgBtnStop.setClickable(false);
JSONObject jObject=null;
jObject=Jarray.getJSONObject(i);
audioUrl_eng=jObject.getString("audio_eng");
audioUrl_urdu=jObject.getString("audio_urdu");
lbl_tDuration.setText("00:00");
lbl_cDuration.setText("00:00");
lbl_loading.setText("Loading audio files...");
loadAudio(audioUrl_eng);
}
// Loading audio files from URL
private void loadAudio(String url)
{
// TODO Auto-generated method stub
mMediaPlayer=new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try
{
mMediaPlayer.setDataSource(url);
mMediaPlayer.prepareAsync();
}
catch(IOException e)
{
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalStateException e)
{
e.printStackTrace();
}
mMediaPlayer.setOnPreparedListener(this);
}
// Notify when audio is ready to be played
#Override
public void onPrepared(MediaPlayer mp)
{
// TODO Auto-generated method stub
audioPlayerList[idx]=mp;
idx++;
if (idx == 1)
{
audioPlayer = mp;
lbl_tDuration.setText(mSecToTimer(mp.getDuration()));
lbl_cDuration.setText(mSecToTimer(mp.getCurrentPosition()));
updateSeekBar();
loadAudio(audioUrl_eng);
}
if (idx == 2)
{
// Enabling the media control buttons
_imgBtnPlay.setClickable(true);
_imgBtnStop.setClickable(true);
rdGrp.setClickable(true);
lbl_loading.setText("");
idx = 0;
}
}
public void onClick(View v)
{
switch(v.getId())
{
// calling search function
case R.id.imgBtnSearch:
onSearchRequested();
break;
// when play/pause button is tapped
case R.id.imgBtnPlay:
if (audioPlayer.isPlaying())
{
audioPlayer.pause();
_imgBtnPlay.setImageResource(R.drawable.ic_action_play);
}
else if (audioPlayer!=null)
{
audioPlayer.start();
_imgBtnPlay.setImageResource(R.drawable.ic_action_pause);
durationHandler.postDelayed(updateSeekBarTime, 100);
}
break;
// when stop button is tapped
case R.id.imgBtnStop:
audioPlayer.pause();
_imgBtnPlay.setImageResource(R.drawable.ic_action_play);
resetProgress();
break;
default:
break;
}
// Updating the seek bar's time after every 100 milliseconds
public void updateSeekBar()
{
durationHandler.postDelayed(updateSeekBarTime, 100);
}
// Updating the progress of seek bar
private Runnable updateSeekBarTime = new Runnable()
{
public void run()
{
long tDuration = audioPlayer.getDuration();
long cDuration = audioPlayer.getCurrentPosition();
lbl_tDuration.setText(mSecToTimer(tDuration));
lbl_cDuration.setText(mSecToTimer(cDuration));
int progress = (int) getProgressPercentage(cDuration, tDuration);
_seekbar.setProgress(progress);
durationHandler.postDelayed(this, 100);
}
};
// Converting milliseconds into min:sec format
public String mSecToTimer(long ms)
{
String finalTimerString = "";
String secString = "";
String minString = "";
// Convert total duration into minutes and seconds
int min = (int)(ms % (1000*60*60)) / (1000*60);
int sec = (int) ((ms % (1000*60*60)) % (1000*60) / 1000);
// Prepending 0 to seconds if it is one digit
if(sec < 10)
secString = "0" + sec;
else
secString = "" + sec;
// Prepending 0 to minutes if it is one digit
if(min < 10)
minString = "0" + min;
else
minString = "" + min;
finalTimerString = minString + ":" + secString;
return finalTimerString;
}
// calculating the percentage progress of seek bar
public int getProgressPercentage(long cDuration, long tDuration)
{
Double percentage = (double) 0;
long cSeconds = (int) (cDuration / 1000);
long tSeconds = (int) (tDuration / 1000);
percentage =(((double)cSeconds)/tSeconds)*100;
return percentage.intValue();
}
// Converting progress of seek bar into time duration in milliseconds
public int progressToTimer(int progress, int tDuration)
{
int cDuration = 0;
tDuration = (int) (tDuration / 1000);
cDuration = (int) ((((double)progress) / 100) * tDuration);
return cDuration * 1000;
}
// Reseting the progress of seek bar when stop button is tapped
public void resetProgress()
{
audioPlayer.seekTo(0);
lbl_cDuration.setText(mSecToTimer(0));
_seekbar.setProgress(0);
}
I have one audio in English and one in Urdu language. both are in the array audioPlayerList. User can select different languages using radio buttons. and idx is variable which tells which audio file is to be played. audio_eng is on index 0 (idx = 0) and audio_urdu is on index 1 (idx = 1). Audio is selected as audioPlayer = audioPlayerList[idx]
code for Radio button selection is this:
rdGrp.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
#Override
public void onCheckedChanged(RadioGroup group, int checkedId)
{
// Find which radio button is selected
if (audioPlayer!=null)
{
if(audioPlayer.isPlaying())
audioPlayer.pause();
_imgBtnPlay.setImageResource(R.drawable.ic_action_play);
resetProgress();
if (checkedId == R.id.rdEng)
audioPlayer = audioPlayerList[0];
else if (checkedId == R.id.rdUrdu)
audioPlayer = audioPlayerList[1];
}
}
});
Duration of your file 'audio_eng' might be less than 1 second. When calculating percentage you convert milliseconds to seconds that results in 0 total length. That's why you get progress bar set to 100% from the beginning (actually, an exception might be thrown in this case - did you check that?).
When calculatng percentage try not to convert xDurationinto xSeconds but divide durations themselves in method getProgressPercentage.
I couldn't find other reasons why you get such result
manager.setSpeakerphoneOn(true);
works for mobile phones but does not works for tablet.
The sound does not plays from the speaker. Does anybody knows why?
Please Help
MODIFY_AUDIO_SETTINGS permission is also added in Manifest file.
My code
Path for the audio file
String mainpath = "android.resource://com.example.texttospeech/raw/";
public void makeSound(String amount) {
AudioManager m_amAudioManager;
m_amAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
m_amAudioManager.setMode(AudioManager.MODE_CURRENT);
m_amAudioManager.setSpeakerphoneOn(true);
int numInput = Integer.parseInt(amount);
String amt = amount;
Log.d("The amount is : ", amt);
int length = amt.length();
Log.d("Length of the String : ", Integer.toString(length));
thousandsDigit = numInput / 1000;
ths = numInput % 1000;
hundredsDigit = ths / 100;
hs = ths % 100;
tensDigit = hs / 10;
onesDigit = hs % 10;
System.out.println(thousandsDigit);
System.out.println(hundredsDigit);
System.out.println(tensDigit);
System.out.println(onesDigit);
Log.d("Transaction type:",transactionType);
if (transactionType.equals("WITHDRAW")){
// withdraw();
final MediaPlayer mp11 = new MediaPlayer();
try {
mp11.setDataSource(AgentTransaction.this,
Uri.parse(mainpath + "withdraw"));
mp11.prepare();
} catch (IllegalArgumentException | SecurityException
| IllegalStateException | IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
mp11.start();
mp11.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp11.release();
}
});
}
}
I have made a voice recorder app, and I want to show the duration of the recordings in a listview. I save the recordings like this:
MediaRecorder recorder = new MediaRecorder();
recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
folder = new File(Environment.getExternalStorageDirectory()
+ File.separator + "Audio recordings");
String[] files = folder.list();
int number = files.length + 1;
String filename = "AudioSample" + number + ".mp3";
File output = new File(Environment.getExternalStorageDirectory()
+ File.separator + "Audio recordings" + File.separator
+ filename);
FileOutputStream writer = new FileOutputStream(output);
FileDescriptor fd = writer.getFD();
recorder.setOutputFile(fd);
try {
recorder.prepare();
recorder.start();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
e.printStackTrace();
}
How can I get the duration in seconds of this file?
Thanks in advance
---EDIT
I got it working, I called MediaPlayer.getduration() inside the MediaPlayer.setOnPreparedListener() method so it returned 0.
MediaMetadataRetriever is a lightweight and efficient way to do this. MediaPlayer is too heavy and could arise performance issue in high performance environment like scrolling, paging, listing, etc.
Furthermore, Error (100,0) could happen on MediaPlayer since it's a heavy and sometimes restart needs to be done again and again.
Uri uri = Uri.parse(pathStr);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(AppContext.getAppContext(),uri);
String durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int millSecond = Integer.parseInt(durationStr);
The quickest way to do is via MediaMetadataRetriever. However, there is a catch
if you use URI and context to set data source you might encounter bug
https://code.google.com/p/android/issues/detail?id=35794
Solution is use absolute path of file to retrieve metadata of media file.
Below is the code snippet to do so
private static String getDuration(File file) {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(file.getAbsolutePath());
String durationStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
return Utils.formateMilliSeccond(Long.parseLong(durationStr));
}
Now you can convert millisecond to human readable format using either of below formats
/**
* Function to convert milliseconds time to
* Timer Format
* Hours:Minutes:Seconds
*/
public static String formateMilliSeccond(long milliseconds) {
String finalTimerString = "";
String secondsString = "";
// Convert total duration into time
int hours = (int) (milliseconds / (1000 * 60 * 60));
int minutes = (int) (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
int seconds = (int) ((milliseconds % (1000 * 60 * 60)) % (1000 * 60) / 1000);
// Add hours if there
if (hours > 0) {
finalTimerString = hours + ":";
}
// Prepending 0 to seconds if it is one digit
if (seconds < 10) {
secondsString = "0" + seconds;
} else {
secondsString = "" + seconds;
}
finalTimerString = finalTimerString + minutes + ":" + secondsString;
// return String.format("%02d Min, %02d Sec",
// TimeUnit.MILLISECONDS.toMinutes(milliseconds),
// TimeUnit.MILLISECONDS.toSeconds(milliseconds) -
// TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliseconds)));
// return timer string
return finalTimerString;
}
Either try this to get duration in milliseconds:
MediaPlayer mp = MediaPlayer.create(yourActivity, Uri.parse(pathofyourrecording));
int duration = mp.getDuration();
Or measure the time elapsed from recorder.start() till recorder.stop() in nanoseconds:
long startTime = System.nanoTime();
// ... do recording ...
long estimatedTime = System.nanoTime() - startTime;
Try use
long totalDuration = mediaPlayer.getDuration(); // to get total duration in milliseconds
long currentDuration = mediaPlayer.getCurrentPosition(); // to Gets the current playback position in milliseconds
Division on 1000 to convert to seconds.
Hope this helped you.
If the audio is from url, just wait for on prepared:
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
length = mp.getDuration();
}
});
Kotlin Extension Solution
You can add this to reliably and safely get your audio file's duration. If it doesn't exist or there is an error, you'll get back 0.
myAudioFile.getMediaDuration(context)
/**
* If file is a Video or Audio file, return the duration of the content in ms
*/
fun File.getMediaDuration(context: Context): Long {
if (!exists()) return 0
val retriever = MediaMetadataRetriever()
return try {
retriever.setDataSource(context, uri)
val duration = retriever.extractMetadata(METADATA_KEY_DURATION)
retriever.release()
duration.toLongOrNull() ?: 0
} catch (exception: Exception) {
0
}
}
If you are regularly working with String or Uri for files, I'd suggest also adding these useful helpers
fun Uri.asFile(): File = File(toString())
fun String?.asUri(): Uri? {
try {
return Uri.parse(this)
} catch (e: Exception) {
Sentry.captureException(e)
}
return null
}
fun String.asFile() = File(this)
According to Vijay's answer, The function gives us the duration of the audio/video file but unfortunately, there is an issue of a run time exception so I sorted out and below function work properly and return the exact duration of the audio or video file.
public String getAudioFileLength(String path, boolean stringFormat) {
StringBuilder stringBuilder = new StringBuilder();
try {
Uri uri = Uri.parse(path);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(HomeActivity.this, uri);
String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int millSecond = Integer.parseInt(duration);
if (millSecond < 0) return String.valueOf(0); // if some error then we say duration is zero
if (!stringFormat) return String.valueOf(millSecond);
int hours, minutes, seconds = millSecond / 1000;
hours = (seconds / 3600);
minutes = (seconds / 60) % 60;
seconds = seconds % 60;
if (hours > 0 && hours < 10) stringBuilder.append("0").append(hours).append(":");
else if (hours > 0) stringBuilder.append(hours).append(":");
if (minutes < 10) stringBuilder.append("0").append(minutes).append(":");
else stringBuilder.append(minutes).append(":");
if (seconds < 10) stringBuilder.append("0").append(seconds);
else stringBuilder.append(seconds);
}catch (Exception e){
e.printStackTrace();
}
return stringBuilder.toString();
}
:)
You can use this readyMade method, hope this helps someone.
Example 1 : getAudioFileLength(address, true); // if you want in stringFormat
Example 2 : getAudioFileLength(address, false); // if you want in milliseconds
public String getAudioFileLength(String path, boolean stringFormat) {
Uri uri = Uri.parse(path);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(Filter_Journals.this, uri);
String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int millSecond = Integer.parseInt(duration);
if (millSecond < 0) return String.valueOf(0); // if some error then we say duration is zero
if (!stringFormat) return String.valueOf(millSecond);
int hours, minutes, seconds = millSecond / 1000;
hours = (seconds / 3600);
minutes = (seconds / 60) % 60;
seconds = seconds % 60;
StringBuilder stringBuilder = new StringBuilder();
if (hours > 0 && hours < 10) stringBuilder.append("0").append(hours).append(":");
else if (hours > 0) stringBuilder.append(hours).append(":");
if (minutes < 10) stringBuilder.append("0").append(minutes).append(":");
else stringBuilder.append(minutes).append(":");
if (seconds < 10) stringBuilder.append("0").append(seconds);
else stringBuilder.append(seconds);
return stringBuilder.toString();
}
For me, the AudioGraph class came to the rescue:
public static async Task<double> AudioFileDuration(StorageFile file)
{
var result = await AudioGraph.CreateAsync(new AudioGraphSettings(Windows.Media.Render.AudioRenderCategory.Speech));
if (result.Status == AudioGraphCreationStatus.Success)
{
AudioGraph audioGraph = result.Graph;
var fileInputNodeResult = await audioGraph.CreateFileInputNodeAsync(file);
return fileInputNodeResult.FileInputNode.Duration.TotalSeconds;
}
return -1;
}
Kotlin shortest way to do it (if it is an audiofile):
private fun getDuration(absolutePath: String): String {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(absolutePath)
//For format in string MM:SS
val rawDuration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
val duration = rawDuration.milliseconds
return format("%02d:%02d", duration.inWholeMinutes, duration.inWholeSeconds % 60)
}
private fun getDurationInSeconds(absolutePath: String): Long {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(absolutePath)
//Return only value in seconds
val rawDuration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
return rawDuration.milliseconds.inWholeSeconds
}
After you write the file, open it up in a MediaPlayer, and call getDuration on it.
Have you looked at Ringdroid?. It's pretty light weight and the integration is straight forward. It works well with VBR media files as well.
For your problem with getting the duration, you might want to do something like below using Ringdroid.
public class AudioUtils
{
public static long getDuration(CheapSoundFile cheapSoundFile)
{
if( cheapSoundFile == null)
return -1;
int sampleRate = cheapSoundFile.getSampleRate();
int samplesPerFrame = cheapSoundFile.getSamplesPerFrame();
int frames = cheapSoundFile.getNumFrames();
cheapSoundFile = null;
return 1000 * ( frames * samplesPerFrame) / sampleRate;
}
public static long getDuration(String mediaPath)
{
if( mediaPath != null && mediaPath.length() > 0)
try
{
return getDuration(CheapSoundFile.create(mediaPath, null));
}catch (FileNotFoundException e){}
catch (IOException e){}
return -1;
}
}
Hope that helps
It's simply. use RandomAccessFile Below is the code snippet to do so
public static int getAudioInfo(File file) {
try {
byte header[] = new byte[12];
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
randomAccessFile.readFully(header, 0, 8);
randomAccessFile.close();
return (int) file.length() /1000;
} catch (Exception e) {
return 0;
}
}
You can, of course, be more complete depending on your needs
I am new to android.I am having an app which can be used for exam management.Users can insert the date of every subjects and the app will provide time(in hours) for each subject.There is countdown timer which will start from the time allocated for each subject(for eg,48 hours) and should go to zero.when it hits zero an alarm should be played showing that time allocated for that subject has got over.this is my code.
public class Alarmpage extends Activity{
TextView hourshow,minshow,secshow;
SQLiteDatabase database_read;
sampleDatabase samp;
Cursor cur;
Handler handler;
int initStart;
Runnable updater;
int t;
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.alarm);
hourshow=(TextView)findViewById(R.id.hr);
samp=new sampleDatabase(getApplicationContext());
database_read=samp.getReadableDatabase();
minshow=(TextView)findViewById(R.id.min);
secshow=(TextView)findViewById(R.id.sec);
cur=database_read.query(sampleDatabase.TABLE_SEC, null, null, null, null, null, null);
}
public void onResume() {
super.onResume();
if(cur.moveToFirst())
{
t=cur.getInt(3);
Log.e("time", String.valueOf(t));
}
handler=new Handler();
initStart = (int) SystemClock.elapsedRealtime();
Log.e("init", String.valueOf(initStart));
updater = new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
int sec,minute,hour;
int diff = t*60*60;
Log.e("time in sec", String.valueOf(diff));
System.out.println(diff);
/*if(diff>=1)
{
hour= diff/3600;
}
else {hour = 00; }
hourshow.setText(String.valueOf(hour));
Log.e("diff after hour", String.valueOf(diff));
Log.e("hour", String.valueOf(hour));
minute = (diff % 3600) / 60;
minshow.setText(":"+String.valueOf(minute));
sec= (diff % 60);
secshow.setText(":"+String.valueOf(sec));*/
if (diff >= 1) {
sec = (int) (diff%60);
} else {
sec=00;
}
secshow.setText(":"+String.valueOf(sec));
diff = diff/60;
if (diff >= 1) {
minute = (int) (diff%60);
} else {
minute=00;
}
minshow.setText(":"+String.valueOf(minute));
diff = diff/60;
if (diff >= 1) {
hour = (int) (diff%24);
} else {hour = 00; }
hourshow.setText(String.valueOf(hour));
t=(t*60*60)-1;
handler.postDelayed(this, 1000);
}
};
handler.post(updater);
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
handler.removeCallbacks(updater);
}
}
but its showing some values.but they are not correct.kindly help me.I dont understand error in this code.Thanks in advance