/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* 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 com.example.google.tv.anymotelibrary.client;
import android.content.Context;
import android.media.AudioManager;
import android.view.MotionEvent;
import com.example.google.tv.anymotelibrary.R;
/**
* The trackball logic.
*/
public final class TrackballHandler {
/**
* Direction of dpad events.
*/
enum Direction {
DOWN,
LEFT,
RIGHT,
UP
}
/**
* Modes this handler can be in.
*/
enum Mode {
DPAD,
SCROLL
}
/**
* Sound played on trackball Dpad event.
*/
private static final int SOUND_TRACKBALL = AudioManager.FX_KEY_CLICK;
/**
* Receives translated trackball events.
*/
interface Listener {
/**
* Called when a trackball event should be interpreted as a dpad event.
*
* @param direction represents the direction of the dpad event
*/
void onDirectionalEvent(Direction direction);
/**
* Called when a trackball event should be interpreted as a scrolling
* event.
*
* @param dx scrolling delta along the horizontal axis
* @param dy scrolling delta along the vertical axis
*/
void onScrollEvent(int dx, int dy);
/**
* Called when the trackball was clicked.
*/
void onClick();
}
private final Listener listener;
/**
* Parameter that controls the threshold for the amount of trackball motion
* needed to generate a directional event.
*/
private final float dpadThreshold;
private final int scrollAmount;
/**
* {@code true} if trackball events should be handled.
*/
private boolean enabled;
/**
* Mode this handler is currently in.
*/
private Mode mode;
/**
* Used to store the amounts of trackball motion observed.
*/
private float dpadAccuX, dpadAccuY;
/**
* Plays some sounds on trackball events.
*/
private AudioManager audioManager;
TrackballHandler(Listener listener, Context context) {
this.listener = listener;
this.mode = Mode.SCROLL;
dpadThreshold = (float) context.getResources().getInteger(
R.integer.dpad_threshold) / 100;
scrollAmount = context.getResources().getInteger(
R.integer.scroll_amount);
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setMode(Mode mode) {
this.mode = mode;
}
public void setAudioManager(AudioManager audioManager) {
this.audioManager = audioManager;
}
/**
* Should be called on every trackball event.
*/
public boolean onTrackballEvent(MotionEvent event) {
if (!enabled) {
return false;
}
switch (mode) {
case DPAD:
return onDpad(event);
case SCROLL:
return onScroll(event);
default:
return false;
}
}
/**
* Called to interpret an event as a scrolling event.
*/
private boolean onScroll(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
int deltaX = (int) (scrollAmount * event.getX()
* event.getXPrecision());
int deltaY = (int) (scrollAmount * event.getY()
* event.getYPrecision());
listener.onScrollEvent(deltaX, deltaY);
return true;
}
return false;
}
/**
* Called to interpret an event as a dpad event.
*/
private boolean onDpad(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
playSoundOnDPad();
listener.onClick();
resetMotionAccumulators();
return true;
}
dpadAccuX += event.getX();
dpadAccuY += event.getY();
if (Math.abs(dpadAccuX) > dpadThreshold) {
playSoundOnDPad();
listener.onDirectionalEvent(
dpadAccuX > 0 ? Direction.RIGHT : Direction.LEFT);
resetMotionAccumulators();
}
if (Math.abs(dpadAccuY) > dpadThreshold) {
playSoundOnDPad();
listener.onDirectionalEvent(
dpadAccuY > 0 ? Direction.DOWN : Direction.UP);
resetMotionAccumulators();
}
return true;
}
private void resetMotionAccumulators() {
dpadAccuX = 0;
dpadAccuY = 0;
}
/**
* Plays a sound when a Dpad event is performed.
*/
private void playSoundOnDPad() {
if (audioManager != null) {
audioManager.playSoundEffect(SOUND_TRACKBALL);
}
}
}