diff options
author | John Du <johnldu@google.com> | 2013-06-27 18:39:42 -0700 |
---|---|---|
committer | John Du <johnldu@google.com> | 2013-08-15 18:52:29 +0000 |
commit | ace834feb02adabd61f628c4471147aea02d939c (patch) | |
tree | ebd083e1abb02412b582db000c53e3ad91f6b920 | |
parent | 8c85057a003441674807e2991092735011c72b26 (diff) | |
download | android_packages_apps_Bluetooth-ace834feb02adabd61f628c4471147aea02d939c.tar.gz android_packages_apps_Bluetooth-ace834feb02adabd61f628c4471147aea02d939c.tar.bz2 android_packages_apps_Bluetooth-ace834feb02adabd61f628c4471147aea02d939c.zip |
Add support for ff/rew
Change-Id: I7ec60d94313b9ba5e4e8e62c82f19ae332b6fdd3
(cherry picked from commit 55123eff985f4d15ec198569a5db895ea086447a)
-rw-r--r-- | jni/com_android_bluetooth_avrcp.cpp | 20 | ||||
-rwxr-xr-x | src/com/android/bluetooth/a2dp/Avrcp.java | 93 |
2 files changed, 112 insertions, 1 deletions
diff --git a/jni/com_android_bluetooth_avrcp.cpp b/jni/com_android_bluetooth_avrcp.cpp index 6ff5cfa84..7406fcd05 100644 --- a/jni/com_android_bluetooth_avrcp.cpp +++ b/jni/com_android_bluetooth_avrcp.cpp @@ -29,6 +29,7 @@ namespace android { static jmethodID method_getPlayStatus; static jmethodID method_getElementAttr; static jmethodID method_registerNotification; +static jmethodID method_handlePassthroughCmd; static const btrc_interface_t *sBluetoothAvrcpInterface = NULL; static jobject mCallbacksObj = NULL; @@ -92,6 +93,19 @@ static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uin checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); } +static void btavrcp_passthrough_command_callback(int id, int pressed) { + ALOGI("%s", __FUNCTION__); + + if (!checkCallbackThread()) { + ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); + return; + } + + sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd, (jint)id, + (jint)pressed); + checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); +} + static btrc_callbacks_t sBluetoothAvrcpCallbacks = { sizeof(sBluetoothAvrcpCallbacks), btavrcp_get_play_status_callback, @@ -102,7 +116,8 @@ static btrc_callbacks_t sBluetoothAvrcpCallbacks = { NULL, NULL, btavrcp_get_element_attr_callback, - btavrcp_register_notification_callback + btavrcp_register_notification_callback, + btavrcp_passthrough_command_callback }; static void classInitNative(JNIEnv* env, jclass clazz) { @@ -115,6 +130,9 @@ static void classInitNative(JNIEnv* env, jclass clazz) { method_registerNotification = env->GetMethodID(clazz, "registerNotification", "(II)V"); + method_handlePassthroughCmd = + env->GetMethodID(clazz, "handlePassthroughCmd", "(II)V"); + ALOGI("%s: succeeds", __FUNCTION__); } diff --git a/src/com/android/bluetooth/a2dp/Avrcp.java b/src/com/android/bluetooth/a2dp/Avrcp.java index ac7a323e0..73b6df698 100755 --- a/src/com/android/bluetooth/a2dp/Avrcp.java +++ b/src/com/android/bluetooth/a2dp/Avrcp.java @@ -16,6 +16,9 @@ package com.android.bluetooth.a2dp; +import java.util.Timer; +import java.util.TimerTask; + import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -73,17 +76,33 @@ final class Avrcp { private int mPlayPosChangedNT; private long mNextPosMs; private long mPrevPosMs; + private long mSkipStartTime; + private Timer mTimer; + + /* AVRC IDs from avrc_defs.h */ + private static final int AVRC_ID_REWIND = 0x48; + private static final int AVRC_ID_FAST_FOR = 0x49; private static final int MESSAGE_GET_PLAY_STATUS = 1; private static final int MESSAGE_GET_ELEM_ATTRS = 2; private static final int MESSAGE_REGISTER_NOTIFICATION = 3; private static final int MESSAGE_PLAY_INTERVAL_TIMEOUT = 4; + private static final int MESSAGE_FAST_FORWARD = 5; + private static final int MESSAGE_REWIND = 6; + private static final int MESSAGE_FF_REW_TIMEOUT = 7; private static final int MSG_UPDATE_STATE = 100; private static final int MSG_SET_METADATA = 101; private static final int MSG_SET_TRANSPORT_CONTROLS = 102; private static final int MSG_SET_ARTWORK = 103; private static final int MSG_SET_GENERATION_ID = 104; + private static final int BUTTON_TIMEOUT_TIME = 2000; + private static final int BASE_SKIP_AMOUNT = 2000; + private static final int KEY_STATE_PRESS = 1; + private static final int KEY_STATE_RELEASE = 0; + private static final int SKIP_PERIOD = 400; + private static final int SKIP_DOUBLE_INTERVAL = 3000; + static { classInitNative(); } @@ -99,6 +118,7 @@ final class Avrcp { mSongLengthMs = 0L; mPlaybackIntervalMs = 0L; mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED; + mTimer = null; mContext = context; @@ -262,6 +282,45 @@ final class Avrcp { registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)getPlayPosition()); break; + case MESSAGE_FAST_FORWARD: + case MESSAGE_REWIND: + final int skipAmount; + if (msg.what == MESSAGE_FAST_FORWARD) { + if (DEBUG) Log.v(TAG, "MESSAGE_FAST_FORWARD"); + skipAmount = BASE_SKIP_AMOUNT; + } else { + if (DEBUG) Log.v(TAG, "MESSAGE_REWIND"); + skipAmount = -BASE_SKIP_AMOUNT; + } + + removeMessages(MESSAGE_FF_REW_TIMEOUT); + if (msg.arg1 == KEY_STATE_PRESS) { + if (mTimer == null) { + /** Begin fast forwarding */ + mSkipStartTime = SystemClock.elapsedRealtime(); + TimerTask task = new TimerTask() { + @Override + public void run() { + changePositionBy(skipAmount*getSkipMultiplier()); + } + }; + mTimer = new Timer(); + mTimer.schedule(task, 0, SKIP_PERIOD); + } + sendMessageDelayed(obtainMessage(MESSAGE_FF_REW_TIMEOUT), BUTTON_TIMEOUT_TIME); + } else if (msg.arg1 == KEY_STATE_RELEASE && mTimer != null) { + mTimer.cancel(); + mTimer = null; + } + break; + + case MESSAGE_FF_REW_TIMEOUT: + if (DEBUG) Log.v(TAG, "MESSAGE_FF_REW_TIMEOUT: FF/REW response timed out"); + if (mTimer != null) { + mTimer.cancel(); + mTimer = null; + } + break; } } } @@ -422,6 +481,40 @@ final class Avrcp { } } + private void handlePassthroughCmd(int id, int keyState) { + switch (id) { + case AVRC_ID_REWIND: + rewind(keyState); + break; + case AVRC_ID_FAST_FOR: + fastForward(keyState); + break; + } + } + + private void fastForward(int keyState) { + Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0); + mHandler.sendMessage(msg); + } + + private void rewind(int keyState) { + Message msg = mHandler.obtainMessage(MESSAGE_REWIND, keyState, 0); + mHandler.sendMessage(msg); + } + + private void changePositionBy(long amount) { + long currentPosMs = getPlayPosition(); + if (currentPosMs == -1L) return; + long newPosMs = Math.max(0L, currentPosMs + amount); + mAudioManager.setRemoteControlClientPlaybackPosition(mClientGeneration, + newPosMs); + } + + private int getSkipMultiplier() { + long currentTime = SystemClock.elapsedRealtime(); + return (int) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL); + } + private void sendTrackChangedRsp() { byte[] track = new byte[TRACK_ID_SIZE]; /* track is stored in big endian format */ |