diff --git a/app/build.gradle b/app/build.gradle index 64fa99c1..2ac98b95 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { minSdkVersion 16 targetSdkVersion 21 - versionName "3.0.3" - versionCode = 49 + versionName "3.1-beta1" + versionCode = 50 } productFlavors { diff --git a/app/libs/limelight-common.jar b/app/libs/limelight-common.jar index db5404c7..c248abce 100644 Binary files a/app/libs/limelight-common.jar and b/app/libs/limelight-common.jar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1dcb1ae1..a1aaa94b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,7 +68,7 @@ android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize|layoutDirection" > + android:value="com.limelight.AppView" /> arg0, View arg1, int pos, - long id) { - AppObject app = (AppObject) appGridAdapter.getItem(pos); - if (app == null || app.app == null) { - return; - } - // Only open the context menu if something is running, otherwise start it - if (getRunningAppId() != -1) { - openContextMenu(arg1); - } else { - doStart(app.app); - } - } - }); - registerForContextMenu(appGrid); + getFragmentManager().beginTransaction() + .add(R.id.appFragmentContainer, new AdapterFragment()).commitAllowingStateLoss(); } @Override @@ -283,8 +283,37 @@ public class AppView extends Activity { } }).start(); } - - public class AppObject { + + @Override + public int getAdapterFragmentLayoutId() { + return PreferenceConfiguration.readPreferences(this).listMode ? + R.layout.list_view : R.layout.app_grid_view; + } + + @Override + public void receiveAbsListView(AbsListView listView) { + listView.setAdapter(appGridAdapter); + listView.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView arg0, View arg1, int pos, + long id) { + AppObject app = (AppObject) appGridAdapter.getItem(pos); + if (app == null || app.app == null) { + return; + } + + // Only open the context menu if something is running, otherwise start it + if (getRunningAppId() != -1) { + openContextMenu(arg1); + } else { + doStart(app.app); + } + } + }); + registerForContextMenu(listView); + } + + public class AppObject { public NvApp app; public AppObject(NvApp app) { diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index baabc55e..90c58042 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -17,18 +17,23 @@ import com.limelight.nvstream.av.video.VideoDecoderRenderer; import com.limelight.nvstream.input.KeyboardPacket; import com.limelight.nvstream.input.MouseButtonPacket; import com.limelight.preferences.PreferenceConfiguration; +import com.limelight.ui.GameGestures; import com.limelight.utils.Dialog; import com.limelight.utils.SpinnerDialog; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Configuration; import android.graphics.Point; import android.media.AudioManager; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; +import android.os.SystemClock; +import android.preference.PreferenceManager; import android.view.Display; import android.view.InputDevice; import android.view.KeyEvent; @@ -43,12 +48,15 @@ import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; +import android.view.inputmethod.InputMethodManager; import android.widget.Toast; +import java.util.Locale; + public class Game extends Activity implements SurfaceHolder.Callback, OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener, - OnSystemUiVisibilityChangeListener + OnSystemUiVisibilityChangeListener, GameGestures { private int lastMouseX = Integer.MIN_VALUE; private int lastMouseY = Integer.MIN_VALUE; @@ -56,6 +64,9 @@ public class Game extends Activity implements SurfaceHolder.Callback, // Only 2 touches are supported private TouchContext[] touchContextMap = new TouchContext[2]; + private long threeFingerDownTime = 0; + + private static final int THREE_FINGER_TAP_THRESHOLD = 300; private ControllerHandler controllerHandler; private VirtualController virtualController; @@ -89,6 +100,13 @@ public class Game extends Activity implements SurfaceHolder.Callback, @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + String locale = PreferenceConfiguration.readPreferences(this).language; + if (!locale.equals(PreferenceConfiguration.DEFAULT_LANGUAGE)) { + Configuration config = new Configuration(getResources().getConfiguration()); + config.locale = new Locale(locale); + getResources().updateConfiguration(config, getResources().getDisplayMetrics()); + } // We don't want a title bar requestWindowFeature(Window.FEATURE_NO_TITLE); @@ -180,7 +198,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, // Initialize the connection conn = new NvConnection(host, uniqueId, Game.this, config, PlatformBinding.getCryptoProvider(this)); keybTranslator = new KeyboardTranslator(conn); - controllerHandler = new ControllerHandler(conn, prefConfig.deadzonePercentage); + controllerHandler = new ControllerHandler(conn, this, prefConfig.deadzonePercentage); SurfaceHolder sh = sv.getHolder(); if (prefConfig.stretchVideo || !decoderRenderer.isHardwareAccelerated()) { @@ -478,6 +496,13 @@ public class Game extends Activity implements SurfaceHolder.Callback, } } + @Override + public void showKeyboard() { + LimeLog.info("Showing keyboard overlay"); + InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); + } + // Returns true if the event was consumed private boolean handleMotionEvent(MotionEvent event) { // Pass through keyboard input if we're not grabbing @@ -499,7 +524,22 @@ public class Game extends Activity implements SurfaceHolder.Callback, int actionIndex = event.getActionIndex(); int eventX = (int)event.getX(actionIndex); - int eventY = (int)event.getY(actionIndex); + int eventY = (int)event.getY(actionIndex); + + // Special handling for 3 finger gesture + if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN && + event.getPointerCount() == 3) { + // Three fingers down + threeFingerDownTime = SystemClock.uptimeMillis(); + + // Cancel the first and second touches to avoid + // erroneous events + for (TouchContext aTouchContext : touchContextMap) { + aTouchContext.cancelTouch(); + } + + return true; + } TouchContext context = getTouchContext(actionIndex); if (context == null) { @@ -514,8 +554,16 @@ public class Game extends Activity implements SurfaceHolder.Callback, break; case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP: + if (event.getPointerCount() == 1) { + // All fingers up + if (SystemClock.uptimeMillis() - threeFingerDownTime < THREE_FINGER_TAP_THRESHOLD) { + // This is a 3 finger tap to bring up the keyboard + showKeyboard(); + return true; + } + } context.touchUpEvent(eventX, eventY); - if (actionIndex == 0 && event.getPointerCount() > 1) { + if (actionIndex == 0 && event.getPointerCount() > 1 && !context.isCancelled()) { // The original secondary touch now becomes primary context.touchDownEvent((int)event.getX(1), (int)event.getY(1)); } diff --git a/app/src/main/java/com/limelight/PcView.java b/app/src/main/java/com/limelight/PcView.java index b3b01a02..8f60d8e6 100644 --- a/app/src/main/java/com/limelight/PcView.java +++ b/app/src/main/java/com/limelight/PcView.java @@ -4,6 +4,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.Locale; import com.limelight.binding.PlatformBinding; import com.limelight.binding.crypto.AndroidCryptoProvider; @@ -16,15 +17,20 @@ import com.limelight.nvstream.http.PairingManager; import com.limelight.nvstream.http.PairingManager.PairState; import com.limelight.nvstream.wol.WakeOnLanSender; import com.limelight.preferences.AddComputerManually; +import com.limelight.preferences.PreferenceConfiguration; import com.limelight.preferences.StreamSettings; +import com.limelight.ui.AdapterFragment; +import com.limelight.ui.AdapterFragmentCallbacks; import com.limelight.utils.Dialog; import com.limelight.utils.UiHelper; import android.app.Activity; +import android.app.FragmentTransaction; import android.app.Service; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; +import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; @@ -35,15 +41,18 @@ import android.view.MenuItem; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; import android.view.View.OnClickListener; +import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.GridView; import android.widget.ImageButton; +import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.Toast; import android.widget.AdapterView.AdapterContextMenuInfo; -public class PcView extends Activity { +public class PcView extends Activity implements AdapterFragmentCallbacks { + private AdapterFragment adapterFragment; private RelativeLayout noPcFoundLayout; private PcGridAdapter pcGridAdapter; private ComputerManagerService.ComputerManagerBinder managerBinder; @@ -102,27 +111,6 @@ public class PcView extends Activity { ImageButton settingsButton = (ImageButton) findViewById(R.id.settingsButton); ImageButton addComputerButton = (ImageButton) findViewById(R.id.manuallyAddPc); - GridView pcGrid = (GridView) findViewById(R.id.pcGridView); - pcGrid.setAdapter(pcGridAdapter); - pcGrid.setOnItemClickListener(new OnItemClickListener() { - @Override - public void onItemClick(AdapterView arg0, View arg1, int pos, - long id) { - ComputerObject computer = (ComputerObject) pcGridAdapter.getItem(pos); - if (computer.details.reachability == ComputerDetails.Reachability.UNKNOWN) { - // Do nothing - } else if (computer.details.reachability == ComputerDetails.Reachability.OFFLINE) { - // Open the context menu if a PC is offline - openContextMenu(arg1); - } else if (computer.details.pairState != PairState.PAIRED) { - // Pair an unpaired machine by default - doPair(computer.details); - } else { - doAppList(computer.details); - } - } - }); - registerForContextMenu(pcGrid); settingsButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -137,6 +125,15 @@ public class PcView extends Activity { } }); + FragmentTransaction transaction = getFragmentManager().beginTransaction(); + if (adapterFragment != null) { + // Remove the old fragment + transaction.remove(adapterFragment); + } + adapterFragment = new AdapterFragment(); + transaction.add(R.id.pcFragmentContainer, adapterFragment); + transaction.commitAllowingStateLoss(); + noPcFoundLayout = (RelativeLayout) findViewById(R.id.no_pc_found_layout); if (pcGridAdapter.getCount() == 0) { noPcFoundLayout.setVisibility(View.VISIBLE); @@ -150,12 +147,20 @@ public class PcView extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + String locale = PreferenceConfiguration.readPreferences(this).language; + if (!locale.equals(PreferenceConfiguration.DEFAULT_LANGUAGE)) { + Configuration config = new Configuration(getResources().getConfiguration()); + config.locale = new Locale(locale); + getResources().updateConfiguration(config, getResources().getDisplayMetrics()); + } // Bind to the computer manager service bindService(new Intent(PcView.this, ComputerManagerService.class), serviceConnection, Service.BIND_AUTO_CREATE); - pcGridAdapter = new PcGridAdapter(this); + pcGridAdapter = new PcGridAdapter(this, + PreferenceConfiguration.readPreferences(this).listMode); initializeViews(); } @@ -561,8 +566,38 @@ public class PcView extends Activity { // Notify the view that the data has changed pcGridAdapter.notifyDataSetChanged(); } - - public class ComputerObject { + + @Override + public int getAdapterFragmentLayoutId() { + return PreferenceConfiguration.readPreferences(this).listMode ? + R.layout.list_view : R.layout.pc_grid_view; + } + + @Override + public void receiveAbsListView(AbsListView listView) { + listView.setAdapter(pcGridAdapter); + listView.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView arg0, View arg1, int pos, + long id) { + ComputerObject computer = (ComputerObject) pcGridAdapter.getItem(pos); + if (computer.details.reachability == ComputerDetails.Reachability.UNKNOWN) { + // Do nothing + } else if (computer.details.reachability == ComputerDetails.Reachability.OFFLINE) { + // Open the context menu if a PC is offline + openContextMenu(arg1); + } else if (computer.details.pairState != PairState.PAIRED) { + // Pair an unpaired machine by default + doPair(computer.details); + } else { + doAppList(computer.details); + } + } + }); + registerForContextMenu(listView); + } + + public class ComputerObject { public ComputerDetails details; public ComputerObject(ComputerDetails details) { diff --git a/app/src/main/java/com/limelight/binding/input/ControllerHandler.java b/app/src/main/java/com/limelight/binding/input/ControllerHandler.java index 3b7fb54d..df9f13a3 100644 --- a/app/src/main/java/com/limelight/binding/input/ControllerHandler.java +++ b/app/src/main/java/com/limelight/binding/input/ControllerHandler.java @@ -10,6 +10,7 @@ import android.view.MotionEvent; import com.limelight.LimeLog; import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.input.ControllerPacket; +import com.limelight.ui.GameGestures; import com.limelight.utils.Vector2d; public class ControllerHandler { @@ -30,6 +31,9 @@ public class ControllerHandler { private long lastLbUpTime = 0; private long lastRbUpTime = 0; private static final int MAXIMUM_BUMPER_UP_DELAY_MS = 100; + + private long startDownTime = 0; + private static final int START_DOWN_TIME_KEYB_MS = 750; private static final int MINIMUM_BUTTON_DOWN_TIME_MS = 25; @@ -46,10 +50,12 @@ public class ControllerHandler { private NvConnection conn; private double stickDeadzone; private final ControllerMapping defaultMapping = new ControllerMapping(); + private GameGestures gestures; private boolean hasGameController; - public ControllerHandler(NvConnection conn, int deadzonePercentage) { + public ControllerHandler(NvConnection conn, GameGestures gestures, int deadzonePercentage) { this.conn = conn; + this.gestures = gestures; // HACK: For now we're hardcoding a 10% deadzone. Some deadzone // is required for controller batching support to work. @@ -513,6 +519,9 @@ public class ControllerHandler { break; case KeyEvent.KEYCODE_BUTTON_START: case KeyEvent.KEYCODE_MENU: + if (SystemClock.uptimeMillis() - startDownTime > ControllerHandler.START_DOWN_TIME_KEYB_MS) { + gestures.showKeyboard(); + } inputMap &= ~ControllerPacket.PLAY_FLAG; break; case KeyEvent.KEYCODE_BACK: @@ -621,6 +630,9 @@ public class ControllerHandler { break; case KeyEvent.KEYCODE_BUTTON_START: case KeyEvent.KEYCODE_MENU: + if (event.getRepeatCount() == 0) { + startDownTime = SystemClock.uptimeMillis(); + } inputMap |= ControllerPacket.PLAY_FLAG; break; case KeyEvent.KEYCODE_BACK: diff --git a/app/src/main/java/com/limelight/binding/input/TouchContext.java b/app/src/main/java/com/limelight/binding/input/TouchContext.java index 91996c4b..87a38249 100644 --- a/app/src/main/java/com/limelight/binding/input/TouchContext.java +++ b/app/src/main/java/com/limelight/binding/input/TouchContext.java @@ -9,6 +9,7 @@ public class TouchContext { private int originalTouchX = 0; private int originalTouchY = 0; private long originalTouchTime = 0; + private boolean cancelled; private NvConnection conn; private int actionIndex; @@ -56,12 +57,17 @@ public class TouchContext { originalTouchX = lastTouchX = eventX; originalTouchY = lastTouchY = eventY; originalTouchTime = System.currentTimeMillis(); - - return true; + cancelled = false; + + return true; } public void touchUpEvent(int eventX, int eventY) { + if (cancelled) { + return; + } + if (isTap()) { byte buttonIndex = getMouseButtonIndex(); @@ -81,8 +87,8 @@ public class TouchContext { } public boolean touchMoveEvent(int eventX, int eventY) - { - if (eventX != lastTouchX || eventY != lastTouchY) + { + if (eventX != lastTouchX || eventY != lastTouchY) { // We only send moves for the primary touch point if (actionIndex == 0) { @@ -102,4 +108,12 @@ public class TouchContext { return true; } + + public void cancelTouch() { + cancelled = true; + } + + public boolean isCancelled() { + return cancelled; + } } diff --git a/app/src/main/java/com/limelight/binding/input/evdev/EvdevReader.java b/app/src/main/java/com/limelight/binding/input/evdev/EvdevReader.java index d5c7c6de..ddb3a7ba 100644 --- a/app/src/main/java/com/limelight/binding/input/evdev/EvdevReader.java +++ b/app/src/main/java/com/limelight/binding/input/evdev/EvdevReader.java @@ -26,7 +26,7 @@ public class EvdevReader { // 4.4 and later to do live SELinux policy changes. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { EvdevShell shell = EvdevShell.getInstance(); - shell.runCommand("supolicy --live \"allow untrusted_app input_device dir getattr\" " + + shell.runCommand("supolicy --live \"allow untrusted_app input_device dir { getattr read search }\" " + "\"allow untrusted_app input_device chr_file { open read write ioctl }\""); } } diff --git a/app/src/main/java/com/limelight/binding/input/virtual_controller/AnalogStick.java b/app/src/main/java/com/limelight/binding/input/virtual_controller/AnalogStick.java index 024c4fe5..131d9acb 100644 --- a/app/src/main/java/com/limelight/binding/input/virtual_controller/AnalogStick.java +++ b/app/src/main/java/com/limelight/binding/input/virtual_controller/AnalogStick.java @@ -5,106 +5,56 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.MotionEvent; -import android.view.View; import java.util.ArrayList; -import java.util.Date; import java.util.List; /** * Created by Karim Mreisi on 30.11.2014. */ -public class AnalogStick extends View +public class AnalogStick extends VirtualControllerElement { - private enum _STICK_STATE - { - NO_MOVEMENT, - MOVED - } + protected static boolean _PRINT_DEBUG_INFORMATION = true; - private enum _CLICK_STATE - { - SINGLE, - DOUBLE - } + float radius_complete = 0; + float radius_dead_zone = 0; + float radius_analog_stick = 0; + float position_stick_x = 0; + float position_stick_y = 0; - private static final boolean _PRINT_DEBUG_INFORMATION = false; + boolean viewPressed = false; + boolean analogStickActive = false; - public interface AnalogStickListener - { - void onMovement(float x, float y); - void onClick(); - void onRevoke(); - void onDoubleClick(); - } + _STICK_STATE stick_state = _STICK_STATE.NO_MOVEMENT; + _CLICK_STATE click_state = _CLICK_STATE.SINGLE; - public void addAnalogStickListener (AnalogStickListener listener) - { - listeners.add(listener); - } - - public void setOnTouchListener(OnTouchListener listener) - { - onTouchListener = listener; - } - - private static final void _DBG(String text) - { - if (_PRINT_DEBUG_INFORMATION) - { - System.out.println("AnalogStick: " + text); - } - } - - private int normalColor = 0xF0888888; - private int pressedColor = 0xF00000FF; - - private long timeoutDoubleClick = 250; - private long timeLastClick = 0; - - float radius_complete = 0; - float radius_dead_zone = 0; - float radius_analog_stick = 0; - - float position_stick_x = 0; - float position_stick_y = 0; - - boolean viewPressed = false; - boolean analogStickActive = false; - _STICK_STATE stick_state = _STICK_STATE.NO_MOVEMENT; - _CLICK_STATE click_state = _CLICK_STATE.SINGLE; - - List listeners = new ArrayList(); - OnTouchListener onTouchListener = null; + List listeners = new ArrayList(); + OnTouchListener onTouchListener = null; + private long timeoutDoubleClick = 250; + private long timeLastClick = 0; public AnalogStick(Context context) { super(context); - position_stick_x = getWidth() / 2; - position_stick_y = getHeight() / 2; - - stick_state = _STICK_STATE.NO_MOVEMENT; - click_state = _CLICK_STATE.SINGLE; - viewPressed = false; - analogStickActive = false; - + position_stick_x = getWidth() / 2; + position_stick_y = getHeight() / 2; } - public void setColors(int normalColor, int pressedColor) - { - this.normalColor = normalColor; - this.pressedColor = pressedColor; - } - - private float getPercent(float value, int percent) + public void addAnalogStickListener(AnalogStickListener listener) { - return value / 100 * percent; + listeners.add(listener); } - private int getCorrectWidth() + public void setOnTouchListener(OnTouchListener listener) { - return getWidth() > getHeight() ? getHeight() : getWidth(); + onTouchListener = listener; + } + + public void setColors(int normalColor, int pressedColor) + { + this.normalColor = normalColor; + this.pressedColor = pressedColor; } private double getMovementRadius(float x, float y) @@ -119,15 +69,15 @@ public class AnalogStick extends View return x > 0 ? x : -x; } - return Math.sqrt(x * x + y * y); + return Math.sqrt(x * x + y * y); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { - radius_complete = getPercent(getCorrectWidth() / 2, 95); - radius_dead_zone = getPercent(getCorrectWidth() / 2, 20); - radius_analog_stick = getPercent(getCorrectWidth() / 2, 30); + radius_complete = getPercent(getCorrectWidth() / 2, 95); + radius_dead_zone = getPercent(getCorrectWidth() / 2, 20); + radius_analog_stick = getPercent(getCorrectWidth() / 2, 30); super.onSizeChanged(w, h, oldw, oldh); } @@ -135,29 +85,29 @@ public class AnalogStick extends View @Override protected void onDraw(Canvas canvas) { - // set transparent background - canvas.drawColor(Color.TRANSPARENT); + // set transparent background + canvas.drawColor(Color.TRANSPARENT); Paint paint = new Paint(); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(getPercent(getCorrectWidth() / 2, 2)); - // draw outer circle - if (!viewPressed || click_state == _CLICK_STATE.SINGLE) - { - paint.setColor(normalColor); - } - else - { - paint.setColor(pressedColor); - } + // draw outer circle + if (!viewPressed || click_state == _CLICK_STATE.SINGLE) + { + paint.setColor(normalColor); + } + else + { + paint.setColor(pressedColor); + } - canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius_complete, paint); + canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius_complete, paint); - paint.setColor(normalColor); + paint.setColor(normalColor); // draw dead zone - canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius_dead_zone, paint); + canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius_dead_zone, paint); // draw stick depending on state (no movement, moved, active(out of dead zone)) if (analogStickActive) @@ -209,11 +159,11 @@ public class AnalogStick extends View { if (way_x > 0) { - angle = Math.PI * 3/2; + angle = Math.PI * 3 / 2; } else if (way_x < 0) { - angle = Math.PI * 1/2; + angle = Math.PI * 1 / 2; } } else @@ -221,30 +171,31 @@ public class AnalogStick extends View if (way_x > 0) { if (way_y < 0) - { // first quadrant - angle = 3 * Math.PI / 2 + Math.atan((double)(-way_y / way_x)); + { // first quadrant + angle = + 3 * Math.PI / 2 + Math.atan((double) (-way_y / way_x)); } else - { // second quadrant - angle = Math.PI + Math.atan((double)(way_x / way_y)); + { // second quadrant + angle = Math.PI + Math.atan((double) (way_x / way_y)); } } else { if (way_y > 0) - { // third quadrant - angle = Math.PI / 2 + Math.atan((double)(way_y / -way_x)); + { // third quadrant + angle = Math.PI / 2 + Math.atan((double) (way_y / -way_x)); } else - { // fourth quadrant + { // fourth quadrant angle = 0 + Math.atan((double) (-way_x / -way_y)); } } } - _DBG("angle: " + angle + " way y: "+ way_y + " way x: " + way_x); + _DBG("angle: " + angle + " way y: " + way_y + " way x: " + way_x); - return angle; + return angle; } private void moveActionCallback(float x, float y) @@ -258,50 +209,49 @@ public class AnalogStick extends View } } - private void clickActionCallback() - { - _DBG("click"); - - // notify listeners - for (AnalogStickListener listener : listeners) - { - listener.onClick(); - } - } - - private void doubleClickActionCallback() - { - _DBG("double click"); - - // notify listeners - for (AnalogStickListener listener : listeners) - { - listener.onDoubleClick(); - } - } - - private void revokeActionCallback() - { - _DBG("revoke"); - - // notify listeners - for (AnalogStickListener listener : listeners) - { - listener.onRevoke(); - } - } - - - private void updatePosition(float x, float y) + private void clickActionCallback() { - float way_x = -(getWidth() / 2 - x); - float way_y = -(getHeight() / 2 - y); + _DBG("click"); - float movement_x = 0; - float movement_y = 0; + // notify listeners + for (AnalogStickListener listener : listeners) + { + listener.onClick(); + } + } - double movement_radius = getMovementRadius(way_x, way_y); - double movement_angle = getAngle(way_x, way_y); + private void doubleClickActionCallback() + { + _DBG("double click"); + + // notify listeners + for (AnalogStickListener listener : listeners) + { + listener.onDoubleClick(); + } + } + + private void revokeActionCallback() + { + _DBG("revoke"); + + // notify listeners + for (AnalogStickListener listener : listeners) + { + listener.onRevoke(); + } + } + + private void updatePosition(float x, float y) + { + float way_x = -(getWidth() / 2 - x); + float way_y = -(getHeight() / 2 - y); + + float movement_x = 0; + float movement_y = 0; + + double movement_radius = getMovementRadius(way_x, way_y); + double movement_angle = getAngle(way_x, way_y); // chop radius if out of outer circle if (movement_radius > (radius_complete - radius_analog_stick)) @@ -309,8 +259,10 @@ public class AnalogStick extends View movement_radius = radius_complete - radius_analog_stick; } - float correlated_y = (float)(Math.sin(Math.PI / 2 - movement_angle) * (movement_radius)); - float correlated_x = (float)(Math.cos(Math.PI / 2 - movement_angle) * (movement_radius)); + float correlated_y = + (float) (Math.sin(Math.PI / 2 - movement_angle) * (movement_radius)); + float correlated_x = + (float) (Math.cos(Math.PI / 2 - movement_angle) * (movement_radius)); float complete = (radius_complete - radius_analog_stick); @@ -336,71 +288,69 @@ public class AnalogStick extends View @Override public boolean onTouchEvent(MotionEvent event) { - if (onTouchListener != null) - { - return onTouchListener.onTouch(this, event); - } + if (onTouchListener != null) + { + return onTouchListener.onTouch(this, event); + } // get masked (not specific to a pointer) action - int action = event.getActionMasked(); - _CLICK_STATE lastClickState = click_state; - boolean wasPressed = analogStickActive; + int action = event.getActionMasked(); + _CLICK_STATE lastClickState = click_state; + boolean wasPressed = analogStickActive; switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: - { - viewPressed = true; - // check for double click - if (lastClickState == _CLICK_STATE.SINGLE && timeLastClick + timeoutDoubleClick > System.currentTimeMillis()) - { - click_state = _CLICK_STATE.DOUBLE; + { + viewPressed = true; + // check for double click + if (lastClickState == _CLICK_STATE.SINGLE && timeLastClick + timeoutDoubleClick > System.currentTimeMillis()) + { + click_state = _CLICK_STATE.DOUBLE; - doubleClickActionCallback(); - } - else - { - click_state = _CLICK_STATE.SINGLE; + doubleClickActionCallback(); + } + else + { + click_state = _CLICK_STATE.SINGLE; - clickActionCallback(); - } + clickActionCallback(); + } - timeLastClick = System.currentTimeMillis(); - - break; - } - case MotionEvent.ACTION_MOVE: - { - if (analogStickActive || timeLastClick + timeoutDoubleClick < System.currentTimeMillis()) - { - analogStickActive = true; - } + timeLastClick = System.currentTimeMillis(); break; } - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_MOVE: { - analogStickActive = false; - viewPressed = false; + if (analogStickActive || timeLastClick + timeoutDoubleClick < System.currentTimeMillis()) + { + analogStickActive = true; + } - revokeActionCallback(); + break; + } + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + { + analogStickActive = false; + viewPressed = false; + + revokeActionCallback(); break; } } + // no longer pressed reset movement if (analogStickActive) - { // when is pressed calculate new positions (will trigger movement if necessary) + { // when is pressed calculate new positions (will trigger movement if necessary) updatePosition(event.getX(), event.getY()); } - else - { // no longer pressed reset movement - if (wasPressed) - { - moveActionCallback(0, 0); - } + else if (wasPressed) + { + moveActionCallback(0, 0); } // to get view refreshed @@ -408,4 +358,28 @@ public class AnalogStick extends View return true; } + + private enum _STICK_STATE + { + NO_MOVEMENT, + MOVED + } + + + private enum _CLICK_STATE + { + SINGLE, + DOUBLE + } + + public interface AnalogStickListener + { + void onMovement(float x, float y); + + void onClick(); + + void onRevoke(); + + void onDoubleClick(); + } } diff --git a/app/src/main/java/com/limelight/binding/input/virtual_controller/DigitalButton.java b/app/src/main/java/com/limelight/binding/input/virtual_controller/DigitalButton.java index 05c8f86b..7c012c9c 100644 --- a/app/src/main/java/com/limelight/binding/input/virtual_controller/DigitalButton.java +++ b/app/src/main/java/com/limelight/binding/input/virtual_controller/DigitalButton.java @@ -6,7 +6,6 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.view.MotionEvent; -import android.view.View; import java.util.ArrayList; import java.util.List; @@ -16,214 +15,181 @@ import java.util.TimerTask; /** * Created by Karim on 24.01.2015. */ -public class DigitalButton extends View +public class DigitalButton extends VirtualControllerElement { - private class TimerLongClickTimerTask extends TimerTask - { - @Override - public void run() - { - onLongClickCallback(); - } - } - private static final boolean _PRINT_DEBUG_INFORMATION = false; + List listeners = new ArrayList(); + OnTouchListener onTouchListener = null; + boolean clicked; + private String text = ""; + private int icon = -1; + private long timerLongClickTimeout = 3000; + private Timer timerLongClick = null; + private TimerLongClickTimerTask longClickTimerTask = null; - private int normalColor = 0xF0888888; - private int pressedColor = 0xF00000FF; - private String text = ""; - private int icon = -1; + public DigitalButton(Context context) + { + super(context); + clicked = false; + } - private long timerLongClickTimeout = 3000; - private Timer timerLongClick = null; - private TimerLongClickTimerTask longClickTimerTask = null; + public void addDigitalButtonListener(DigitalButtonListener listener) + { + listeners.add(listener); + } + public void setOnTouchListener(OnTouchListener listener) + { + onTouchListener = listener; + } - public interface DigitalButtonListener - { - void onClick(); - void onLongClick(); - void onRelease(); - } + public void setText(String text) + { + this.text = text; + invalidate(); + } - public void addDigitalButtonListener(DigitalButtonListener listener) - { - listeners.add(listener); - } + public void setIcon(int id) + { + this.icon = id; + invalidate(); + } - public void setColors(int normalColor, int pressedColor) - { - this.normalColor = normalColor; - this.pressedColor = pressedColor; - } + @Override + protected void onDraw(Canvas canvas) + { + // set transparent background + canvas.drawColor(Color.TRANSPARENT); - public void setOnTouchListener(OnTouchListener listener) - { - onTouchListener = listener; - } + Paint paint = new Paint(); - private static final void _DBG(String text) - { - if (_PRINT_DEBUG_INFORMATION) - { - System.out.println("DigitalButton: " + text); - } - } + paint.setTextSize(getPercent(getCorrectWidth(), 50)); + paint.setTextAlign(Paint.Align.CENTER); + paint.setStrokeWidth(3); - List listeners = new ArrayList(); - OnTouchListener onTouchListener = null; + paint.setColor(clicked ? pressedColor : normalColor); + paint.setStyle(Paint.Style.STROKE); + canvas.drawRect( + 1, 1, + getWidth() - 1, getHeight() - 1, + paint + ); - boolean clicked; + if (icon != -1) + { + Drawable d = getResources().getDrawable(icon); + d.setBounds(5, 5, getWidth() - 5, getHeight() - 5); + d.draw(canvas); + } + else + { + paint.setStyle(Paint.Style.FILL_AND_STROKE); + canvas.drawText(text, + getPercent(getWidth(), 50), getPercent(getHeight(), 73), + paint); + } - public DigitalButton(Context context) - { - super(context); + super.onDraw(canvas); + } - clicked = false; - } + private void onClickCallback() + { + _DBG("clicked"); - public void setText(String text) - { - this.text = text; + // notify listeners + for (DigitalButtonListener listener : listeners) + { + listener.onClick(); + } - invalidate(); - } + timerLongClick = new Timer(); + longClickTimerTask = new TimerLongClickTimerTask(); - public void setIcon(int id) - { - this.icon = id; + timerLongClick.schedule(longClickTimerTask, timerLongClickTimeout); + } - invalidate(); - } + private void onLongClickCallback() + { + _DBG("long click"); - private float getPercent(float value, float percent) - { - return value / 100 * percent; - } + // notify listeners + for (DigitalButtonListener listener : listeners) + { + listener.onLongClick(); + } + } - private int getCorrectWidth() - { - return getWidth() > getHeight() ? getHeight() : getWidth(); - } + private void onReleaseCallback() + { + _DBG("released"); - @Override - protected void onDraw(Canvas canvas) - { - // set transparent background - canvas.drawColor(Color.TRANSPARENT); + // notify listeners + for (DigitalButtonListener listener : listeners) + { + listener.onRelease(); + } - Paint paint = new Paint(); + timerLongClick.cancel(); + longClickTimerTask.cancel(); + } - paint.setTextSize(getPercent(getCorrectWidth(), 50)); - paint.setTextAlign(Paint.Align.CENTER); - paint.setStrokeWidth(3); + @Override + public boolean onTouchEvent(MotionEvent event) + { + /* + if (onTouchListener != null) + { + return onTouchListener.onTouch(this, event); + } + */ + // get masked (not specific to a pointer) action + int action = event.getActionMasked(); - paint.setColor(clicked ? pressedColor : normalColor); - paint.setStyle(Paint.Style.STROKE); - canvas.drawRect( - 1, 1, - getWidth() - 1, getHeight() - 1, - paint - ); + switch (action) + { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + { + clicked = true; + onClickCallback(); - if (icon != -1) - { - Drawable d = getResources().getDrawable(icon); - d.setBounds(5, 5, getWidth() - 5, getHeight() - 5); - d.draw(canvas); - } - else - { - paint.setStyle(Paint.Style.FILL_AND_STROKE); - canvas.drawText(text, - getPercent(getWidth(), 50), getPercent(getHeight(), 73), - paint); - } + invalidate(); - super.onDraw(canvas); - } + return true; + } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + { + clicked = false; + onReleaseCallback(); - private void onClickCallback() - { - _DBG("clicked"); + invalidate(); - // notify listeners - for (DigitalButtonListener listener : listeners) - { - listener.onClick(); - } + return true; + } + default: + { + } + } - timerLongClick = new Timer(); - longClickTimerTask = new TimerLongClickTimerTask(); + return true; + } - timerLongClick.schedule(longClickTimerTask, timerLongClickTimeout); - } + public interface DigitalButtonListener + { + void onClick(); - private void onLongClickCallback() - { - _DBG("long click"); + void onLongClick(); - // notify listeners - for (DigitalButtonListener listener : listeners) - { - listener.onLongClick(); - } - } + void onRelease(); + } - private void onReleaseCallback() - { - _DBG("released"); - - // notify listeners - for (DigitalButtonListener listener : listeners) - { - listener.onRelease(); - } - - timerLongClick.cancel(); - longClickTimerTask.cancel(); - } - - - @Override - public boolean onTouchEvent(MotionEvent event) - { - /* - if (onTouchListener != null) - { - return onTouchListener.onTouch(this, event); - } - */ - // get masked (not specific to a pointer) action - int action = event.getActionMasked(); - - switch (action) - { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - { - clicked = true; - onClickCallback(); - - invalidate(); - - return true; - } - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - { - clicked = false; - onReleaseCallback(); - - invalidate(); - - return true; - } - default: - { - } - } - - return true; - } + private class TimerLongClickTimerTask extends TimerTask + { + @Override + public void run() + { + onLongClickCallback(); + } + } } diff --git a/app/src/main/java/com/limelight/binding/input/virtual_controller/DigitalPad.java b/app/src/main/java/com/limelight/binding/input/virtual_controller/DigitalPad.java index 0722fcf1..d7171d22 100644 --- a/app/src/main/java/com/limelight/binding/input/virtual_controller/DigitalPad.java +++ b/app/src/main/java/com/limelight/binding/input/virtual_controller/DigitalPad.java @@ -5,7 +5,6 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.MotionEvent; -import android.view.View; import java.util.ArrayList; import java.util.List; @@ -13,273 +12,244 @@ import java.util.List; /** * Created by Karim Mreisi on 23.01.2015. */ -public class DigitalPad extends View +public class DigitalPad extends VirtualControllerElement { - public final static int DIGITAL_PAD_DIRECTION_NO_DIRECTION = 0; - public final static int DIGITAL_PAD_DIRECTION_LEFT = 1; - public final static int DIGITAL_PAD_DIRECTION_UP = 2; - public final static int DIGITAL_PAD_DIRECTION_RIGHT = 4; - public final static int DIGITAL_PAD_DIRECTION_DOWN = 8; + public final static int DIGITAL_PAD_DIRECTION_NO_DIRECTION = 0; + int direction = DIGITAL_PAD_DIRECTION_NO_DIRECTION; + public final static int DIGITAL_PAD_DIRECTION_LEFT = 1; + public final static int DIGITAL_PAD_DIRECTION_UP = 2; + public final static int DIGITAL_PAD_DIRECTION_RIGHT = 4; + public final static int DIGITAL_PAD_DIRECTION_DOWN = 8; + List listeners = new ArrayList(); + OnTouchListener onTouchListener = null; - private int normalColor = 0xF0888888; - private int pressedColor = 0xF00000FF; + public DigitalPad(Context context) + { + super(context); + } - private static final boolean _PRINT_DEBUG_INFORMATION = false; + public void addDigitalPadListener(DigitalPadListener listener) + { + listeners.add(listener); + } - public interface DigitalPadListener - { - void onDirectionChange(int direction); - } + public void setOnTouchListener(OnTouchListener listener) + { + onTouchListener = listener; + } - public void addDigitalPadListener (DigitalPadListener listener) - { - listeners.add(listener); - } + @Override + protected void onDraw(Canvas canvas) + { + // set transparent background + canvas.drawColor(Color.TRANSPARENT); - public void setOnTouchListener(OnTouchListener listener) - { - onTouchListener = listener; - } + Paint paint = new Paint(); - private static final void _DBG(String text) - { - if (_PRINT_DEBUG_INFORMATION) - { - System.out.println("DigitalPad: " + text); - } - } + paint.setTextSize(getPercent(getCorrectWidth(), 20)); + paint.setTextAlign(Paint.Align.CENTER); + paint.setStrokeWidth(3); - List listeners = new ArrayList(); - OnTouchListener onTouchListener = null; + if (direction == DIGITAL_PAD_DIRECTION_NO_DIRECTION) + { + // draw no direction rect + paint.setStyle(Paint.Style.STROKE); + paint.setColor(normalColor); + canvas.drawRect( + getPercent(getWidth(), 36), getPercent(getHeight(), 36), + getPercent(getWidth(), 63), getPercent(getHeight(), 63), + paint + ); + } - int direction; + // draw left rect + paint.setColor( + (direction & DIGITAL_PAD_DIRECTION_LEFT) > 0 ? pressedColor : normalColor); + paint.setStyle(Paint.Style.FILL_AND_STROKE); + canvas.drawText("LF", + getPercent(getWidth(), 16.5f), getPercent(getHeight(), 56), + paint); + paint.setStyle(Paint.Style.STROKE); + canvas.drawRect( + 0, getPercent(getHeight(), 33), + getPercent(getWidth(), 33), getPercent(getHeight(), 66), + paint + ); - public DigitalPad(Context context) - { - super(context); + // draw left up line + paint.setColor(( + (direction & DIGITAL_PAD_DIRECTION_LEFT) > 0 && + (direction & DIGITAL_PAD_DIRECTION_UP) > 0 + ) ? pressedColor : normalColor + ); + paint.setStyle(Paint.Style.STROKE); + canvas.drawLine( + 0, getPercent(getWidth(), 33), + getPercent(getWidth(), 33), 0, + paint + ); - direction = DIGITAL_PAD_DIRECTION_NO_DIRECTION; - } + // draw up rect + paint.setColor( + (direction & DIGITAL_PAD_DIRECTION_UP) > 0 ? pressedColor : normalColor); + paint.setStyle(Paint.Style.FILL_AND_STROKE); + canvas.drawText("UP", + getPercent(getWidth(), 49.5f), getPercent(getHeight(), 23), + paint); + paint.setStyle(Paint.Style.STROKE); + canvas.drawRect( + getPercent(getWidth(), 33), 0, + getPercent(getWidth(), 66), getPercent(getHeight(), 33), + paint + ); - private float getPercent(float value, float percent) - { - return value / 100 * percent; - } + // draw up right line + paint.setColor(( + (direction & DIGITAL_PAD_DIRECTION_UP) > 0 && + (direction & DIGITAL_PAD_DIRECTION_RIGHT) > 0 + ) ? pressedColor : normalColor + ); + paint.setStyle(Paint.Style.STROKE); + canvas.drawLine( + getPercent(getWidth(), 66), 0, + getPercent(getWidth(), 100), getPercent(getHeight(), 33), + paint + ); - private int getCorrectWidth() - { - return getWidth() > getHeight() ? getHeight() : getWidth(); - } + // draw right rect + paint.setColor( + (direction & DIGITAL_PAD_DIRECTION_RIGHT) > 0 ? pressedColor : normalColor); + paint.setStyle(Paint.Style.FILL_AND_STROKE); + canvas.drawText("RI", + getPercent(getWidth(), 82.5f), getPercent(getHeight(), 56), + paint); + paint.setStyle(Paint.Style.STROKE); + canvas.drawRect( + getPercent(getWidth(), 66), getPercent(getHeight(), 33), + getPercent(getWidth(), 100), getPercent(getHeight(), 66), + paint + ); - public void setColors(int normalColor, int pressedColor) - { - this.normalColor = normalColor; - this.pressedColor = pressedColor; - } + // draw right down line + paint.setColor(( + (direction & DIGITAL_PAD_DIRECTION_RIGHT) > 0 && + (direction & DIGITAL_PAD_DIRECTION_DOWN) > 0 + ) ? pressedColor : normalColor + ); + paint.setStyle(Paint.Style.STROKE); + canvas.drawLine( + getPercent(getWidth(), 100), getPercent(getHeight(), 66), + getPercent(getWidth(), 66), getPercent(getHeight(), 100), + paint + ); - @Override - protected void onDraw(Canvas canvas) - { - // set transparent background - canvas.drawColor(Color.TRANSPARENT); + // draw down rect + paint.setColor( + (direction & DIGITAL_PAD_DIRECTION_DOWN) > 0 ? pressedColor : normalColor); + paint.setStyle(Paint.Style.FILL_AND_STROKE); + canvas.drawText("DW", + getPercent(getWidth(), 49.5f), getPercent(getHeight(), 89), + paint); + paint.setStyle(Paint.Style.STROKE); + canvas.drawRect( + getPercent(getWidth(), 33), getPercent(getHeight(), 66), + getPercent(getWidth(), 66), getPercent(getHeight(), 100), + paint + ); - Paint paint = new Paint(); + // draw down left line + paint.setColor(( + (direction & DIGITAL_PAD_DIRECTION_DOWN) > 0 && + (direction & DIGITAL_PAD_DIRECTION_LEFT) > 0 + ) ? pressedColor : normalColor + ); + paint.setStyle(Paint.Style.STROKE); + canvas.drawLine( + getPercent(getWidth(), 33), getPercent(getHeight(), 100), + getPercent(getWidth(), 0), getPercent(getHeight(), 66), + paint + ); - paint.setTextSize(getPercent(getCorrectWidth(), 20)); - paint.setTextAlign(Paint.Align.CENTER); - paint.setStrokeWidth(3); + super.onDraw(canvas); + } - if (direction == DIGITAL_PAD_DIRECTION_NO_DIRECTION) - { - // draw no direction rect - paint.setStyle(Paint.Style.STROKE); - paint.setColor(normalColor); - canvas.drawRect( - getPercent(getWidth(), 36), getPercent(getHeight(), 36), - getPercent(getWidth(), 63), getPercent(getHeight(), 63), - paint - ); - } + private void newDirectionCallback(int direction) + { + _DBG("direction: " + direction); - // draw left rect - paint.setColor((direction & DIGITAL_PAD_DIRECTION_LEFT) > 0 ? pressedColor : normalColor); - paint.setStyle(Paint.Style.FILL_AND_STROKE); - canvas.drawText("LF", - getPercent(getWidth(), 16.5f), getPercent(getHeight(), 56), - paint); - paint.setStyle(Paint.Style.STROKE); - canvas.drawRect( - 0, getPercent(getHeight(), 33), - getPercent(getWidth(), 33), getPercent(getHeight(), 66), - paint - ); + // notify listeners + for (DigitalPadListener listener : listeners) + { + listener.onDirectionChange(direction); + } + } - // draw left up line - paint.setColor(( - (direction & DIGITAL_PAD_DIRECTION_LEFT) > 0 && - (direction & DIGITAL_PAD_DIRECTION_UP) > 0 - ) ? pressedColor : normalColor - ); - paint.setStyle(Paint.Style.STROKE); - canvas.drawLine( - 0, getPercent(getWidth(), 33), - getPercent(getWidth(), 33), 0, - paint - ); + @Override + public boolean onTouchEvent(MotionEvent event) + { + if (onTouchListener != null) + { + return onTouchListener.onTouch(this, event); + } - // draw up rect - paint.setColor((direction & DIGITAL_PAD_DIRECTION_UP) > 0 ? pressedColor : normalColor); - paint.setStyle(Paint.Style.FILL_AND_STROKE); - canvas.drawText("UP", - getPercent(getWidth(), 49.5f), getPercent(getHeight(), 23), - paint); - paint.setStyle(Paint.Style.STROKE); - canvas.drawRect( - getPercent(getWidth(), 33), 0, - getPercent(getWidth(), 66), getPercent(getHeight(), 33), - paint - ); + // get masked (not specific to a pointer) action + int action = event.getActionMasked(); - // draw up right line - paint.setColor(( - (direction & DIGITAL_PAD_DIRECTION_UP) > 0 && - (direction & DIGITAL_PAD_DIRECTION_RIGHT) > 0 - ) ? pressedColor : normalColor - ); - paint.setStyle(Paint.Style.STROKE); - canvas.drawLine( - getPercent(getWidth(), 66), 0, - getPercent(getWidth(), 100), getPercent(getHeight(), 33), - paint - ); + switch (action) + { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + { + direction = 0; - // draw right rect - paint.setColor((direction & DIGITAL_PAD_DIRECTION_RIGHT) > 0 ? pressedColor : normalColor); - paint.setStyle(Paint.Style.FILL_AND_STROKE); - canvas.drawText("RI", - getPercent(getWidth(), 82.5f), getPercent(getHeight(), 56), - paint); - paint.setStyle(Paint.Style.STROKE); - canvas.drawRect( - getPercent(getWidth(), 66), getPercent(getHeight(), 33), - getPercent(getWidth(), 100), getPercent(getHeight(), 66), - paint - ); + if (event.getX() < getPercent(getWidth(), 33)) + { + direction |= DIGITAL_PAD_DIRECTION_LEFT; + } - // draw right down line - paint.setColor(( - (direction & DIGITAL_PAD_DIRECTION_RIGHT) > 0 && - (direction & DIGITAL_PAD_DIRECTION_DOWN) > 0 - ) ? pressedColor : normalColor - ); - paint.setStyle(Paint.Style.STROKE); - canvas.drawLine( - getPercent(getWidth(), 100), getPercent(getHeight(), 66), - getPercent(getWidth(), 66), getPercent(getHeight(), 100), - paint - ); + if (event.getX() > getPercent(getWidth(), 66)) + { + direction |= DIGITAL_PAD_DIRECTION_RIGHT; + } - // draw down rect - paint.setColor((direction & DIGITAL_PAD_DIRECTION_DOWN) > 0 ? pressedColor : normalColor); - paint.setStyle(Paint.Style.FILL_AND_STROKE); - canvas.drawText("DW", - getPercent(getWidth(), 49.5f), getPercent(getHeight(), 89), - paint); - paint.setStyle(Paint.Style.STROKE); - canvas.drawRect( - getPercent(getWidth(), 33), getPercent(getHeight(), 66), - getPercent(getWidth(), 66), getPercent(getHeight(), 100), - paint - ); + if (event.getY() > getPercent(getHeight(), 66)) + { + direction |= DIGITAL_PAD_DIRECTION_DOWN; + } - // draw down left line - paint.setColor(( - (direction & DIGITAL_PAD_DIRECTION_DOWN) > 0 && - (direction & DIGITAL_PAD_DIRECTION_LEFT) > 0 - ) ? pressedColor : normalColor - ); - paint.setStyle(Paint.Style.STROKE); - canvas.drawLine( - getPercent(getWidth(), 33), getPercent(getHeight(), 100), - getPercent(getWidth(), 0), getPercent(getHeight(), 66), - paint - ); + if (event.getY() < getPercent(getHeight(), 33)) + { + direction |= DIGITAL_PAD_DIRECTION_UP; + } - super.onDraw(canvas); - } + newDirectionCallback(direction); - private void newDirectionCallback(int direction) - { - _DBG("direction: " + direction); + invalidate(); - // notify listeners - for (DigitalPadListener listener : listeners) - { - listener.onDirectionChange(direction); - } - } + return true; + } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + { + direction = 0; - @Override - public boolean onTouchEvent(MotionEvent event) - { - if (onTouchListener != null) - { - return onTouchListener.onTouch(this, event); - } + newDirectionCallback(direction); - // get masked (not specific to a pointer) action - int action = event.getActionMasked(); + invalidate(); - switch (action) - { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - { - direction = 0; + return true; + } + default: + { + } + } - if (event.getX() < getPercent(getWidth(), 33)) - { - direction |= DIGITAL_PAD_DIRECTION_LEFT; - } + return true; + } - if (event.getX() > getPercent(getWidth(), 66)) - { - direction |= DIGITAL_PAD_DIRECTION_RIGHT; - } - - if (event.getY() > getPercent(getHeight(), 66)) - { - direction |= DIGITAL_PAD_DIRECTION_DOWN; - } - - if (event.getY() < getPercent(getHeight(), 33)) - { - direction |= DIGITAL_PAD_DIRECTION_UP; - } - - newDirectionCallback(direction); - - invalidate(); - - return true; - } - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - { - direction = 0; - - newDirectionCallback(direction); - - invalidate(); - - return true; - } - default: - { - } - } - - return true; - } + public interface DigitalPadListener + { + void onDirectionChange(int direction); + } } diff --git a/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualController.java b/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualController.java index 8942c0ea..87c9a2a7 100644 --- a/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualController.java +++ b/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualController.java @@ -2,13 +2,9 @@ package com.limelight.binding.input.virtual_controller; import android.content.Context; import android.content.Intent; -import android.view.MenuInflater; import android.view.View; -import android.view.WindowManager; import android.widget.FrameLayout; -import android.widget.PopupMenu; import android.widget.RelativeLayout; -import android.widget.Toast; import com.limelight.R; import com.limelight.nvstream.NvConnection; @@ -19,183 +15,63 @@ import com.limelight.nvstream.input.ControllerPacket; */ public class VirtualController { - private static final boolean _PRINT_DEBUG_INFORMATION = false; + private static final boolean _PRINT_DEBUG_INFORMATION = false; + NvConnection connection = null; + private Context context = null; + private short inputMap = 0x0000; + private byte leftTrigger = 0x00; + private byte rightTrigger = 0x00; + private short rightStickX = 0x0000; + private short rightStickY = 0x0000; + private short leftStickX = 0x0000; + private short leftStickY = 0x0000; - private static final void _DBG(String text) - { - if (_PRINT_DEBUG_INFORMATION) - { - System.out.println("VirtualController: " + text); - } - } + private FrameLayout frame_layout = null; + private RelativeLayout relative_layout = null; - private short inputMap = 0x0000; - private byte leftTrigger = 0x00; - private byte rightTrigger = 0x00; - private short rightStickX = 0x0000; - private short rightStickY = 0x0000; - private short leftStickX = 0x0000; - private short leftStickY = 0x0000; + private RelativeLayout.LayoutParams layoutParamsButtonStart = null; + private RelativeLayout.LayoutParams layoutParamsButtonSelect = null; - private FrameLayout frame_layout = null; - private RelativeLayout relative_layout = null; + private RelativeLayout.LayoutParams layoutParamsDPad = null; - private RelativeLayout.LayoutParams layoutParamsButtonStart = null; - private RelativeLayout.LayoutParams layoutParamsButtonSelect = null; + private RelativeLayout.LayoutParams layoutParamsButtonA = null; + private RelativeLayout.LayoutParams layoutParamsButtonB = null; + private RelativeLayout.LayoutParams layoutParamsButtonX = null; + private RelativeLayout.LayoutParams layoutParamsButtonY = null; + private RelativeLayout.LayoutParams layoutParamsButtonLT = null; + private RelativeLayout.LayoutParams layoutParamsButtonRT = null; + private RelativeLayout.LayoutParams layoutParamsButtonLB = null; + private RelativeLayout.LayoutParams layoutParamsButtonRB = null; - private RelativeLayout.LayoutParams layoutParamsDPad = null; + private RelativeLayout.LayoutParams layoutParamsStick = null; + private RelativeLayout.LayoutParams layoutParamsStick2 = null; - private RelativeLayout.LayoutParams layoutParamsButtonA = null; - private RelativeLayout.LayoutParams layoutParamsButtonB = null; - private RelativeLayout.LayoutParams layoutParamsButtonX = null; - private RelativeLayout.LayoutParams layoutParamsButtonY = null; - private RelativeLayout.LayoutParams layoutParamsButtonLT = null; - private RelativeLayout.LayoutParams layoutParamsButtonRT = null; - private RelativeLayout.LayoutParams layoutParamsButtonLB = null; - private RelativeLayout.LayoutParams layoutParamsButtonRB = null; + private RelativeLayout.LayoutParams layoutParamsButtonConfigure = null; - private RelativeLayout.LayoutParams layoutParamsStick = null; - private RelativeLayout.LayoutParams layoutParamsStick2 = null; + private DigitalButton buttonStart = null; + private DigitalButton buttonSelect = null; - private RelativeLayout.LayoutParams layoutParamsButtonConfigure = null; + private DigitalPad digitalPad = null; - private DigitalButton buttonStart = null; - private DigitalButton buttonSelect = null; + private DigitalButton buttonA = null; + private DigitalButton buttonB = null; + private DigitalButton buttonX = null; + private DigitalButton buttonY = null; + private DigitalButton buttonLT = null; + private DigitalButton buttonRT = null; + private DigitalButton buttonLB = null; + private DigitalButton buttonRB = null; - private DigitalPad digitalPad = null; + private AnalogStick stick = null; + private AnalogStick stick2 = null; - private DigitalButton buttonA = null; - private DigitalButton buttonB = null; - private DigitalButton buttonX = null; - private DigitalButton buttonY = null; - private DigitalButton buttonLT = null; - private DigitalButton buttonRT = null; - private DigitalButton buttonLB = null; - private DigitalButton buttonRB = null; - - private AnalogStick stick = null; - private AnalogStick stick2 = null; - - private DigitalButton buttonConfigure = null; - - NvConnection connection = null; - - private int getPercentageV(int percent) - { - return (int)(((float)frame_layout.getHeight() / (float)100) * (float)percent); - } - - private int getPercentageH(int percent) - { - return (int)(((float)frame_layout.getWidth() / (float)100) * (float)percent); - } - - private void setPercentilePosition(RelativeLayout.LayoutParams parm, float pos_x, float pos_y) - { - parm.setMargins( - (int)(((float)frame_layout.getWidth() / (float)100 * pos_x) - ((float)parm.width / (float)2)), - (int)(((float)frame_layout.getHeight() / (float)100 * pos_y) - ((float)parm.height / (float)2)), - 0, - 0 - ); - } - - void refreshLayout() - { - relative_layout.removeAllViews(); - - layoutParamsDPad = new RelativeLayout.LayoutParams(getPercentageV(30), getPercentageV(30)); - - layoutParamsStick = new RelativeLayout.LayoutParams(getPercentageV(40), getPercentageV(40)); - layoutParamsStick2 = new RelativeLayout.LayoutParams(getPercentageV(40), getPercentageV(40)); - - layoutParamsButtonA = new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); - layoutParamsButtonB = new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); - layoutParamsButtonX = new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); - layoutParamsButtonY = new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); - layoutParamsButtonLT = new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); - layoutParamsButtonRT = new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); - - layoutParamsButtonLB = new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); - layoutParamsButtonRB = new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); - - layoutParamsButtonStart = new RelativeLayout.LayoutParams(getPercentageH(12), getPercentageV(8)); - layoutParamsButtonSelect = new RelativeLayout.LayoutParams(getPercentageH(12), getPercentageV(8)); - - layoutParamsButtonConfigure = new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); - - setPercentilePosition(layoutParamsDPad, 10, 35); - - setPercentilePosition(layoutParamsStick, 22, 78); - setPercentilePosition(layoutParamsStick2, 78, 78); - - setPercentilePosition(layoutParamsButtonA, 85, 52); - setPercentilePosition(layoutParamsButtonB, 92, 47); - setPercentilePosition(layoutParamsButtonX, 85, 40); - setPercentilePosition(layoutParamsButtonY, 92, 35); - - setPercentilePosition(layoutParamsButtonLT, 95, 68); - setPercentilePosition(layoutParamsButtonRT, 95, 80); - - setPercentilePosition(layoutParamsButtonLB, 85, 28); - setPercentilePosition(layoutParamsButtonRB, 92, 23); - - setPercentilePosition(layoutParamsButtonSelect, 43, 94); - setPercentilePosition(layoutParamsButtonStart, 57, 94); - - setPercentilePosition(layoutParamsButtonConfigure, 93, 7); - - relative_layout.addView(digitalPad, layoutParamsDPad); - - relative_layout.addView(stick, layoutParamsStick); - relative_layout.addView(stick2, layoutParamsStick2); - - relative_layout.addView(buttonA, layoutParamsButtonA); - relative_layout.addView(buttonB, layoutParamsButtonB); - relative_layout.addView(buttonX, layoutParamsButtonX); - relative_layout.addView(buttonY, layoutParamsButtonY); - relative_layout.addView(buttonLT, layoutParamsButtonLT); - relative_layout.addView(buttonRT, layoutParamsButtonRT); - relative_layout.addView(buttonLB, layoutParamsButtonLB); - relative_layout.addView(buttonRB, layoutParamsButtonRB); - - relative_layout.addView(buttonSelect, layoutParamsButtonSelect); - relative_layout.addView(buttonStart, layoutParamsButtonStart); - - relative_layout.addView(buttonConfigure, layoutParamsButtonConfigure); - } - - private DigitalButton createDigitalButton(String text, final int key, Context context) - { - DigitalButton button = new DigitalButton(context); - button.setText(text); - button.addDigitalButtonListener(new DigitalButton.DigitalButtonListener() { - @Override - public void onClick() { - inputMap |= key; - sendControllerInputPacket(); - } - - @Override - public void onLongClick() - { - - } - - @Override - public void onRelease() { - inputMap &= ~key; - sendControllerInputPacket(); - } - }); - - return button; - } + private DigitalButton buttonConfigure = null; public VirtualController(final NvConnection conn, FrameLayout layout, final Context context) { - this.connection = conn; - frame_layout = layout; + this.connection = conn; + this.frame_layout = layout; + this.context = context; relative_layout = new RelativeLayout(context); @@ -210,113 +86,113 @@ public class VirtualController frame_layout.addView(relative_layout); - digitalPad = new DigitalPad(context); - digitalPad.addDigitalPadListener(new DigitalPad.DigitalPadListener() - { - @Override - public void onDirectionChange(int direction) - { - do - { - if (direction == DigitalPad.DIGITAL_PAD_DIRECTION_NO_DIRECTION) - { - inputMap &= ~ControllerPacket.LEFT_FLAG; - inputMap &= ~ControllerPacket.RIGHT_FLAG; - inputMap &= ~ControllerPacket.UP_FLAG; - inputMap &= ~ControllerPacket.DOWN_FLAG; + digitalPad = new DigitalPad(context); + digitalPad.addDigitalPadListener(new DigitalPad.DigitalPadListener() + { + @Override + public void onDirectionChange(int direction) + { + do + { + if (direction == DigitalPad.DIGITAL_PAD_DIRECTION_NO_DIRECTION) + { + inputMap &= ~ControllerPacket.LEFT_FLAG; + inputMap &= ~ControllerPacket.RIGHT_FLAG; + inputMap &= ~ControllerPacket.UP_FLAG; + inputMap &= ~ControllerPacket.DOWN_FLAG; - break; - } + break; + } - if ((direction & DigitalPad.DIGITAL_PAD_DIRECTION_LEFT) > 0) - { - inputMap |= ControllerPacket.LEFT_FLAG; - } + if ((direction & DigitalPad.DIGITAL_PAD_DIRECTION_LEFT) > 0) + { + inputMap |= ControllerPacket.LEFT_FLAG; + } - if ((direction & DigitalPad.DIGITAL_PAD_DIRECTION_RIGHT) > 0) - { - inputMap |= ControllerPacket.RIGHT_FLAG; - } + if ((direction & DigitalPad.DIGITAL_PAD_DIRECTION_RIGHT) > 0) + { + inputMap |= ControllerPacket.RIGHT_FLAG; + } - if ((direction & DigitalPad.DIGITAL_PAD_DIRECTION_UP) > 0) - { - inputMap |= ControllerPacket.UP_FLAG; - } + if ((direction & DigitalPad.DIGITAL_PAD_DIRECTION_UP) > 0) + { + inputMap |= ControllerPacket.UP_FLAG; + } - if ((direction & DigitalPad.DIGITAL_PAD_DIRECTION_DOWN) > 0) - { - inputMap |= ControllerPacket.DOWN_FLAG; - } - } - while (false); + if ((direction & DigitalPad.DIGITAL_PAD_DIRECTION_DOWN) > 0) + { + inputMap |= ControllerPacket.DOWN_FLAG; + } + } + while (false); - sendControllerInputPacket(); - } - }); + sendControllerInputPacket(); + } + }); - buttonX = createDigitalButton("X", ControllerPacket.X_FLAG ,context); - buttonY = createDigitalButton("Y", ControllerPacket.Y_FLAG ,context); - buttonA = createDigitalButton("A", ControllerPacket.A_FLAG ,context); - buttonB = createDigitalButton("B", ControllerPacket.B_FLAG ,context); + buttonX = createDigitalButton("X", ControllerPacket.X_FLAG, context); + buttonY = createDigitalButton("Y", ControllerPacket.Y_FLAG, context); + buttonA = createDigitalButton("A", ControllerPacket.A_FLAG, context); + buttonB = createDigitalButton("B", ControllerPacket.B_FLAG, context); buttonLT = new DigitalButton(context); buttonLT.setText("LT"); - buttonLT.addDigitalButtonListener(new DigitalButton.DigitalButtonListener() - { - @Override - public void onClick() - { - leftTrigger = (byte) (1 * 0xFF); + buttonLT.addDigitalButtonListener(new DigitalButton.DigitalButtonListener() + { + @Override + public void onClick() + { + leftTrigger = (byte) (1 * 0xFF); - sendControllerInputPacket(); - } + sendControllerInputPacket(); + } - @Override - public void onLongClick() - { + @Override + public void onLongClick() + { - } + } - @Override - public void onRelease() - { - leftTrigger = (byte) (0 * 0xFF); + @Override + public void onRelease() + { + leftTrigger = (byte) (0 * 0xFF); - sendControllerInputPacket(); - } - }); + sendControllerInputPacket(); + } + }); buttonRT = new DigitalButton(context); buttonRT.setText("RT"); - buttonRT.addDigitalButtonListener(new DigitalButton.DigitalButtonListener() - { - @Override - public void onClick() - { - rightTrigger = (byte) (0xFF); + buttonRT.addDigitalButtonListener(new DigitalButton.DigitalButtonListener() + { + @Override + public void onClick() + { + rightTrigger = (byte) (0xFF); - sendControllerInputPacket(); - } + sendControllerInputPacket(); + } - @Override - public void onLongClick() - { + @Override + public void onLongClick() + { - } + } - @Override - public void onRelease() - { - rightTrigger = (byte) (0); + @Override + public void onRelease() + { + rightTrigger = (byte) (0); - sendControllerInputPacket(); - } - }); + sendControllerInputPacket(); + } + }); - buttonLB = createDigitalButton("LB", ControllerPacket.LB_FLAG ,context); - buttonRB = createDigitalButton("RB", ControllerPacket.RB_FLAG ,context); + buttonLB = createDigitalButton("LB", ControllerPacket.LB_FLAG, context); + buttonRB = createDigitalButton("RB", ControllerPacket.RB_FLAG, context); - stick = new AnalogStick(context); + stick = new AnalogStick(context); stick.addAnalogStickListener(new AnalogStick.AnalogStickListener() { @@ -326,30 +202,30 @@ public class VirtualController leftStickX = (short) (x * 0x7FFE); leftStickY = (short) (y * 0x7FFE); - _DBG("LEFT STICK MOVEMENT X: "+ leftStickX + " Y: " + leftStickY); + _DBG("LEFT STICK MOVEMENT X: " + leftStickX + " Y: " + leftStickY); sendControllerInputPacket(); } - @Override - public void onClick() - { - } + @Override + public void onClick() + { + } - @Override - public void onDoubleClick() - { - inputMap |= ControllerPacket.LS_CLK_FLAG; + @Override + public void onDoubleClick() + { + inputMap |= ControllerPacket.LS_CLK_FLAG; - sendControllerInputPacket(); - } + sendControllerInputPacket(); + } - @Override - public void onRevoke() - { - inputMap &= ~ControllerPacket.LS_CLK_FLAG; + @Override + public void onRevoke() + { + inputMap &= ~ControllerPacket.LS_CLK_FLAG; - sendControllerInputPacket(); - } + sendControllerInputPacket(); + } }); stick2 = new AnalogStick(context); @@ -361,83 +237,229 @@ public class VirtualController rightStickX = (short) (x * 0x7FFE); rightStickY = (short) (y * 0x7FFE); - _DBG("RIGHT STICK MOVEMENT X: "+ rightStickX + " Y: " + rightStickY); + _DBG("RIGHT STICK MOVEMENT X: " + rightStickX + " Y: " + rightStickY); sendControllerInputPacket(); } - @Override - public void onClick() - { - } + @Override + public void onClick() + { + } - @Override - public void onDoubleClick() - { - inputMap |= ControllerPacket.RS_CLK_FLAG; + @Override + public void onDoubleClick() + { + inputMap |= ControllerPacket.RS_CLK_FLAG; - sendControllerInputPacket(); - } + sendControllerInputPacket(); + } - @Override - public void onRevoke() - { - inputMap &= ~ControllerPacket.RS_CLK_FLAG; + @Override + public void onRevoke() + { + inputMap &= ~ControllerPacket.RS_CLK_FLAG; - sendControllerInputPacket(); - } - }); + sendControllerInputPacket(); + } + }); - buttonStart = createDigitalButton("START", ControllerPacket.PLAY_FLAG, context); - buttonSelect = createDigitalButton("SELECT", ControllerPacket.SPECIAL_BUTTON_FLAG, context); + buttonStart = createDigitalButton("START", ControllerPacket.PLAY_FLAG, context); + buttonSelect = + createDigitalButton("SELECT", ControllerPacket.SPECIAL_BUTTON_FLAG, context); - buttonConfigure = new DigitalButton(context); - buttonConfigure.setIcon(R.drawable.settings); - buttonConfigure.addDigitalButtonListener(new DigitalButton.DigitalButtonListener() - { - @Override - public void onClick() { + buttonConfigure = new DigitalButton(context); + buttonConfigure.setIcon(R.drawable.settings); + buttonConfigure.addDigitalButtonListener(new DigitalButton.DigitalButtonListener() + { + @Override + public void onClick() + { - } + } - @Override - public void onLongClick() - { - Intent virtualControllerConfiguration = new Intent(context,VirtualControllerSettings.class); + @Override + public void onLongClick() + { + openSettingsDialog(); + } - virtualControllerConfiguration.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + @Override + public void onRelease() + { - context.startActivity(virtualControllerConfiguration); - - } - - @Override - public void onRelease() { - - } - }); + } + }); refreshLayout(); } + private static final void _DBG(String text) + { + if (_PRINT_DEBUG_INFORMATION) + { + System.out.println("VirtualController: " + text); + } + } + + private int getPercentageV(int percent) + { + return (int) (((float) frame_layout.getHeight() / (float) 100) * (float) percent); + } + + private int getPercentageH(int percent) + { + return (int) (((float) frame_layout.getWidth() / (float) 100) * (float) percent); + } + + private void setPercentilePosition(RelativeLayout.LayoutParams parm, float pos_x, float pos_y) + { + parm.setMargins( + (int) (((float) frame_layout.getWidth() / (float) 100 * pos_x) - ((float) parm.width / (float) 2)), + (int) (((float) frame_layout.getHeight() / (float) 100 * pos_y) - ((float) parm.height / (float) 2)), + 0, + 0 + ); + } + + public void openSettingsDialog() + { + Intent virtualControllerConfiguration = + new Intent(context, VirtualControllerSettings.class); + virtualControllerConfiguration.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + context.startActivity(virtualControllerConfiguration); + + } + + void refreshLayout() + { + relative_layout.removeAllViews(); + + layoutParamsDPad = + new RelativeLayout.LayoutParams(getPercentageV(30), getPercentageV(30)); + + layoutParamsStick = + new RelativeLayout.LayoutParams(getPercentageV(40), getPercentageV(40)); + layoutParamsStick2 = + new RelativeLayout.LayoutParams(getPercentageV(40), getPercentageV(40)); + + layoutParamsButtonA = + new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); + layoutParamsButtonB = + new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); + layoutParamsButtonX = + new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); + layoutParamsButtonY = + new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); + layoutParamsButtonLT = + new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); + layoutParamsButtonRT = + new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); + + layoutParamsButtonLB = + new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); + layoutParamsButtonRB = + new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); + + layoutParamsButtonStart = + new RelativeLayout.LayoutParams(getPercentageH(12), getPercentageV(8)); + layoutParamsButtonSelect = + new RelativeLayout.LayoutParams(getPercentageH(12), getPercentageV(8)); + + layoutParamsButtonConfigure = + new RelativeLayout.LayoutParams(getPercentageV(10), getPercentageV(10)); + + setPercentilePosition(layoutParamsDPad, 10, 35); + + setPercentilePosition(layoutParamsStick, 22, 78); + setPercentilePosition(layoutParamsStick2, 78, 78); + + setPercentilePosition(layoutParamsButtonA, 85, 52); + setPercentilePosition(layoutParamsButtonB, 92, 47); + setPercentilePosition(layoutParamsButtonX, 85, 40); + setPercentilePosition(layoutParamsButtonY, 92, 35); + + setPercentilePosition(layoutParamsButtonLT, 95, 68); + setPercentilePosition(layoutParamsButtonRT, 95, 80); + + setPercentilePosition(layoutParamsButtonLB, 85, 28); + setPercentilePosition(layoutParamsButtonRB, 92, 23); + + setPercentilePosition(layoutParamsButtonSelect, 43, 94); + setPercentilePosition(layoutParamsButtonStart, 57, 94); + + setPercentilePosition(layoutParamsButtonConfigure, 93, 7); + + relative_layout.addView(digitalPad, layoutParamsDPad); + + relative_layout.addView(stick, layoutParamsStick); + relative_layout.addView(stick2, layoutParamsStick2); + + relative_layout.addView(buttonA, layoutParamsButtonA); + relative_layout.addView(buttonB, layoutParamsButtonB); + relative_layout.addView(buttonX, layoutParamsButtonX); + relative_layout.addView(buttonY, layoutParamsButtonY); + relative_layout.addView(buttonLT, layoutParamsButtonLT); + relative_layout.addView(buttonRT, layoutParamsButtonRT); + relative_layout.addView(buttonLB, layoutParamsButtonLB); + relative_layout.addView(buttonRB, layoutParamsButtonRB); + + relative_layout.addView(buttonSelect, layoutParamsButtonSelect); + relative_layout.addView(buttonStart, layoutParamsButtonStart); + + relative_layout.addView(buttonConfigure, layoutParamsButtonConfigure); + } + + private DigitalButton createDigitalButton(String text, final int key, Context context) + { + DigitalButton button = new DigitalButton(context); + button.setText(text); + button.addDigitalButtonListener(new DigitalButton.DigitalButtonListener() + { + @Override + public void onClick() + { + inputMap |= key; + sendControllerInputPacket(); + } + + @Override + public void onLongClick() + { + + } + + @Override + public void onRelease() + { + inputMap &= ~key; + sendControllerInputPacket(); + } + }); + + return button; + } + private void sendControllerInputPacket() { - try { - _DBG("INPUT_MAP + " + inputMap); - _DBG("LEFT_TRIGGER " + leftTrigger); - _DBG("RIGHT_TRIGGER " + rightTrigger); - _DBG("LEFT STICK X: " + leftStickX + " Y: " + leftStickY); - _DBG("RIGHT STICK X: " + rightStickX + " Y: " + rightStickY); - _DBG("RIGHT STICK X: " + rightStickX + " Y: " + rightStickY); + try + { + _DBG("INPUT_MAP + " + inputMap); + _DBG("LEFT_TRIGGER " + leftTrigger); + _DBG("RIGHT_TRIGGER " + rightTrigger); + _DBG("LEFT STICK X: " + leftStickX + " Y: " + leftStickY); + _DBG("RIGHT STICK X: " + rightStickX + " Y: " + rightStickY); + _DBG("RIGHT STICK X: " + rightStickX + " Y: " + rightStickY); - if (connection != null) - { - connection.sendControllerInput(inputMap, leftTrigger, rightTrigger, - leftStickX, leftStickY, rightStickX, rightStickY); - } - } - catch (Exception e) - { - e.printStackTrace(); - } + if (connection != null) + { + connection.sendControllerInput(inputMap, leftTrigger, rightTrigger, + leftStickX, leftStickY, rightStickX, rightStickY); + } + } + catch (Exception e) + { + e.printStackTrace(); + } } } diff --git a/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualControllerConfiguration.java b/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualControllerConfiguration.java index 6d8ecbd9..25f952be 100644 --- a/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualControllerConfiguration.java +++ b/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualControllerConfiguration.java @@ -1,9 +1,7 @@ package com.limelight.binding.input.virtual_controller; import android.app.Activity; -import android.graphics.Color; import android.os.Bundle; -import android.os.PersistableBundle; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; @@ -16,29 +14,30 @@ import com.limelight.R; */ public class VirtualControllerConfiguration extends Activity { - VirtualController virtualController; + VirtualController virtualController; - @Override - protected void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); - // We don't want a title bar - requestWindowFeature(Window.FEATURE_NO_TITLE); + // We don't want a title bar + requestWindowFeature(Window.FEATURE_NO_TITLE); - // Full-screen and don't let the display go off - getWindow().addFlags( - WindowManager.LayoutParams.FLAG_FULLSCREEN | - WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + // Full-screen and don't let the display go off + getWindow().addFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN | + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - // Inflate the content - setContentView(R.layout.activity_configure_virtual_controller); + // Inflate the content + setContentView(R.layout.activity_configure_virtual_controller); - FrameLayout frameLayout = (FrameLayout) findViewById(R.id.configure_virtual_controller_frameLayout); + FrameLayout frameLayout = + (FrameLayout) findViewById(R.id.configure_virtual_controller_frameLayout); - // start with configuration constructor - virtualController = new VirtualController(null, frameLayout, this); + // start with configuration constructor + virtualController = new VirtualController(null, frameLayout, this); - Toast.makeText(getApplicationContext(), "Not implemented yet!", Toast.LENGTH_SHORT).show(); - } + Toast.makeText(getApplicationContext(), "Not implemented yet!", Toast.LENGTH_SHORT).show(); + } } diff --git a/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualControllerElement.java b/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualControllerElement.java new file mode 100644 index 00000000..72e30c32 --- /dev/null +++ b/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualControllerElement.java @@ -0,0 +1,45 @@ +package com.limelight.binding.input.virtual_controller; + +import android.content.Context; +import android.view.View; + +/** + * Created by Karim on 27.01.2015. + */ +public abstract class VirtualControllerElement extends View +{ + protected static boolean _PRINT_DEBUG_INFORMATION = false; + protected int normalColor = 0xF0888888; + protected int pressedColor = 0xF00000FF; + + protected VirtualControllerElement(Context context) + { + super(context); + } + + protected static final void _DBG(String text) + { + if (_PRINT_DEBUG_INFORMATION) + { + System.out.println("DigitalButton: " + text); + } + } + + public void setColors(int normalColor, int pressedColor) + { + this.normalColor = normalColor; + this.pressedColor = pressedColor; + + invalidate(); + } + + protected final float getPercent(float value, float percent) + { + return value / 100 * percent; + } + + protected final int getCorrectWidth() + { + return getWidth() > getHeight() ? getHeight() : getWidth(); + } +} diff --git a/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualControllerSettings.java b/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualControllerSettings.java index 3432b63e..621d9b19 100644 --- a/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualControllerSettings.java +++ b/app/src/main/java/com/limelight/binding/input/virtual_controller/VirtualControllerSettings.java @@ -2,46 +2,28 @@ package com.limelight.binding.input.virtual_controller; import android.app.Activity; import android.os.Bundle; -import android.view.MenuInflater; -import android.view.View; import android.view.Window; -import android.view.WindowManager; -import android.widget.FrameLayout; -import android.widget.PopupMenu; import android.widget.Toast; import com.limelight.R; -import org.apache.http.util.VersionInfo; - /** * Created by Karim on 26.01.2015. */ public class VirtualControllerSettings extends Activity { - private static VirtualController controller = null; - private static View view = null; + private VirtualController controller = null; - static void setController(VirtualController value) - { - controller = value; - } + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + // We don't want a title bar + requestWindowFeature(Window.FEATURE_NO_TITLE); - static void setView(View value) - { - view = value; - } + // Inflate the content + setContentView(R.layout.activity_virtual_controller_settings); - @Override - protected void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - // We don't want a title bar - requestWindowFeature(Window.FEATURE_NO_TITLE); - - // Inflate the content - setContentView(R.layout.activity_virtual_controller_settings); - - Toast.makeText(getApplicationContext(), "Not implemented yet!", Toast.LENGTH_SHORT).show(); - } + Toast.makeText(getApplicationContext(), "Not implemented yet!", Toast.LENGTH_SHORT).show(); + } } diff --git a/app/src/main/java/com/limelight/computers/ComputerManagerService.java b/app/src/main/java/com/limelight/computers/ComputerManagerService.java index 182ff690..afafbe58 100644 --- a/app/src/main/java/com/limelight/computers/ComputerManagerService.java +++ b/app/src/main/java/com/limelight/computers/ComputerManagerService.java @@ -251,6 +251,11 @@ public class ComputerManagerService extends Service { ((!details.name.isEmpty() && !tuple.computer.name.isEmpty()) && tuple.computer.name.equals(details.name))) { + // Update details anyway in case this machine has been re-added by IP + // after not being reachable by our existing information + tuple.computer.localIp = details.localIp; + tuple.computer.remoteIp = details.remoteIp; + // Start a polling thread if polling is active if (pollingActive && tuple.thread == null) { tuple.thread = createPollingThread(details); diff --git a/app/src/main/java/com/limelight/grid/AppGridAdapter.java b/app/src/main/java/com/limelight/grid/AppGridAdapter.java index c2d1624b..199eaf03 100644 --- a/app/src/main/java/com/limelight/grid/AppGridAdapter.java +++ b/app/src/main/java/com/limelight/grid/AppGridAdapter.java @@ -1,16 +1,30 @@ package com.limelight.grid; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.widget.ImageView; import android.widget.TextView; import com.koushikdutta.async.future.FutureCallback; +import com.koushikdutta.ion.ImageViewBitmapInfo; import com.koushikdutta.ion.Ion; +import com.koushikdutta.ion.bitmap.BitmapInfo; import com.limelight.AppView; +import com.limelight.LimeLog; import com.limelight.R; import com.limelight.binding.PlatformBinding; import com.limelight.nvstream.http.LimelightCryptoProvider; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.net.InetAddress; import java.net.Socket; import java.security.KeyManagementException; @@ -32,14 +46,15 @@ import java.security.cert.X509Certificate; public class AppGridAdapter extends GenericGridAdapter { + private boolean listMode; private InetAddress address; private String uniqueId; private LimelightCryptoProvider cryptoProvider; private SSLContext sslContext; private final HashMap pendingRequests = new HashMap(); - public AppGridAdapter(Context context, InetAddress address, String uniqueId) throws NoSuchAlgorithmException, KeyManagementException { - super(context, R.layout.app_grid_item, R.drawable.image_loading); + public AppGridAdapter(Context context, boolean listMode, InetAddress address, String uniqueId) throws NoSuchAlgorithmException, KeyManagementException { + super(context, listMode ? R.layout.simple_row : R.layout.app_grid_item, R.drawable.image_loading); this.address = address; this.uniqueId = uniqueId; @@ -107,7 +122,9 @@ public class AppGridAdapter extends GenericGridAdapter { } for (Future f : tempMap.values()) { - f.cancel(true); + if (!f.isCancelled() && !f.isDone()) { + f.cancel(true); + } } synchronized (pendingRequests) { @@ -118,28 +135,92 @@ public class AppGridAdapter extends GenericGridAdapter { } } + private Bitmap checkBitmapCache(String addrStr, int appId) { + File addrFolder = new File(context.getCacheDir(), addrStr); + if (addrFolder.isDirectory()) { + File bitmapFile = new File(addrFolder, appId+".png"); + if (bitmapFile.exists()) { + InputStream fileIn = null; + try { + fileIn = new BufferedInputStream(new FileInputStream(bitmapFile)); + Bitmap bm = BitmapFactory.decodeStream(fileIn); + if (bm == null) { + // The image seems corrupt + bitmapFile.delete(); + } + + return bm; + } catch (IOException e) { + e.printStackTrace(); + bitmapFile.delete(); + } finally { + if (fileIn != null) { + try { + fileIn.close(); + } catch (IOException ignored) {} + } + } + } + } + + return null; + } + + // TODO: Handle pruning of bitmap cache + private void populateBitmapCache(String addrStr, int appId, Bitmap bitmap) { + File addrFolder = new File(context.getCacheDir(), addrStr); + addrFolder.mkdirs(); + + File bitmapFile = new File(addrFolder, appId+".png"); + try { + // PNG ignores quality setting + bitmap.compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(bitmapFile)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + @Override - public boolean populateImageView(final ImageView imgView, AppView.AppObject obj) { + public boolean populateImageView(final ImageView imgView, final AppView.AppObject obj) { // Set SSL contexts correctly to allow us to authenticate Ion.getDefault(imgView.getContext()).getHttpClient().getSSLSocketMiddleware().setTrustManagers(trustAllCerts); Ion.getDefault(imgView.getContext()).getHttpClient().getSSLSocketMiddleware().setSSLContext(sslContext); - // Set off the deferred image load + // Check the on-disk cache + Bitmap cachedBitmap = checkBitmapCache(address.getHostAddress(), obj.app.getAppId()); + if (cachedBitmap != null) { + // Cache hit; we're done + LimeLog.info("Image cache hit for ("+address.getHostAddress()+", "+obj.app.getAppId()+")"); + imgView.setImageBitmap(cachedBitmap); + return true; + } + + // Kick off the deferred image load synchronized (pendingRequests) { - Future f = Ion.with(imgView) + Future f = Ion.with(imgView) .placeholder(defaultImageRes) .error(defaultImageRes) .load("https://" + address.getHostAddress() + ":47984/appasset?uniqueid=" + uniqueId + "&appid=" + obj.app.getAppId() + "&AssetType=2&AssetIdx=0") - .setCallback(new FutureCallback() { - @Override - public void onCompleted(Exception e, ImageView result) { - synchronized (pendingRequests) { - pendingRequests.remove(imgView); - } - } - }); + .withBitmapInfo() + .setCallback( + new FutureCallback() { + @Override + public void onCompleted(Exception e, ImageViewBitmapInfo result) { + synchronized (pendingRequests) { + pendingRequests.remove(imgView); + } + + // Populate the cache if we got an image back + if (result != null && + result.getBitmapInfo() != null && + result.getBitmapInfo().bitmap != null) { + populateBitmapCache(address.getHostAddress(), obj.app.getAppId(), + result.getBitmapInfo().bitmap); + } + } + }); pendingRequests.put(imgView, f); } diff --git a/app/src/main/java/com/limelight/grid/GenericGridAdapter.java b/app/src/main/java/com/limelight/grid/GenericGridAdapter.java index d2d68a14..f0a17618 100644 --- a/app/src/main/java/com/limelight/grid/GenericGridAdapter.java +++ b/app/src/main/java/com/limelight/grid/GenericGridAdapter.java @@ -60,17 +60,21 @@ public abstract class GenericGridAdapter extends BaseAdapter { ImageView overlayView = (ImageView) convertView.findViewById(R.id.grid_overlay); TextView txtView = (TextView) convertView.findViewById(R.id.grid_text); - if (!populateImageView(imgView, itemList.get(i))) { - imgView.setImageResource(defaultImageRes); + if (imgView != null) { + if (!populateImageView(imgView, itemList.get(i))) { + imgView.setImageResource(defaultImageRes); + } } if (!populateTextView(txtView, itemList.get(i))) { txtView.setText(itemList.get(i).toString()); } - if (!populateOverlayView(overlayView, itemList.get(i))) { - overlayView.setVisibility(View.INVISIBLE); - } - else { - overlayView.setVisibility(View.VISIBLE); + if (overlayView != null) { + if (!populateOverlayView(overlayView, itemList.get(i))) { + overlayView.setVisibility(View.INVISIBLE); + } + else { + overlayView.setVisibility(View.VISIBLE); + } } return convertView; diff --git a/app/src/main/java/com/limelight/grid/PcGridAdapter.java b/app/src/main/java/com/limelight/grid/PcGridAdapter.java index a191381c..8e370de3 100644 --- a/app/src/main/java/com/limelight/grid/PcGridAdapter.java +++ b/app/src/main/java/com/limelight/grid/PcGridAdapter.java @@ -8,14 +8,27 @@ import com.limelight.PcView; import com.limelight.R; import com.limelight.nvstream.http.ComputerDetails; +import java.util.Collections; +import java.util.Comparator; + public class PcGridAdapter extends GenericGridAdapter { - public PcGridAdapter(Context context) { - super(context, R.layout.pc_grid_item, R.drawable.computer); + public PcGridAdapter(Context context, boolean listMode) { + super(context, listMode ? R.layout.simple_row : R.layout.pc_grid_item, R.drawable.computer); } public void addComputer(PcView.ComputerObject computer) { itemList.add(computer); + sortList(); + } + + private void sortList() { + Collections.sort(itemList, new Comparator() { + @Override + public int compare(PcView.ComputerObject lhs, PcView.ComputerObject rhs) { + return lhs.details.name.compareTo(rhs.details.name); + } + }); } public boolean removeComputer(PcView.ComputerObject computer) { diff --git a/app/src/main/java/com/limelight/preferences/AddComputerManually.java b/app/src/main/java/com/limelight/preferences/AddComputerManually.java index 9e8b1584..c2eaa19a 100644 --- a/app/src/main/java/com/limelight/preferences/AddComputerManually.java +++ b/app/src/main/java/com/limelight/preferences/AddComputerManually.java @@ -2,6 +2,7 @@ package com.limelight.preferences; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.Locale; import java.util.concurrent.LinkedBlockingQueue; import com.limelight.computers.ComputerManagerService; @@ -15,8 +16,10 @@ import android.app.Service; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; +import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; +import android.preference.Preference; import android.view.KeyEvent; import android.view.inputmethod.EditorInfo; import android.widget.TextView; @@ -132,6 +135,13 @@ public class AddComputerManually extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + String locale = PreferenceConfiguration.readPreferences(this).language; + if (!locale.equals(PreferenceConfiguration.DEFAULT_LANGUAGE)) { + Configuration config = new Configuration(getResources().getConfiguration()); + config.locale = new Locale(locale); + getResources().updateConfiguration(config, getResources().getDisplayMetrics()); + } + setContentView(R.layout.activity_add_computer_manually); UiHelper.notifyNewRootView(this); diff --git a/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java b/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java index 50f0159a..73aee4a3 100644 --- a/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java +++ b/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java @@ -13,6 +13,8 @@ public class PreferenceConfiguration { private static final String DISABLE_TOASTS_PREF_STRING = "checkbox_disable_warnings"; private static final String HOST_AUDIO_PREF_STRING = "checkbox_host_audio"; private static final String DEADZONE_PREF_STRING = "seekbar_deadzone"; + private static final String LANGUAGE_PREF_STRING = "list_languages"; + private static final String LIST_MODE_PREF_STRING = "checkbox_list_mode"; private static final String VIRTUAL_CONTROLLER_ENABLE = "virtual_controller_checkbox_enable"; private static final Boolean VIRTUAL_CONTROLLER_ENABLE_DEFAULT = true; @@ -30,6 +32,8 @@ public class PreferenceConfiguration { private static final boolean DEFAULT_DISABLE_TOASTS = false; private static final boolean DEFAULT_HOST_AUDIO = false; private static final int DEFAULT_DEADZONE = 15; + public static final String DEFAULT_LANGUAGE = "default"; + private static final boolean DEFAULT_LIST_MODE = false; public static final int FORCE_HARDWARE_DECODER = -1; public static final int AUTOSELECT_DECODER = 0; @@ -40,6 +44,8 @@ public class PreferenceConfiguration { public int decoder; public int deadzonePercentage; public boolean stretchVideo, enableSops, playHostAudio, disableWarnings; + public String language; + public boolean listMode; public boolean virtualController_enable; @@ -140,11 +146,14 @@ public class PreferenceConfiguration { config.deadzonePercentage = prefs.getInt(DEADZONE_PREF_STRING, DEFAULT_DEADZONE); + config.language = prefs.getString(LANGUAGE_PREF_STRING, DEFAULT_LANGUAGE); + // Checkbox preferences config.disableWarnings = prefs.getBoolean(DISABLE_TOASTS_PREF_STRING, DEFAULT_DISABLE_TOASTS); config.enableSops = prefs.getBoolean(SOPS_PREF_STRING, DEFAULT_SOPS); config.stretchVideo = prefs.getBoolean(STRETCH_PREF_STRING, DEFAULT_STRETCH); config.playHostAudio = prefs.getBoolean(HOST_AUDIO_PREF_STRING, DEFAULT_HOST_AUDIO); + config.listMode = prefs.getBoolean(LIST_MODE_PREF_STRING, DEFAULT_LIST_MODE); config.virtualController_enable = prefs.getBoolean(VIRTUAL_CONTROLLER_ENABLE, VIRTUAL_CONTROLLER_ENABLE_DEFAULT); diff --git a/app/src/main/java/com/limelight/preferences/StreamSettings.java b/app/src/main/java/com/limelight/preferences/StreamSettings.java index 5b9e361f..821e7449 100644 --- a/app/src/main/java/com/limelight/preferences/StreamSettings.java +++ b/app/src/main/java/com/limelight/preferences/StreamSettings.java @@ -2,22 +2,33 @@ package com.limelight.preferences; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.Configuration; import android.os.Bundle; import android.app.Activity; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; +import com.limelight.PcView; import com.limelight.R; import com.limelight.binding.input.virtual_controller.VirtualController; import com.limelight.binding.input.virtual_controller.VirtualControllerConfiguration; import com.limelight.utils.UiHelper; +import java.util.Locale; + public class StreamSettings extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + String locale = PreferenceConfiguration.readPreferences(this).language; + if (!locale.equals(PreferenceConfiguration.DEFAULT_LANGUAGE)) { + Configuration config = new Configuration(getResources().getConfiguration()); + config.locale = new Locale(locale); + getResources().updateConfiguration(config, getResources().getDisplayMetrics()); + } + setContentView(R.layout.activity_stream_settings); getFragmentManager().beginTransaction().replace( R.id.stream_settings, new SettingsFragment() @@ -26,6 +37,16 @@ public class StreamSettings extends Activity { UiHelper.notifyNewRootView(this); } + @Override + public void onBackPressed() { + finish(); + + // Restart the PC view to apply UI changes + Intent intent = new Intent(this, PcView.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent, null); + } + public static class SettingsFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/com/limelight/ui/AdapterFragment.java b/app/src/main/java/com/limelight/ui/AdapterFragment.java new file mode 100644 index 00000000..8076fa06 --- /dev/null +++ b/app/src/main/java/com/limelight/ui/AdapterFragment.java @@ -0,0 +1,35 @@ +package com.limelight.ui; + + +import android.app.Activity; +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; + +import com.limelight.R; + +public class AdapterFragment extends Fragment { + private AdapterFragmentCallbacks callbacks; + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + callbacks = (AdapterFragmentCallbacks) activity; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(callbacks.getAdapterFragmentLayoutId(), container, false); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + callbacks.receiveAbsListView((AbsListView) getView().findViewById(R.id.fragmentView)); + } +} diff --git a/app/src/main/java/com/limelight/ui/AdapterFragmentCallbacks.java b/app/src/main/java/com/limelight/ui/AdapterFragmentCallbacks.java new file mode 100644 index 00000000..4ebade79 --- /dev/null +++ b/app/src/main/java/com/limelight/ui/AdapterFragmentCallbacks.java @@ -0,0 +1,8 @@ +package com.limelight.ui; + +import android.widget.AbsListView; + +public interface AdapterFragmentCallbacks { + public int getAdapterFragmentLayoutId(); + public void receiveAbsListView(AbsListView gridView); +} diff --git a/app/src/main/java/com/limelight/ui/GameGestures.java b/app/src/main/java/com/limelight/ui/GameGestures.java new file mode 100644 index 00000000..4589ecc9 --- /dev/null +++ b/app/src/main/java/com/limelight/ui/GameGestures.java @@ -0,0 +1,5 @@ +package com.limelight.ui; + +public interface GameGestures { + public void showKeyboard(); +} diff --git a/app/src/main/res/drawable/list_view_unselected.xml b/app/src/main/res/drawable/list_view_unselected.xml new file mode 100644 index 00000000..b691f3a2 --- /dev/null +++ b/app/src/main/res/drawable/list_view_unselected.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_pc_view.xml b/app/src/main/res/layout-land/activity_pc_view.xml index 91169932..523341b9 100644 --- a/app/src/main/res/layout-land/activity_pc_view.xml +++ b/app/src/main/res/layout-land/activity_pc_view.xml @@ -33,12 +33,10 @@ android:text="@string/searching_pc"/> - - - - + android:layout_below="@+id/appListText"/> @@ -93,7 +93,8 @@ android:text="Color 2"/> diff --git a/app/src/main/res/layout/app_grid_view.xml b/app/src/main/res/layout/app_grid_view.xml new file mode 100644 index 00000000..6926a933 --- /dev/null +++ b/app/src/main/res/layout/app_grid_view.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_view.xml b/app/src/main/res/layout/list_view.xml new file mode 100644 index 00000000..c41508ee --- /dev/null +++ b/app/src/main/res/layout/list_view.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/pc_grid_view.xml b/app/src/main/res/layout/pc_grid_view.xml new file mode 100644 index 00000000..98a0c9cd --- /dev/null +++ b/app/src/main/res/layout/pc_grid_view.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/simple_row.xml b/app/src/main/res/layout/simple_row.xml new file mode 100644 index 00000000..92ba894d --- /dev/null +++ b/app/src/main/res/layout/simple_row.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 77cb9740..705bd4f4 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -94,11 +94,17 @@ Aggiusta deadzone degli stick analogici % + UI Settings + Lingua + Lingua da usare in Limelight + Use lists instead of grids + Display apps and PCs in lists instead of grids + Impostazioni Host Ottimizza le impostazioni dei giochi Permetti a GFE di modificare le impostazioni dei giochi per uno streaming ottimale Riproduci audio sul PC - Riproduci l\'audio sul computer e su questo dispositivo. Richiede GFE 2.1.2+ + Riproduci l\'audio sul computer e su questo dispositivo Impostazioni Avanzate Cambia decoder diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 73df0415..44a32405 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -13,6 +13,17 @@ 1080p60 + + Default + English + Italiano + + + default + en + it + + Auto-select Decoder Force Software Decoding diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ccd9898e..b9a58ee2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -94,11 +94,17 @@ Adjust analog stick deadzone % + UI Settings + Language + Language to use for Limelight + Use lists instead of grids + Display apps and PCs in lists instead of grids + Host Settings Optimize game settings Allow GFE to modify game settings for optimal streaming Play audio on PC - Play audio from the computer and this device. Requires GFE 2.1.2+ + Play audio from the computer and this device Advanced Settings Change decoder diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d5c608e9..91563473 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -46,6 +46,20 @@ android:summary="@string/summary_checkbox_host_audio" android:defaultValue="false" /> + + + + - \ No newline at end of file +