/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.media;
import android.app.Activity;
import android.content.Context;
import static android.content.Context.TELEPHONY_SERVICE;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.Looper;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.codename1.impl.android.AndroidImplementation;
import com.codename1.ui.Component;
import com.codename1.ui.Display;
import java.io.InputStream;
import java.util.Vector;
/**
*
* @author Chen
*/
public class Audio implements Runnable, com.codename1.media.Media, MediaPlayer.OnInfoListener, AudioManager.OnAudioFocusChangeListener {
private static final int MEDIA_INFO_BUFFERING_START = 701;
private static final int MEDIA_INFO_BUFFERING_END = 702;
private MediaPlayer player;
private Runnable onComplete;
private InputStream stream;
private int lastTime;
private int lastDuration;
private Activity activity;
private boolean buffering;
private boolean disposeOnComplete = true;
private int tempVolume = -1;
private static Vector currentPlayingAudio = new Vector();
private static PhoneStateListener phoneStateListener;
public Audio(Activity activity, MediaPlayer player, InputStream stream, Runnable onComplete) {
this.activity = activity;
this.player = player;
this.stream = stream;
this.onComplete = onComplete;
bindPlayerCleanupOnComplete();
AudioManager audioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
}
private void cleanVars() {
if (player != null) {
try {
player.release();
} catch (Throwable t) {
}
player = null;
if (stream != null) {
try {
stream.close();
} catch (Throwable t) {
}
stream = null;
}
System.gc();
}
}
public void run() {
if (player != null) {
cleanup();
cleanVars();
}
}
private void bindPlayerCleanupOnComplete() {
if(player == null) {
return;
}
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer arg0) {
if(disposeOnComplete){
run();
}
if (onComplete != null) {
Display.getInstance().callSerially(onComplete);
}
}
});
player.setOnErrorListener(new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer arg0, int arg1, int arg2) {
run();
return true;
}
});
}
@Override
public void cleanup() {
try {
if (player != null) {
if (player.isPlaying()) {
player.stop();
}
cleanVars();
}
} catch (Throwable t) {
t.printStackTrace();
}
}
@Override
public void prepare() {
AndroidImplementation.runOnUiThreadAndBlock(new Runnable() {
@Override
public void run() {
try {
if (player != null) {
player.prepare();
}
} catch(Throwable t) {
// some exceptions might occur here, with all the various illegal states they rarely matter
t.printStackTrace();
}
}
});
}
@Override
public void play() {
try {
if (player != null) {
player.start();
}
} catch(Throwable t) {
// some exceptions might occur here, with all the various illegal states they rarely matter
t.printStackTrace();
}
}
@Override
public void pause() {
try {
if (player != null) {
player.pause();
}
} catch(Throwable t) {
// some exceptions might occur here, with all the various illegal states they rarely matter
t.printStackTrace();
}
}
@Override
public int getTime() {
if (player == null) {
return lastTime;
}
try {
lastTime = player.getCurrentPosition();
return lastTime;
} catch (IllegalStateException err) {
// no idea???
//err.printStackTrace();
return lastTime;
}
}
@Override
public void setTime(int time) {
try {
if (player == null) {
return;
}
final boolean[] flag = new boolean[1];
player.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
public void onSeekComplete(MediaPlayer arg0) {
flag[0] = true;
}
});
if (player.isPlaying()) {
player.seekTo(time);
} else {
player.start();
player.seekTo(time);
player.pause();
}
} catch(Throwable t) {
// some exceptions might occur here, with all the various illegal states they rarely matter
t.printStackTrace();
}
}
@Override
public int getDuration() {
if (player == null) {
return lastDuration;
}
try {
int d = player.getDuration();
if (d == 0) {
return -1;
}
lastDuration = d;
} catch (IllegalStateException err) {
return -1;
}
return lastDuration;
}
@Override
public void setVolume(int vol) {
float v = ((float) vol) / 100.0F;
if (player != null) {
player.setVolume(v, v);
}
}
@Override
public int getVolume() {
AudioManager am = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
return am.getStreamVolume(AudioManager.STREAM_MUSIC);
}
@Override
public boolean isVideo() {
return false;
}
@Override
public boolean isFullScreen() {
return false;
}
@Override
public void setFullScreen(boolean fullScreen) {
}
@Override
public Component getVideoComponent() {
return null;
}
@Override
public void setNativePlayerMode(boolean nativePlayer) {
}
@Override
public boolean isNativePlayerMode() {
return false;
}
@Override
public boolean isPlaying() {
try {
return player != null && player.isPlaying() && !buffering;
} catch(Exception err) {
return false;
}
}
public void setVariable(String key, Object value) {
if(key != null){
if(key.equals("disposeOnComplete")){
if(value != null){
String v= value.toString();
disposeOnComplete = v.equalsIgnoreCase("true");
}
}
}
}
public Object getVariable(String key) {
if(key != null){
if(key.equals("disposeOnComplete")){
return "" + disposeOnComplete;
}
}
return null;
}
/**
* Allows us to detect buffering of media to return a better result in playback
*/
@Override
public boolean onInfo(MediaPlayer mp, int i, int i1) {
switch(i) {
case MEDIA_INFO_BUFFERING_START:
buffering = true;
break;
case MEDIA_INFO_BUFFERING_END:
buffering = false;
break;
}
return false;
}
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback
if (!isPlaying() && player != null) {
player.start();
if(tempVolume > -1) {
setVolume(tempVolume);
tempVolume = -1;
}
}
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media player
cleanup();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
Log.d("CN1", "AUDIOFOCUS_LOSS_TRANSIENT");
// Lost focus for a short time, but we have to stop
// playback. We don't release the media player because playback
// is likely to resume
if (isPlaying()) {
pause();
}
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
if (isPlaying()) {
tempVolume = getVolume();
setVolume(10);
}
break;
}
}
}