InfiniTimeOrg / InfiniTime

Firmware for Pinetime smartwatch written in C++ and based on FreeRTOS
GNU General Public License v3.0
2.75k stars 941 forks source link

Forward or Rewind Media by 5 Seconds Option in the Music App #1320

Open dyllan500 opened 2 years ago

dyllan500 commented 2 years ago

Verification

Pitch us your idea!

Forward or Rewind Media By 5 Seconds

Description

Hello Everyone,

I have been enjoying my PineTime for a while now. I especially love using the media controls in the Music app. I did have a great idea earlier about having the ability to fast forward or rewind my media by 5 seconds. I think it maybe fairly easy to implement. I haven't ever contributed to an open source project before, so I am not sure how to go about this. I hoping with some guidance I may be able implement this code myself. I have listed what I would like the music app to do and the research I did on how to implement this below.

My vision for the Music App is:

Default Buttons: Previous_Track_Button < Pause/Play_Button > Next_Track_Button Volume Buttons: Volume_Down_Button < Pause/Play_Button > Volume_Up_Button Forward/Rewind Buttons: Rewind_Button < Pause/Play_Button > Forward_Button

When the user opens the music app the default buttons are shown, when they swipe up the volume buttons show, if they swipe up again the Forward/Rewind Buttons show, if the swipe up again then the default buttons are shown. Swiping Down will do the reverse. So swiping up or down just cycles through the various buttons.

My Research:

I looked through the infiniTime code and I think it will be fairly easy to implement this.

I think the only code that needs to be added to MusicService.h is 2 more events.

MusicService.h

static const char EVENT_MUSIC_OPEN = 0xe0;
static const char EVENT_MUSIC_PLAY = 0x00;
static const char EVENT_MUSIC_PAUSE = 0x01;
static const char EVENT_MUSIC_NEXT = 0x03;
static const char EVENT_MUSIC_PREV = 0x04;
static const char EVENT_MUSIC_VOLUP = 0x05;
static const char EVENT_MUSIC_VOLDOWN = 0x06;
static const char EVENT_MUSIC_FORWARD = 0x07;
static const char EVENT_MUSIC_REWIND = 0x08;

But for Music.cpp code for the Forward and Rewind buttons will needed to be added. And a way to tell which buttons need to be displayed when the user swipes up or down. I think this code is fairly easy to add.

Also in my research I have noticed that GadgetBridge, which is the companion app that I use, has the ability to Forward and Rewind. So I think my idea wouldn't be too hard to implement for GadgetBridge users. At the moment I am not sure what GadgetBridge's Forward and Rewind do I am just hoping that it moves the media playback by a few seconds like I want. As for other companion apps I am not too sure how hard it will be to implement this idea for those.

The code in GadgetBridge that shows they have Forward and Rewind implemented.

GBDeviceEventMusicControl.java

public class GBDeviceEventMusicControl extends GBDeviceEvent {
    public Event event = Event.UNKNOWN;

    public enum Event {
        UNKNOWN,
        PLAY,
        PAUSE,
        PLAYPAUSE,
        NEXT,
        PREVIOUS,
        VOLUMEUP,
        VOLUMEDOWN,
        FORWARD,
        REWIND
    }
}

I believe only this code needs to be inserted into the onCharacteristicChanged function in the PineTimeJFSupport.java file.

case 7:
  deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.FORWARD;
  break;
case 8:
  deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.REWIND;
    break;

PineTimeJFSupport.java

public boolean onCharacteristicChanged(BluetoothGatt gatt,
                                           BluetoothGattCharacteristic characteristic) {
        if (super.onCharacteristicChanged(gatt, characteristic)) {
            return true;
        }

        UUID characteristicUUID = characteristic.getUuid();
        if (characteristicUUID.equals(PineTimeJFConstants.UUID_CHARACTERISTICS_MUSIC_EVENT)) {
            byte[] value = characteristic.getValue();
            GBDeviceEventMusicControl deviceEventMusicControl = new GBDeviceEventMusicControl();

            switch (value[0]) {
                case 0:
                    deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PLAY;
                    break;
                case 1:
                    deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PAUSE;
                    break;
                case 3:
                    deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.NEXT;
                    break;
                case 4:
                    deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PREVIOUS;
                    break;
                case 5:
                    deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.VOLUMEUP;
                    break;
                case 6:
                    deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.VOLUMEDOWN;
                    break;
                default:
                    return false;
            }
            evaluateGBDeviceEvent(deviceEventMusicControl);
            return true;
        } else if (characteristicUUID.equals(PineTimeJFConstants.UUID_CHARACTERISTIC_ALERT_NOTIFICATION_EVENT)) {
            byte[] value = characteristic.getValue();
            GBDeviceEventCallControl deviceEventCallControl = new GBDeviceEventCallControl();
            switch (value[0]) {
                case 0:
                    deviceEventCallControl.event = GBDeviceEventCallControl.Event.REJECT;
                    break;
                case 1:
                    deviceEventCallControl.event = GBDeviceEventCallControl.Event.ACCEPT;
                    break;
                case 2:
                    deviceEventCallControl.event = GBDeviceEventCallControl.Event.IGNORE;
                    break;
                default:
                    return false;
            }
            evaluateGBDeviceEvent(deviceEventCallControl);
            return true;
        } else if (characteristicUUID.equals(PineTimeJFConstants.UUID_CHARACTERISTIC_MOTION_STEP_COUNT)) {
            int steps = BLETypeConversions.toUint32(characteristic.getValue());
            if (LOG.isDebugEnabled()) {
                LOG.debug("onCharacteristicChanged: MotionService:Steps=" + steps);
            }
            onReceiveStepsSample(steps);
            return true;
        }

        LOG.info("Unhandled characteristic changed: " + characteristicUUID);
        return false;
    }
dyllan500 commented 2 years ago

Okay I do some farther research and added my changes to gadgetbridge and InfiniTime to test my idea. I have found that GadgetBridge's implementation of Forward and Rewind will skip the media by 10 seconds, which is fine to me. But what I have also found is that not all apps have seem to have forward and rewind implemented like Spotify. I tried forward and rewind on both songs and podcast and it does not work. But it does work on VLC. I am not sure if I should implement something that only works on some apps or not maybe someone can help on that.

minacode commented 2 years ago

Hey, nice idea and good job working on it! While the companion app is obviously relevant for this all to work, this repo just cares about the side of the watch. So for now it might be enough to implement that the new commands are sent correctly via BLE. Of course, even contributing the other side to GadgetBridge is fantastic. And maybe you or someone else can then also do it for other companions as itd for example. If you submit a pull request with your changes (maybe as a draft for now) you can also get feedback on what you are doing if someone has time for it.