/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fm.last.android.player;
import android.content.Context;
import android.media.AudioManager;
import android.util.Log;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MusicPlayerFocusHelper {
private static final String TAG = "MusicPlayerFocusHelper";
private AudioManager mAM;
private MusicFocusable mMusicFocusable;
private Object mAudioFocusChangeListener;
private boolean mAudioFocusLost = false;
public MusicPlayerFocusHelper(Context context, MusicFocusable musicFocusable) {
if (sClassOnAudioFocusChangeListener != null) {
mAM = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mMusicFocusable = musicFocusable;
createAudioFocusChangeListener();
}
}
public boolean isSupported() {
return (sClassOnAudioFocusChangeListener != null);
}
public void requestMusicFocus() {
requestAudioFocusCompat(mAM, mAudioFocusChangeListener,
AudioManager.STREAM_MUSIC, AUDIOFOCUS_GAIN);
}
public void abandonMusicFocus() {
abandonAudioFocusCompat(mAM, mAudioFocusChangeListener);
}
public void createAudioFocusChangeListener() {
if (sClassOnAudioFocusChangeListener == null)
return;
mAudioFocusChangeListener = Proxy.newProxyInstance(AudioManager.class.getClassLoader(),
new Class[]{ sClassOnAudioFocusChangeListener },
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) {
if (!method.getName().equals("onAudioFocusChange"))
return null;
int focusChange = (Integer) args[0];
Log.i(TAG, "Audio focus change: " + Integer.toString(focusChange));
if (focusChange == AUDIOFOCUS_GAIN) {
if (mAudioFocusLost) {
mMusicFocusable.focusGained();
mAudioFocusLost = false;
}
} else if (focusChange == AUDIOFOCUS_LOSS) {
mAudioFocusLost = true;
mMusicFocusable.focusLost(false, false);
} else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT) {
mAudioFocusLost = true;
mMusicFocusable.focusLost(true, false);
} else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
mAudioFocusLost = true;
mMusicFocusable.focusLost(true, true);
}
return null;
}
});
}
// Backwards compatibility code (methods available as of SDK Level 8)
static {
initializeStaticCompat();
}
@SuppressWarnings("rawtypes" )
static Class sClassOnAudioFocusChangeListener;
static Method sMethodRequestAudioFocus;
static Method sMethodAbandonAudioFocus;
static int AUDIOFOCUS_GAIN;
static int AUDIOFOCUS_GAIN_TRANSIENT;
static int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
static int AUDIOFOCUS_LOSS;
static int AUDIOFOCUS_LOSS_TRANSIENT;
static int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
private static void initializeStaticCompat() {
try {
sClassOnAudioFocusChangeListener = Class.forName("android.media.AudioManager$OnAudioFocusChangeListener");
sMethodRequestAudioFocus = AudioManager.class.getMethod(
"requestAudioFocus",
new Class[] { sClassOnAudioFocusChangeListener, int.class, int.class });
sMethodAbandonAudioFocus = AudioManager.class.getMethod(
"abandonAudioFocus",
new Class[] { sClassOnAudioFocusChangeListener });
AUDIOFOCUS_GAIN = AudioManager.class.getField("AUDIOFOCUS_GAIN").getInt(null);
AUDIOFOCUS_GAIN_TRANSIENT = AudioManager.class.getField("AUDIOFOCUS_GAIN_TRANSIENT").getInt(null);
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = AudioManager.class.getField("AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK").getInt(null);
AUDIOFOCUS_LOSS = AudioManager.class.getField("AUDIOFOCUS_LOSS").getInt(null);
AUDIOFOCUS_LOSS_TRANSIENT = AudioManager.class.getField("AUDIOFOCUS_LOSS_TRANSIENT").getInt(null);
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = AudioManager.class.getField("AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK").getInt(null);
} catch (ClassNotFoundException e) {
// Silently fail when running on an OS before SDK level 8.
} catch (NoSuchMethodException e) {
// Silently fail when running on an OS before SDK level 8.
} catch (NoSuchFieldException e) {
// Silently fail when running on an OS before SDK level 8.
} catch (IllegalArgumentException e) {
// Silently fail when running on an OS before SDK level 8.
} catch (SecurityException e) {
// Silently fail when running on an OS before SDK level 8.
} catch (IllegalAccessException e) {
// Silently fail when running on an OS before SDK level 8.
}
}
private static void requestAudioFocusCompat(AudioManager audioManager,
Object focusChangeListener, int stream, int durationHint) {
Log.i(TAG, "reg is null?: " + Boolean.toString(sMethodRequestAudioFocus == null));
if (sMethodRequestAudioFocus == null)
return;
try {
Object[] args = new Object[3];
args[0] = focusChangeListener;
args[1] = stream;
args[2] = durationHint;
sMethodRequestAudioFocus.invoke(audioManager, args);
} catch (InvocationTargetException e) {
// Unpack original exception when possible
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
// Unexpected checked exception; wrap and re-throw
throw new RuntimeException(e);
}
} catch (IllegalAccessException e) {
Log.e(TAG, "IllegalAccessException invoking requestAudioFocus.");
e.printStackTrace();
}
}
private static void abandonAudioFocusCompat(AudioManager audioManager,
Object focusChangeListener) {
if (sMethodAbandonAudioFocus == null)
return;
try {
sMethodAbandonAudioFocus.invoke(audioManager, focusChangeListener);
} catch (InvocationTargetException e) {
// Unpack original exception when possible
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
// Unexpected checked exception; wrap and re-throw
throw new RuntimeException(e);
}
} catch (IllegalAccessException e) {
Log.e(TAG, "IllegalAccessException invoking abandonAudioFocus.");
e.printStackTrace();
}
}
}