Skip to content

Commit

Permalink
Key binding settings
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyPavlenko committed Jul 16, 2023
1 parent b2e49eb commit 5842af6
Show file tree
Hide file tree
Showing 22 changed files with 878 additions and 525 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ext {
def abi = project.properties['ABI']
VERSION_CODE = 221
VERSION_CODE = 225
VERSION_NAME = "1.9.3"
SDK_MIN_VERSION = 23
SDK_TARGET_VERSION = 33
Expand Down
142 changes: 142 additions & 0 deletions fermata/src/main/java/me/aap/fermata/action/Action.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package me.aap.fermata.action;

import static android.media.AudioManager.ADJUST_LOWER;
import static android.media.AudioManager.ADJUST_RAISE;
import static android.media.AudioManager.ADJUST_TOGGLE_MUTE;
import static android.media.AudioManager.FLAG_SHOW_UI;
import static android.media.AudioManager.STREAM_MUSIC;
import static android.os.SystemClock.uptimeMillis;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;

import android.content.Context;
import android.media.AudioManager;

import androidx.annotation.Nullable;
import androidx.annotation.StringRes;

import java.util.List;

import me.aap.fermata.R;
import me.aap.fermata.media.service.MediaSessionCallback;
import me.aap.fermata.ui.activity.MainActivityDelegate;
import me.aap.utils.app.App;
import me.aap.utils.ui.activity.ActivityDelegate;

/**
* @author Andrey Pavlenko
*/
public enum Action {
STOP(R.string.action_stop, m(MediaSessionCallback::onStop)),
PLAY(R.string.action_play, m(MediaSessionCallback::onPlay)),
PAUSE(R.string.action_pause, m(MediaSessionCallback::onPause)),
PLAY_PAUSE(R.string.action_play_pause, m(cb -> {
if (cb.isPlaying()) cb.onPause();
else cb.onPlay();
})),
PREV(R.string.action_prev, m(MediaSessionCallback::onSkipToPrevious)),
NEXT(R.string.action_next, m(MediaSessionCallback::onSkipToNext)),
RW(R.string.action_rw, new RwFfHandler(false)),
FF(R.string.action_ff, new RwFfHandler(true)),
VOLUME_UP(R.string.action_vol_up, new VolumeHandler(ADJUST_RAISE)),
VOLUME_DOWN(R.string.action_vol_down, new VolumeHandler(ADJUST_LOWER)),
VOLUME_MUTE_UNMUTE(R.string.action_vol_mute_unmute, new VolumeHandler(ADJUST_TOGGLE_MUTE)),
ACTIVATE_VOICE_CTRL(R.string.action_activate_voice_ctrl,
m(cb -> cb.getAssistant().startVoiceAssistant())),
MENU(R.string.action_menu, a(a -> a.getNavBarMediator().showMenu(a))),
CP_MENU(R.string.action_cp_menu, a(a -> {
var cp = a.getControlPanel();
if (cp.isActive()) cp.showMenu();
})),
BACK_OR_EXIT(R.string.action_back_or_exit, a(ActivityDelegate::onBackPressed)),
EXIT(R.string.action_exit, a(ActivityDelegate::finish)),
NONE(R.string.action_none, m(cb -> {})),
;

private static final List<Action> all = unmodifiableList(asList(values()));

@StringRes
private final int name;
private final Action.Handler handler;

Action(int name, Action.Handler handler) {
this.name = name;
this.handler = handler;
}

@Nullable
public static Action get(int ordinal) {
return (ordinal >= 0) && (ordinal < all.size()) ? all.get(ordinal) : null;
}

public static List<Action> getAll() {
return all;
}

@StringRes
public int getName() {
return name;
}

public Handler getHandler() {
return handler;
}

private static Handler m(MediaHandler h) {
return h;
}

private static Handler a(ActivityHandler h) {
return h;
}

public interface Handler {
void handle(MediaSessionCallback cb, @Nullable MainActivityDelegate a, long timestamp);
}

private interface MediaHandler extends Handler {
void handle(MediaSessionCallback cb);

@Override
default void handle(MediaSessionCallback cb, @Nullable MainActivityDelegate a,
long timestamp) {
handle(cb);
}
}

private interface ActivityHandler extends Handler {
void handle(MainActivityDelegate a);

@Override
default void handle(MediaSessionCallback cb, @Nullable MainActivityDelegate a,
long timestamp) {
if (a != null) handle(a);
}
}

private static final class VolumeHandler implements Handler {
private final int direction;

VolumeHandler(int direction) {this.direction = direction;}

@Override
public void handle(MediaSessionCallback cb, @Nullable MainActivityDelegate a, long timestamp) {
var eng = cb.getEngine();
if ((eng != null) && eng.adjustVolume(direction)) return;
var ctx = (a == null) ? App.get() : a.getContext();
var amgr = (AudioManager) ctx.getSystemService(Context.AUDIO_SERVICE);
if (amgr != null) amgr.adjustStreamVolume(STREAM_MUSIC, direction, FLAG_SHOW_UI);
}
}

private static final class RwFfHandler implements Handler {
private final boolean ff;

private RwFfHandler(boolean ff) {this.ff = ff;}

@Override
public void handle(MediaSessionCallback cb, @Nullable MainActivityDelegate a, long timestamp) {
cb.rewindFastForward(ff, (int) ((uptimeMillis() - timestamp) / 1000));
}
}
}
148 changes: 148 additions & 0 deletions fermata/src/main/java/me/aap/fermata/action/Key.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package me.aap.fermata.action;

import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;

import android.view.KeyEvent;

import androidx.annotation.Nullable;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import me.aap.fermata.ui.activity.MainActivityPrefs;
import me.aap.utils.function.IntSupplier;
import me.aap.utils.pref.PreferenceStore;

/**
* @author Andrey Pavlenko
*/
public enum Key {
MEDIA_STOP(KeyEvent.KEYCODE_MEDIA_STOP, Action.STOP),
MEDIA_PLAY(KeyEvent.KEYCODE_MEDIA_PLAY, Action.PLAY),
MEDIA_PAUSE(KeyEvent.KEYCODE_MEDIA_PAUSE, Action.PAUSE),
MEDIA_PLAY_PAUSE(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, Action.PLAY_PAUSE),
MEDIA_PREVIOUS(KeyEvent.KEYCODE_MEDIA_PREVIOUS, Action.PREV),
MEDIA_NEXT(KeyEvent.KEYCODE_MEDIA_NEXT, Action.NEXT),
MEDIA_REWIND(KeyEvent.KEYCODE_MEDIA_REWIND, Action.RW, Action.RW, Action.RW),
MEDIA_FAST_FORWARD(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, Action.FF, Action.FF, Action.FF),
VOLUME_UP(KeyEvent.KEYCODE_VOLUME_UP, Action.VOLUME_UP),
VOLUME_DOWN(KeyEvent.KEYCODE_VOLUME_DOWN, Action.VOLUME_DOWN),
HEADSETHOOK(KeyEvent.KEYCODE_HEADSETHOOK, Action.PLAY_PAUSE, Action.NEXT,
Action.ACTIVATE_VOICE_CTRL),
SEARCH(KeyEvent.KEYCODE_SEARCH, Action.ACTIVATE_VOICE_CTRL),
BACK(KeyEvent.KEYCODE_BACK, Action.BACK_OR_EXIT),
ESCAPE(KeyEvent.KEYCODE_ESCAPE, Action.BACK_OR_EXIT),
DEL(KeyEvent.KEYCODE_DEL, Action.STOP),
MENU(KeyEvent.KEYCODE_MENU, Action.MENU, Action.CP_MENU, Action.CP_MENU),
M(KeyEvent.KEYCODE_M, Action.MENU, Action.CP_MENU, Action.CP_MENU),
P(KeyEvent.KEYCODE_P, Action.PLAY_PAUSE),
S(KeyEvent.KEYCODE_S, Action.STOP),
X(KeyEvent.KEYCODE_X, Action.EXIT);

private static final Map<Integer, Key> keys = new HashMap<>();

private static final List<Key> all = unmodifiableList(asList(values()));
private static final PrefsListener listener = new PrefsListener();

static {
for (var k : all) keys.put(k.code, k);
MainActivityPrefs.get().addBroadcastListener(listener);
}

private final int code;
private final boolean media;
private final PreferenceStore.Pref<IntSupplier> actionPref;
private final PreferenceStore.Pref<IntSupplier> dblActionPref;
private final PreferenceStore.Pref<IntSupplier> longActionPref;
@Nullable
private Action.Handler clickHandler;
@Nullable
private Action.Handler dblClickHandler;
@Nullable
private Action.Handler longClickHandler;

Key(int code, Action action) {
this(code, action, action, action);
}

Key(int code, Action action, Action dblAction, Action longAction) {
this.code = code;
var name = name();
media = name.startsWith("MEDIA_") || name.startsWith("VOLUME_");
actionPref =
PreferenceStore.Pref.i("KEY_ACTION_" + name, action.ordinal()).withInheritance(false);
dblActionPref = PreferenceStore.Pref.i("KEY_ACTION_DBL_" + name, dblAction.ordinal())
.withInheritance(false);
longActionPref = PreferenceStore.Pref.i("KEY_ACTION_LONG_" + name, longAction.ordinal())
.withInheritance(false);
}

@Nullable
public static Key get(int code) {
return keys.get(code);
}

public static List<Key> getAll() {
return all;
}

public static PreferenceStore getPrefs() {
return MainActivityPrefs.get();
}

public PreferenceStore.Pref<IntSupplier> getActionPref() {
return actionPref;
}

public PreferenceStore.Pref<IntSupplier> getDblActionPref() {
return dblActionPref;
}

public PreferenceStore.Pref<IntSupplier> getLongActionPref() {
return longActionPref;
}

@Nullable
public Action.Handler getClickHandler() {
if (clickHandler != null) return clickHandler;
var a = Action.get(getPrefs().getIntPref(actionPref));
return (a == null) ? null : (clickHandler = a.getHandler());
}

@Nullable
public Action.Handler getDblClickHandler() {
if (dblClickHandler != null) return dblClickHandler;
var a = Action.get(getPrefs().getIntPref(dblActionPref));
return (a == null) ? null : (dblClickHandler = a.getHandler());
}

@Nullable
public Action.Handler getLongClickHandler() {
if (longClickHandler != null) return longClickHandler;
var a = Action.get(getPrefs().getIntPref(longActionPref));
return (a == null) ? null : (longClickHandler = a.getHandler());
}

public boolean isMedia() {
return media;
}

public static final class PrefsListener implements PreferenceStore.Listener {

@Override
public void onPreferenceChanged(PreferenceStore store, List<PreferenceStore.Pref<?>> prefs) {
for (var p : prefs) {
if (p.getName().startsWith("KEY_ACTION_")) {
for (var k : Key.all) {
k.clickHandler = null;
k.dblClickHandler = null;
k.longClickHandler = null;
}
return;
}
}
}
}
}
Loading

0 comments on commit 5842af6

Please sign in to comment.