Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56b814e877 | |||
| 628ccd39d6 | |||
| 59db3f9b62 | |||
| 416f922b56 | |||
| b52a86e6cc | |||
| e523b5069e | |||
| e8ae8d9807 | |||
| 64e56a861d | |||
| c1bcd09c9b | |||
| 574258804f | |||
| 21ea3d8a2b | |||
| 6de4288a85 | |||
| a107b5e652 | |||
| b02db2c182 | |||
| f8a04cda7a | |||
| 226e8edefc |
+21
-15
@@ -72,13 +72,6 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/shaders" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/assets" type="java-test-resource" />
|
||||
@@ -86,6 +79,13 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
||||
@@ -107,13 +107,6 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
|
||||
@@ -121,19 +114,32 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndkBuild" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 24 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="bcprov-jdk15on-1.52" level="project" />
|
||||
<orderEntry type="library" exported="" name="bcpkix-jdk15on-1.52" level="project" />
|
||||
|
||||
+5
-5
@@ -4,15 +4,15 @@ import org.apache.tools.ant.taskdefs.condition.Os
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 24
|
||||
buildToolsVersion "24.0.3"
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion "25.0.0"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 24
|
||||
targetSdkVersion 25
|
||||
|
||||
versionName "4.7.1"
|
||||
versionCode = 105
|
||||
versionName "4.7.3"
|
||||
versionCode = 108
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
|
||||
Binary file not shown.
@@ -11,12 +11,14 @@ import com.limelight.grid.AppGridAdapter;
|
||||
import com.limelight.nvstream.http.ComputerDetails;
|
||||
import com.limelight.nvstream.http.NvApp;
|
||||
import com.limelight.nvstream.http.NvHTTP;
|
||||
import com.limelight.nvstream.http.PairingManager;
|
||||
import com.limelight.preferences.PreferenceConfiguration;
|
||||
import com.limelight.ui.AdapterFragment;
|
||||
import com.limelight.ui.AdapterFragmentCallbacks;
|
||||
import com.limelight.utils.CacheHelper;
|
||||
import com.limelight.utils.Dialog;
|
||||
import com.limelight.utils.ServerHelper;
|
||||
import com.limelight.utils.ShortcutHelper;
|
||||
import com.limelight.utils.SpinnerDialog;
|
||||
import com.limelight.utils.UiHelper;
|
||||
|
||||
@@ -43,14 +45,16 @@ import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
private AppGridAdapter appGridAdapter;
|
||||
private String uuidString;
|
||||
private ShortcutHelper shortcutHelper;
|
||||
|
||||
private ComputerDetails computer;
|
||||
private ComputerManagerService.ApplistPoller poller;
|
||||
private SpinnerDialog blockingLoadSpinner;
|
||||
private SpinnerDialog blockingLoadSpinner, blockingServerinfoSpinner;
|
||||
private String lastRawApplist;
|
||||
private int lastRunningAppId;
|
||||
private boolean suspendGridUpdates;
|
||||
private boolean inForeground;
|
||||
private boolean launchedFromShortcut;
|
||||
|
||||
private final static int START_OR_RESUME_ID = 1;
|
||||
private final static int QUIT_ID = 2;
|
||||
@@ -59,6 +63,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
|
||||
public final static String NAME_EXTRA = "Name";
|
||||
public final static String UUID_EXTRA = "UUID";
|
||||
public final static String SHORTCUT_EXTRA = "Shortcut";
|
||||
|
||||
private ComputerManagerService.ComputerManagerBinder managerBinder;
|
||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@@ -90,12 +95,15 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the app grid with cached data (if possible).
|
||||
// This must be done _before_ startComputerUpdates()
|
||||
// so the initial serverinfo response can update the running
|
||||
// icon.
|
||||
populateAppGridWithCache();
|
||||
|
||||
// Start updates
|
||||
startComputerUpdates();
|
||||
|
||||
// Load the app grid with cached data (if possible)
|
||||
populateAppGridWithCache();
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -132,7 +140,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
|
||||
managerBinder.startPolling(new ComputerManagerListener() {
|
||||
@Override
|
||||
public void notifyComputerUpdated(ComputerDetails details) {
|
||||
public void notifyComputerUpdated(final ComputerDetails details) {
|
||||
// Do nothing if updates are suspended
|
||||
if (suspendGridUpdates) {
|
||||
return;
|
||||
@@ -157,6 +165,48 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
return;
|
||||
}
|
||||
|
||||
// Close immediately if the PC is no longer paired
|
||||
if (details.state == ComputerDetails.State.ONLINE && details.pairState != PairingManager.PairState.PAIRED) {
|
||||
AppView.this.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Disable shortcuts referencing this PC for now
|
||||
shortcutHelper.disableShortcut(details.uuid.toString(),
|
||||
getResources().getString(R.string.scut_not_paired));
|
||||
|
||||
// Display a toast to the user and quit the activity
|
||||
Toast.makeText(AppView.this, getResources().getText(R.string.scut_not_paired), Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (launchedFromShortcut) {
|
||||
if (details.state == ComputerDetails.State.ONLINE) {
|
||||
if (blockingServerinfoSpinner != null) {
|
||||
blockingServerinfoSpinner.dismiss();
|
||||
blockingServerinfoSpinner = null;
|
||||
}
|
||||
|
||||
if (details.runningGameId != 0) {
|
||||
AppView.this.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// We have to finish this activity here otherwise we'll get into a loop
|
||||
// when the user hits back
|
||||
finish();
|
||||
|
||||
// When launched from shortcut, resume the running game
|
||||
ServerHelper.doStart(AppView.this, new NvApp("app", details.runningGameId), computer, managerBinder);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// App list is the same or empty
|
||||
if (details.rawAppList == null || details.rawAppList.equals(lastRawApplist)) {
|
||||
|
||||
@@ -175,6 +225,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
|
||||
try {
|
||||
updateUiWithAppList(NvHTTP.getAppListByReader(new StringReader(details.rawAppList)));
|
||||
updateUiWithServerinfo(details);
|
||||
|
||||
if (blockingLoadSpinner != null) {
|
||||
blockingLoadSpinner.dismiss();
|
||||
@@ -208,6 +259,8 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
shortcutHelper = new ShortcutHelper(this);
|
||||
|
||||
String locale = PreferenceConfiguration.readPreferences(this).language;
|
||||
if (!locale.equals(PreferenceConfiguration.DEFAULT_LANGUAGE)) {
|
||||
Configuration config = new Configuration(getResources().getConfiguration());
|
||||
@@ -221,6 +274,15 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
|
||||
uuidString = getIntent().getStringExtra(UUID_EXTRA);
|
||||
|
||||
launchedFromShortcut = getIntent().getBooleanExtra(SHORTCUT_EXTRA, false);
|
||||
if (launchedFromShortcut) {
|
||||
// Display blocking loading spinner
|
||||
blockingLoadSpinner = SpinnerDialog.displayDialog(this, getResources().getString(R.string.conn_establishing_title),
|
||||
getResources().getString(R.string.applist_connect_msg), true);
|
||||
}
|
||||
|
||||
shortcutHelper.reportShortcutUsed(uuidString);
|
||||
|
||||
String labelText = getResources().getString(R.string.title_applist)+" "+getIntent().getStringExtra(NAME_EXTRA);
|
||||
TextView label = (TextView) findViewById(R.id.appListText);
|
||||
setTitle(labelText);
|
||||
@@ -282,27 +344,14 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
stopComputerUpdates();
|
||||
}
|
||||
|
||||
private int getRunningAppId() {
|
||||
int runningAppId = -1;
|
||||
for (int i = 0; i < appGridAdapter.getCount(); i++) {
|
||||
AppObject app = (AppObject) appGridAdapter.getItem(i);
|
||||
if (app.app.getIsRunning()) {
|
||||
runningAppId = app.app.getAppId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return runningAppId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
|
||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
|
||||
AppObject selectedApp = (AppObject) appGridAdapter.getItem(info.position);
|
||||
int runningAppId = getRunningAppId();
|
||||
if (runningAppId != -1) {
|
||||
if (runningAppId == selectedApp.app.getAppId()) {
|
||||
if (lastRunningAppId != 0) {
|
||||
if (lastRunningAppId == selectedApp.app.getAppId()) {
|
||||
menu.add(Menu.NONE, START_OR_RESUME_ID, 1, getResources().getString(R.string.applist_menu_resume));
|
||||
menu.add(Menu.NONE, QUIT_ID, 2, getResources().getString(R.string.applist_menu_quit));
|
||||
}
|
||||
@@ -378,19 +427,19 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
AppObject existingApp = (AppObject) appGridAdapter.getItem(i);
|
||||
|
||||
// There can only be one or zero apps running.
|
||||
if (existingApp.app.getIsRunning() &&
|
||||
if (existingApp.isRunning &&
|
||||
existingApp.app.getAppId() == details.runningGameId) {
|
||||
// This app was running and still is, so we're done now
|
||||
return;
|
||||
}
|
||||
else if (existingApp.app.getAppId() == details.runningGameId) {
|
||||
// This app wasn't running but now is
|
||||
existingApp.app.setIsRunning(true);
|
||||
existingApp.isRunning = true;
|
||||
updated = true;
|
||||
}
|
||||
else if (existingApp.app.getIsRunning()) {
|
||||
else if (existingApp.isRunning) {
|
||||
// This app was running but now isn't
|
||||
existingApp.app.setIsRunning(false);
|
||||
existingApp.isRunning = false;
|
||||
updated = true;
|
||||
}
|
||||
else {
|
||||
@@ -420,10 +469,6 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
AppObject existingApp = (AppObject) appGridAdapter.getItem(i);
|
||||
if (existingApp.app.getAppId() == app.getAppId()) {
|
||||
// Found the app; update its properties
|
||||
if (existingApp.app.getIsRunning() != app.getIsRunning()) {
|
||||
existingApp.app.setIsRunning(app.getIsRunning());
|
||||
updated = true;
|
||||
}
|
||||
if (!existingApp.app.getAppName().equals(app.getAppName())) {
|
||||
existingApp.app.setAppName(app.getAppName());
|
||||
updated = true;
|
||||
@@ -493,7 +538,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
AppObject app = (AppObject) appGridAdapter.getItem(pos);
|
||||
|
||||
// Only open the context menu if something is running, otherwise start it
|
||||
if (getRunningAppId() != -1) {
|
||||
if (lastRunningAppId != 0) {
|
||||
openContextMenu(arg1);
|
||||
} else {
|
||||
ServerHelper.doStart(AppView.this, app.app, computer, managerBinder);
|
||||
@@ -506,6 +551,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
||||
|
||||
public class AppObject {
|
||||
public final NvApp app;
|
||||
public boolean isRunning;
|
||||
|
||||
public AppObject(NvApp app) {
|
||||
if (app == null) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.limelight.ui.AdapterFragment;
|
||||
import com.limelight.ui.AdapterFragmentCallbacks;
|
||||
import com.limelight.utils.Dialog;
|
||||
import com.limelight.utils.ServerHelper;
|
||||
import com.limelight.utils.ShortcutHelper;
|
||||
import com.limelight.utils.UiHelper;
|
||||
|
||||
import android.app.Activity;
|
||||
@@ -52,6 +53,7 @@ import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
public class PcView extends Activity implements AdapterFragmentCallbacks {
|
||||
private RelativeLayout noPcFoundLayout;
|
||||
private PcGridAdapter pcGridAdapter;
|
||||
private ShortcutHelper shortcutHelper;
|
||||
private ComputerManagerService.ComputerManagerBinder managerBinder;
|
||||
private boolean freezeUpdates, runningPolling, inForeground;
|
||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@@ -142,6 +144,8 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
shortcutHelper = new ShortcutHelper(this);
|
||||
|
||||
String locale = PreferenceConfiguration.readPreferences(this).language;
|
||||
if (!locale.equals(PreferenceConfiguration.DEFAULT_LANGUAGE)) {
|
||||
Configuration config = new Configuration(getResources().getConfiguration());
|
||||
@@ -334,6 +338,9 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
||||
else if (pairState == PairingManager.PairState.FAILED) {
|
||||
message = getResources().getString(R.string.pair_fail);
|
||||
}
|
||||
else if (pairState == PairingManager.PairState.ALREADY_IN_PROGRESS) {
|
||||
message = getResources().getString(R.string.pair_already_in_progress);
|
||||
}
|
||||
else if (pairState == PairingManager.PairState.PAIRED) {
|
||||
// Just navigate to the app view without displaying a toast
|
||||
message = null;
|
||||
@@ -366,6 +373,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
||||
|
||||
if (toastSuccess) {
|
||||
// Open the app list after a successful pairing attempt
|
||||
computer.pairState = PairState.PAIRED;
|
||||
doAppList(computer);
|
||||
}
|
||||
else {
|
||||
@@ -486,6 +494,8 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
||||
return;
|
||||
}
|
||||
|
||||
shortcutHelper.createAppViewShortcut(computer.uuid.toString(), computer);
|
||||
|
||||
Intent i = new Intent(this, AppView.class);
|
||||
i.putExtra(AppView.NAME_EXTRA, computer.name);
|
||||
i.putExtra(AppView.UUID_EXTRA, computer.uuid.toString());
|
||||
@@ -558,6 +568,10 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
||||
ComputerObject computer = (ComputerObject) pcGridAdapter.getItem(i);
|
||||
|
||||
if (details.equals(computer.details)) {
|
||||
// Disable or delete shortcuts referencing this PC
|
||||
shortcutHelper.disableShortcut(details.uuid.toString(),
|
||||
getResources().getString(R.string.scut_deleted_pc));
|
||||
|
||||
pcGridAdapter.removeComputer(computer);
|
||||
pcGridAdapter.notifyDataSetChanged();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.hardware.input.InputManager;
|
||||
import android.os.SystemClock;
|
||||
import android.util.SparseArray;
|
||||
import android.view.InputDevice;
|
||||
import android.view.InputEvent;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.Toast;
|
||||
@@ -415,21 +416,27 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
return context;
|
||||
}
|
||||
|
||||
private InputDeviceContext getContextForDevice(InputDevice dev) {
|
||||
private InputDeviceContext getContextForEvent(InputEvent event) {
|
||||
// Unknown devices use the default context
|
||||
if (dev == null) {
|
||||
if (event.getDeviceId() == 0) {
|
||||
return defaultContext;
|
||||
}
|
||||
else if (event.getDevice() == null) {
|
||||
// During device removal, sometimes we can get events after the
|
||||
// input device has been destroyed. In this case we'll see a
|
||||
// != 0 device ID but no device attached.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the existing context if it exists
|
||||
InputDeviceContext context = inputDeviceContexts.get(dev.getId());
|
||||
InputDeviceContext context = inputDeviceContexts.get(event.getDeviceId());
|
||||
if (context != null) {
|
||||
return context;
|
||||
}
|
||||
|
||||
// Otherwise create a new context
|
||||
context = createInputDeviceContextForDevice(dev);
|
||||
inputDeviceContexts.put(dev.getId(), context);
|
||||
context = createInputDeviceContextForDevice(event.getDevice());
|
||||
inputDeviceContexts.put(event.getDeviceId(), context);
|
||||
|
||||
return context;
|
||||
}
|
||||
@@ -627,6 +634,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
default:
|
||||
// Other buttons are mapped correctly
|
||||
}
|
||||
|
||||
// The Xbox button is sent as MENU
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) {
|
||||
return KeyEvent.KEYCODE_BUTTON_MODE;
|
||||
}
|
||||
}
|
||||
|
||||
if (context.hatXAxis != -1 && context.hatYAxis != -1) {
|
||||
@@ -642,7 +654,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
else if (context.hatXAxis == -1 &&
|
||||
context.hatYAxis == -1 &&
|
||||
context.isXboxController &&
|
||||
/* FIXME: There's no good way to know for sure if xpad is bound
|
||||
to this device, so we won't use the name to validate if these
|
||||
scancodes should be mapped to DPAD
|
||||
|
||||
context.isXboxController &&
|
||||
*/
|
||||
event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
|
||||
// If there's not a proper Xbox controller mapping, we'll translate the raw d-pad
|
||||
// scan codes into proper key codes
|
||||
@@ -732,9 +749,23 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
if (context.leftTriggerAxis != -1 && context.rightTriggerAxis != -1) {
|
||||
// Android sends an initial 0 value for trigger axes even if the trigger
|
||||
// should be negative when idle. After the first touch, the axes will go back
|
||||
// to normal behavior, so ignore triggersIdleNegative for each trigger until
|
||||
// first touch.
|
||||
if (lt != 0) {
|
||||
context.leftTriggerUsed = true;
|
||||
}
|
||||
if (rt != 0) {
|
||||
context.rightTriggerUsed = true;
|
||||
}
|
||||
if (context.triggersIdleNegative) {
|
||||
lt = (lt + 1) / 2;
|
||||
rt = (rt + 1) / 2;
|
||||
if (context.leftTriggerUsed) {
|
||||
lt = (lt + 1) / 2;
|
||||
}
|
||||
if (context.rightTriggerUsed) {
|
||||
rt = (rt + 1) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (lt <= context.triggerDeadzone) {
|
||||
@@ -770,7 +801,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
public boolean handleMotionEvent(MotionEvent event) {
|
||||
InputDeviceContext context = getContextForDevice(event.getDevice());
|
||||
InputDeviceContext context = getContextForEvent(event);
|
||||
if (context == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
float lsX = 0, lsY = 0, rsX = 0, rsY = 0, rt = 0, lt = 0, hatX = 0, hatY = 0;
|
||||
|
||||
// We purposefully ignore the historical values in the motion event as it makes
|
||||
@@ -842,7 +877,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
public boolean handleButtonUp(KeyEvent event) {
|
||||
InputDeviceContext context = getContextForDevice(event.getDevice());
|
||||
InputDeviceContext context = getContextForEvent(event);
|
||||
if (context == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int keyCode = handleRemapping(context, event);
|
||||
if (keyCode == 0) {
|
||||
@@ -867,7 +905,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_START:
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
if (SystemClock.uptimeMillis() - context.startDownTime > ControllerHandler.START_DOWN_TIME_MOUSE_MODE_MS) {
|
||||
// Sometimes we'll get a spurious key up event on controller disconnect.
|
||||
// Make sure it's real by checking that the key is actually down before taking
|
||||
// any action.
|
||||
if ((context.inputMap & ControllerPacket.PLAY_FLAG) != 0 &&
|
||||
SystemClock.uptimeMillis() - context.startDownTime > ControllerHandler.START_DOWN_TIME_MOUSE_MODE_MS) {
|
||||
toggleMouseEmulation(context);
|
||||
}
|
||||
context.inputMap &= ~ControllerPacket.PLAY_FLAG;
|
||||
@@ -965,7 +1007,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
}
|
||||
|
||||
public boolean handleButtonDown(KeyEvent event) {
|
||||
InputDeviceContext context = getContextForDevice(event.getDevice());
|
||||
InputDeviceContext context = getContextForEvent(event);
|
||||
if (context == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int keyCode = handleRemapping(context, event);
|
||||
if (keyCode == 0) {
|
||||
@@ -1153,6 +1198,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
public int leftTriggerAxis = -1;
|
||||
public int rightTriggerAxis = -1;
|
||||
public boolean triggersIdleNegative;
|
||||
public boolean leftTriggerUsed, rightTriggerUsed;
|
||||
|
||||
public int hatXAxis = -1;
|
||||
public int hatYAxis = -1;
|
||||
|
||||
@@ -65,7 +65,7 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
||||
Collections.sort(itemList, new Comparator<AppView.AppObject>() {
|
||||
@Override
|
||||
public int compare(AppView.AppObject lhs, AppView.AppObject rhs) {
|
||||
return lhs.app.getAppName().compareTo(rhs.app.getAppName());
|
||||
return lhs.app.getAppName().toLowerCase().compareTo(rhs.app.getAppName().toLowerCase());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -101,7 +101,7 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
||||
|
||||
@Override
|
||||
public boolean populateOverlayView(ImageView overlayView, AppView.AppObject obj) {
|
||||
if (obj.app.getIsRunning()) {
|
||||
if (obj.isRunning) {
|
||||
// Show the play button overlay
|
||||
overlayView.setImageResource(R.drawable.ic_play);
|
||||
return true;
|
||||
|
||||
@@ -28,7 +28,7 @@ public class PcGridAdapter extends GenericGridAdapter<PcView.ComputerObject> {
|
||||
Collections.sort(itemList, new Comparator<PcView.ComputerObject>() {
|
||||
@Override
|
||||
public int compare(PcView.ComputerObject lhs, PcView.ComputerObject rhs) {
|
||||
return lhs.details.name.compareTo(rhs.details.name);
|
||||
return lhs.details.name.toLowerCase().compareTo(rhs.details.name.toLowerCase());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.limelight.utils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.os.Build;
|
||||
|
||||
import com.limelight.AppView;
|
||||
import com.limelight.R;
|
||||
import com.limelight.nvstream.http.ComputerDetails;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ShortcutHelper {
|
||||
|
||||
private final ShortcutManager sm;
|
||||
private final Context context;
|
||||
|
||||
public ShortcutHelper(Context context) {
|
||||
this.context = context;
|
||||
if (Build.VERSION.SDK_INT >= 25) {
|
||||
sm = context.getSystemService(ShortcutManager.class);
|
||||
}
|
||||
else {
|
||||
sm = null;
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(25)
|
||||
private void reapShortcutsForDynamicAdd() {
|
||||
List<ShortcutInfo> dynamicShortcuts = sm.getDynamicShortcuts();
|
||||
while (dynamicShortcuts.size() >= sm.getMaxShortcutCountPerActivity()) {
|
||||
ShortcutInfo maxRankShortcut = dynamicShortcuts.get(0);
|
||||
for (ShortcutInfo scut : dynamicShortcuts) {
|
||||
if (maxRankShortcut.getRank() < scut.getRank()) {
|
||||
maxRankShortcut = scut;
|
||||
}
|
||||
}
|
||||
sm.removeDynamicShortcuts(Arrays.asList(maxRankShortcut.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(25)
|
||||
private List<ShortcutInfo> getAllShortcuts() {
|
||||
LinkedList<ShortcutInfo> list = new LinkedList<>();
|
||||
list.addAll(sm.getDynamicShortcuts());
|
||||
list.addAll(sm.getPinnedShortcuts());
|
||||
return list;
|
||||
}
|
||||
|
||||
@TargetApi(25)
|
||||
private ShortcutInfo getInfoForId(String id) {
|
||||
List<ShortcutInfo> shortcuts = getAllShortcuts();
|
||||
|
||||
for (ShortcutInfo info : shortcuts) {
|
||||
if (info.getId().equals(id)) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void reportShortcutUsed(String id) {
|
||||
if (Build.VERSION.SDK_INT >= 25) {
|
||||
ShortcutInfo sinfo = getInfoForId(id);
|
||||
if (sinfo != null) {
|
||||
sm.reportShortcutUsed(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void createAppViewShortcut(String id, ComputerDetails details) {
|
||||
if (Build.VERSION.SDK_INT >= 25) {
|
||||
Intent i = new Intent(context, AppView.class);
|
||||
i.putExtra(AppView.NAME_EXTRA, details.name);
|
||||
i.putExtra(AppView.UUID_EXTRA, details.uuid.toString());
|
||||
i.putExtra(AppView.SHORTCUT_EXTRA, true);
|
||||
i.setAction(Intent.ACTION_DEFAULT);
|
||||
|
||||
ShortcutInfo sinfo = new ShortcutInfo.Builder(context, id)
|
||||
.setIntent(i)
|
||||
.setShortLabel(details.name)
|
||||
.setLongLabel(details.name)
|
||||
.build();
|
||||
|
||||
ShortcutInfo existingSinfo = getInfoForId(id);
|
||||
if (existingSinfo != null) {
|
||||
// Update in place
|
||||
sm.updateShortcuts(Arrays.asList(sinfo));
|
||||
sm.enableShortcuts(Arrays.asList(id));
|
||||
}
|
||||
else {
|
||||
// Reap shortcuts to make space for this new one
|
||||
reapShortcutsForDynamicAdd();
|
||||
|
||||
// Add the new shortcut
|
||||
//TODO: Testing and proper icon - sm.addDynamicShortcuts(Arrays.asList(sinfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void disableShortcut(String id, CharSequence reason) {
|
||||
if (Build.VERSION.SDK_INT >= 25) {
|
||||
ShortcutInfo sinfo = getInfoForId(id);
|
||||
if (sinfo != null) {
|
||||
sm.disableShortcuts(Arrays.asList(id), reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#define EVDEV_MAX_EVENT_SIZE 24
|
||||
|
||||
@@ -19,7 +19,8 @@ Java_com_limelight_nvstream_av_audio_OpusDecoder_init(JNIEnv *env, jobject this,
|
||||
ChannelCount = channelCount;
|
||||
|
||||
jni_mapping_data = (*env)->GetByteArrayElements(env, mapping, 0);
|
||||
ret = nv_opus_init(sampleRate, channelCount, streams, coupledStreams, jni_mapping_data);
|
||||
ret = nv_opus_init(sampleRate, channelCount, streams, coupledStreams,
|
||||
(const unsigned char*)jni_mapping_data);
|
||||
(*env)->ReleaseByteArrayElements(env, mapping, jni_mapping_data, JNI_ABORT);
|
||||
|
||||
return ret;
|
||||
@@ -49,7 +50,8 @@ Java_com_limelight_nvstream_av_audio_OpusDecoder_decode(
|
||||
if (indata != NULL) {
|
||||
jni_input_data = (*env)->GetByteArrayElements(env, indata, 0);
|
||||
|
||||
ret = nv_opus_decode(&jni_input_data[inoff], inlen, (jshort*)jni_pcm_data, SamplesPerChannel);
|
||||
ret = nv_opus_decode((unsigned char*)&jni_input_data[inoff], inlen,
|
||||
(jshort*)jni_pcm_data, SamplesPerChannel);
|
||||
|
||||
// The input data isn't changed so it can be safely aborted
|
||||
(*env)->ReleaseByteArrayElements(env, indata, jni_input_data, JNI_ABORT);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Shortcut strings -->
|
||||
<string name="scut_deleted_pc">PC deleted</string>
|
||||
<string name="scut_not_paired">PC not paired</string>
|
||||
|
||||
<!-- PC view menu entries -->
|
||||
<string name="pcview_menu_app_list">View Game List</string>
|
||||
@@ -16,6 +19,7 @@
|
||||
<string name="pair_pairing_msg">Please enter the following PIN on the target PC:</string>
|
||||
<string name="pair_incorrect_pin">Incorrect PIN</string>
|
||||
<string name="pair_fail">Pairing failed</string>
|
||||
<string name="pair_already_in_progress">Pairing already in progress</string>
|
||||
|
||||
<!-- WOL messages -->
|
||||
<string name="wol_pc_online">Computer is online</string>
|
||||
@@ -63,6 +67,7 @@
|
||||
|
||||
<!-- AppList activity -->
|
||||
<string name="title_applist">Apps on</string>
|
||||
<string name="applist_connect_msg">Connecting to PC…</string>
|
||||
<string name="applist_menu_resume">Resume Session</string>
|
||||
<string name="applist_menu_quit">Quit Session</string>
|
||||
<string name="applist_menu_quit_and_start">Quit Current Game and Start</string>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.2.0'
|
||||
classpath 'com.android.tools.build:gradle:2.2.2'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user