Compare commits

..
16 Commits
Author SHA1 Message Date
Cameron Gutman f1d7f556fd Bump to version 3.1.8 2015-07-21 18:03:37 -07:00
Cameron Gutman 1e70e1d329 GFE 2.5.11 update to fix black screen on Fire TV Stick 2015-07-18 17:06:41 -07:00
Cameron Gutman e02a009635 Add support for the Razer Serval controller. The start and select buttons are manually handled for devices without a mapping for them. The back button is ignored so it can be used to exit the stream. 2015-07-18 00:46:25 -07:00
Cameron Gutman bd6ff35603 Update to 3.1.7 2015-06-15 10:37:58 -07:00
Cameron Gutman 1cb7727dc7 Update common 2015-06-15 10:28:31 -07:00
Cameron Gutman 0c73e3d0ae Only propagate a decoder exception if it happens at the beginning of a stream 2015-06-15 10:28:09 -07:00
Cameron Gutman 6371d364e1 Lint warning cleanup 2015-05-29 23:22:40 -05:00
Cameron Gutman ded9c9140d Handle being online but not having a known reachability 2015-05-29 23:20:04 -05:00
Cameron Gutman 7c8a108e28 Use the leanback feature on API 21+ devices 2015-05-29 23:18:56 -05:00
Cameron Gutman 2a18ffcdba Update to Gradle 1.2.3 2015-05-19 10:10:18 -05:00
Cameron Gutman 381d0d5e81 Add support for multi-window functionality on Samsung devices 2015-05-10 00:02:04 -05:00
Cameron Gutman be126acfd1 Update version info to 3.1.6 2015-05-05 20:52:53 -04:00
Cameron Gutman fc2f5cfe4d Manually pass through Samsung capacitive buttons 2015-05-05 20:20:37 -04:00
Cameron Gutman 9878902a89 Use IDs to track controllers instead of descriptors. Fixes #64 2015-05-05 20:08:58 -04:00
Cameron Gutman f1230d46f3 Android Studio 1.2 and Grade 1.2.2 update 2015-05-05 20:02:53 -04:00
Michelle Bergeron d8822392f1 Link to site 2015-05-03 23:36:19 -07:00
15 changed files with 132 additions and 58 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
#Moonlight #Moonlight
Moonlight is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield. [Moonlight](http://moonlight-stream.com) is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield.
We reverse engineered the Shield streaming software and created a version that can be run on any Android device. We reverse engineered the Shield streaming software and created a version that can be run on any Android device.
Moonlight will allow you to stream your full collection of games from your Windows PC to your Android device, Moonlight will allow you to stream your full collection of games from your Windows PC to your Android device,
+4 -4
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="moonlight-android" external.system.module.version="unspecified" type="JAVA_MODULE" version="4"> <module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="moonlight-android" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager"> <component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle"> <facet type="android-gradle" name="Android-Gradle">
<configuration> <configuration>
@@ -12,8 +12,9 @@
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" /> <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleNonRootDebug" /> <option name="ASSEMBLE_TASK_NAME" value="assembleNonRootDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileNonRootDebugSources" /> <option name="COMPILE_JAVA_TASK_NAME" value="compileNonRootDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleNonRootDebugAndroidTest" />
<option name="SOURCE_GEN_TASK_NAME" value="generateNonRootDebugSources" /> <option name="SOURCE_GEN_TASK_NAME" value="generateNonRootDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleNonRootDebugAndroidTest" />
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileNonRootDebugAndroidTestSources" />
<option name="TEST_SOURCE_GEN_TASK_NAME" value="generateNonRootDebugAndroidTestSources" /> <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateNonRootDebugAndroidTestSources" />
<option name="ALLOW_USER_CONFIGURATION" value="false" /> <option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" /> <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
@@ -114,5 +115,4 @@
<orderEntry type="library" exported="" name="jcodec-0.1.9" level="project" /> <orderEntry type="library" exported="" name="jcodec-0.1.9" level="project" />
<orderEntry type="library" exported="" name="okio-1.2.0" level="project" /> <orderEntry type="library" exported="" name="okio-1.2.0" level="project" />
</component> </component>
</module> </module>
+2 -2
View File
@@ -11,8 +11,8 @@ android {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 22 targetSdkVersion 22
versionName "3.1.5" versionName "3.1.8"
versionCode = 60 versionCode = 63
} }
productFlavors { productFlavors {
Binary file not shown.
+5
View File
@@ -16,6 +16,10 @@
android:allowBackup="true" android:allowBackup="true"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:theme="@style/AppTheme" > android:theme="@style/AppTheme" >
<!-- Samsung multi-window support -->
<uses-library android:name="com.sec.android.app.multiwindow" android:required="false" />
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
<!-- Launcher for traditional devices --> <!-- Launcher for traditional devices -->
<activity <activity
@@ -24,6 +28,7 @@
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
<category android:name="tv.ouya.intent.category.APP" /> <category android:name="tv.ouya.intent.category.APP" />
</intent-filter> </intent-filter>
</activity> </activity>
@@ -1,19 +1,14 @@
package com.limelight; package com.limelight;
import java.io.FileNotFoundException;
import java.io.StringReader; import java.io.StringReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.UUID; import java.util.UUID;
import com.limelight.binding.PlatformBinding;
import com.limelight.computers.ComputerManagerListener; import com.limelight.computers.ComputerManagerListener;
import com.limelight.computers.ComputerManagerService; import com.limelight.computers.ComputerManagerService;
import com.limelight.grid.AppGridAdapter; import com.limelight.grid.AppGridAdapter;
import com.limelight.nvstream.http.ComputerDetails; import com.limelight.nvstream.http.ComputerDetails;
import com.limelight.nvstream.http.GfeHttpResponseException;
import com.limelight.nvstream.http.NvApp; import com.limelight.nvstream.http.NvApp;
import com.limelight.nvstream.http.NvHTTP; import com.limelight.nvstream.http.NvHTTP;
import com.limelight.preferences.PreferenceConfiguration; import com.limelight.preferences.PreferenceConfiguration;
@@ -26,10 +21,8 @@ import com.limelight.utils.SpinnerDialog;
import com.limelight.utils.UiHelper; import com.limelight.utils.UiHelper;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog;
import android.app.Service; import android.app.Service;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.res.Configuration; import android.content.res.Configuration;
@@ -306,6 +306,10 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
else if (computer.reachability == ComputerDetails.Reachability.REMOTE) { else if (computer.reachability == ComputerDetails.Reachability.REMOTE) {
addr = computer.remoteIp; addr = computer.remoteIp;
} }
else {
LimeLog.warning("Unknown reachability - using local IP");
addr = computer.localIp;
}
httpConn = new NvHTTP(addr, httpConn = new NvHTTP(addr,
managerBinder.getUniqueId(), managerBinder.getUniqueId(),
@@ -431,6 +435,10 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
else if (computer.reachability == ComputerDetails.Reachability.REMOTE) { else if (computer.reachability == ComputerDetails.Reachability.REMOTE) {
addr = computer.remoteIp; addr = computer.remoteIp;
} }
else {
LimeLog.warning("Unknown reachability - using local IP");
addr = computer.localIp;
}
httpConn = new NvHTTP(addr, httpConn = new NvHTTP(addr,
managerBinder.getUniqueId(), managerBinder.getUniqueId(),
@@ -1,10 +1,8 @@
package com.limelight.binding.input; package com.limelight.binding.input;
import java.util.HashMap;
import java.util.Map;
import android.hardware.input.InputManager; import android.hardware.input.InputManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.SparseArray;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
@@ -31,7 +29,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
private final Vector2d inputVector = new Vector2d(); private final Vector2d inputVector = new Vector2d();
private final HashMap<String, ControllerContext> contexts = new HashMap<String, ControllerContext>(); private final SparseArray<ControllerContext> contexts = new SparseArray<ControllerContext>();
private final NvConnection conn; private final NvConnection conn;
private final double stickDeadzone; private final double stickDeadzone;
@@ -52,8 +50,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
deadzonePercentage = 10; deadzonePercentage = 10;
int[] ids = InputDevice.getDeviceIds(); int[] ids = InputDevice.getDeviceIds();
for (int i = 0; i < ids.length; i++) { for (int id : ids) {
InputDevice dev = InputDevice.getDevice(ids[i]); InputDevice dev = InputDevice.getDevice(id);
if ((dev.getSources() & InputDevice.SOURCE_JOYSTICK) != 0 || if ((dev.getSources() & InputDevice.SOURCE_JOYSTICK) != 0 ||
(dev.getSources() & InputDevice.SOURCE_GAMEPAD) != 0) { (dev.getSources() & InputDevice.SOURCE_GAMEPAD) != 0) {
// This looks like a gamepad, but we'll check X and Y to be sure // This looks like a gamepad, but we'll check X and Y to be sure
@@ -104,13 +102,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
@Override @Override
public void onInputDeviceRemoved(int deviceId) { public void onInputDeviceRemoved(int deviceId) {
for (Map.Entry<String, ControllerContext> device : contexts.entrySet()) { ControllerContext context = contexts.get(deviceId);
if (device.getValue().id == deviceId) { if (context != null) {
LimeLog.info("Removed controller: "+device.getValue().name); LimeLog.info("Removed controller: "+context.name+" ("+deviceId+")");
releaseControllerNumber(device.getValue()); releaseControllerNumber(context);
contexts.remove(device.getKey()); contexts.remove(deviceId);
return;
}
} }
} }
@@ -135,7 +131,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
return; return;
} }
LimeLog.info(context.name+" needs a controller number assigned"); LimeLog.info(context.name+" ("+context.id+") needs a controller number assigned");
if (context.name != null && context.name.contains("gpio-keys")) { if (context.name != null && context.name.contains("gpio-keys")) {
// This is the back button on Shield portable consoles // This is the back button on Shield portable consoles
LimeLog.info("Built-in buttons hardcoded as controller 0"); LimeLog.info("Built-in buttons hardcoded as controller 0");
@@ -277,6 +273,15 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
} }
} }
// Ignore the back buttonn if a controller has both buttons
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
boolean[] hasSelectKey = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_SELECT, KeyEvent.KEYCODE_BACK, 0);
if (hasSelectKey[0] && hasSelectKey[1]) {
LimeLog.info("Ignoring back button because select is present");
context.ignoreBack = true;
}
}
if (devName != null) { if (devName != null) {
// For the Nexus Player (and probably other ATV devices), we should // For the Nexus Player (and probably other ATV devices), we should
// use the back button as start since it doesn't have a start/menu button // use the back button as start since it doesn't have a start/menu button
@@ -300,7 +305,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
else if (devName.contains("Fire TV Remote") || devName.contains("Nexus Remote")) { else if (devName.contains("Fire TV Remote") || devName.contains("Nexus Remote")) {
// It's only a remote if it doesn't any sticks // It's only a remote if it doesn't any sticks
if (!context.hasJoystickAxes) { if (!context.hasJoystickAxes) {
context.isRemote = true; context.ignoreBack = true;
} }
} }
// SHIELD controllers will use small stick deadzones // SHIELD controllers will use small stick deadzones
@@ -308,6 +313,17 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
context.leftStickDeadzoneRadius = 0.07f; context.leftStickDeadzoneRadius = 0.07f;
context.rightStickDeadzoneRadius = 0.07f; context.rightStickDeadzoneRadius = 0.07f;
} }
// Samsung's face buttons appear as a non-virtual button so we'll explicitly ignore
// back presses on this device
else if (devName.equals("sec_touchscreen")) {
context.ignoreBack = true;
}
// The Serval has a couple of unknown buttons that are start and select. It also has
// a back button which we want to ignore since there's already a select button.
else if (devName.contains("Razer Serval")) {
context.isServal = true;
context.ignoreBack = true;
}
} }
LimeLog.info("Analog stick deadzone: "+context.leftStickDeadzoneRadius+" "+context.rightStickDeadzoneRadius); LimeLog.info("Analog stick deadzone: "+context.leftStickDeadzoneRadius+" "+context.rightStickDeadzoneRadius);
@@ -322,17 +338,15 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
return defaultContext; return defaultContext;
} }
String descriptor = dev.getDescriptor();
// Return the existing context if it exists // Return the existing context if it exists
ControllerContext context = contexts.get(descriptor); ControllerContext context = contexts.get(dev.getId());
if (context != null) { if (context != null) {
return context; return context;
} }
// Otherwise create a new context // Otherwise create a new context
context = createContextForDevice(dev); context = createContextForDevice(dev);
contexts.put(descriptor, context); contexts.put(dev.getId(), context);
return context; return context;
} }
@@ -348,8 +362,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
// Return a valid keycode, 0 to consume, or -1 to not consume the event // Return a valid keycode, 0 to consume, or -1 to not consume the event
// Device MAY BE NULL // Device MAY BE NULL
private int handleRemapping(ControllerContext context, KeyEvent event) { private int handleRemapping(ControllerContext context, KeyEvent event) {
// For remotes, don't capture the back button // Don't capture the back button if configured
if (context.isRemote) { if (context.ignoreBack) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
return -1; return -1;
} }
@@ -393,6 +407,16 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
return 0; return 0;
} }
} }
// If this is a Serval controller sending an unknown key code, it's probably
// the start and select buttons
else if (context.isServal && event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
switch (event.getScanCode()) {
case 314:
return KeyEvent.KEYCODE_BUTTON_SELECT;
case 315:
return KeyEvent.KEYCODE_BUTTON_START;
}
}
if (context.hatXAxis != -1 && context.hatYAxis != -1) { if (context.hatXAxis != -1 && context.hatYAxis != -1) {
switch (event.getKeyCode()) { switch (event.getKeyCode()) {
@@ -814,9 +838,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener {
public boolean isDualShock4; public boolean isDualShock4;
public boolean isXboxController; public boolean isXboxController;
public boolean isServal;
public boolean backIsStart; public boolean backIsStart;
public boolean modeIsSelect; public boolean modeIsSelect;
public boolean isRemote; public boolean ignoreBack;
public boolean hasJoystickAxes; public boolean hasJoystickAxes;
public boolean assignedControllerNumber; public boolean assignedControllerNumber;
@@ -134,11 +134,14 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
} }
} }
if (buf != null || codecFlags != 0) { // Only throw if this happens at the beginning of a stream
throw new RendererException(this, e, buf, codecFlags); if (totalFrames < 60) {
} if (buf != null || codecFlags != 0) {
else { throw new RendererException(this, e, buf, codecFlags);
throw new RendererException(this, e); }
else {
throw new RendererException(this, e);
}
} }
} }
@@ -483,19 +486,49 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
LimeLog.info("Patching num_ref_frames in SPS"); LimeLog.info("Patching num_ref_frames in SPS");
sps.num_ref_frames = 1; sps.num_ref_frames = 1;
// GFE 2.5.11 changed the SPS to add additional extensions
// Some devices don't like these so we remove them here.
sps.vuiParams.video_signal_type_present_flag = false;
sps.vuiParams.colour_description_present_flag = false;
sps.vuiParams.colour_primaries = 2;
sps.vuiParams.transfer_characteristics = 2;
sps.vuiParams.matrix_coefficients = 2;
sps.vuiParams.chroma_loc_info_present_flag = false;
sps.vuiParams.chroma_sample_loc_type_bottom_field = 0;
sps.vuiParams.chroma_sample_loc_type_top_field = 0;
if (needsSpsBitstreamFixup || isExynos4) { if (needsSpsBitstreamFixup || isExynos4) {
// The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag // The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
// or max_dec_frame_buffering which increases decoding latency on Tegra. // or max_dec_frame_buffering which increases decoding latency on Tegra.
LimeLog.info("Adding bitstream restrictions");
sps.vuiParams.bitstreamRestriction = new VUIParameters.BitstreamRestriction(); // GFE 2.5.11 started sending bitstream restrictions
sps.vuiParams.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag = true; if (sps.vuiParams.bitstreamRestriction == null) {
LimeLog.info("Adding bitstream restrictions");
sps.vuiParams.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
sps.vuiParams.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag = true;
sps.vuiParams.bitstreamRestriction.log2_max_mv_length_horizontal = 16;
sps.vuiParams.bitstreamRestriction.log2_max_mv_length_vertical = 16;
sps.vuiParams.bitstreamRestriction.num_reorder_frames = 0;
}
else {
LimeLog.info("Patching bitstream restrictions");
}
// Some devices throw errors if max_dec_frame_buffering < num_ref_frames
sps.vuiParams.bitstreamRestriction.max_dec_frame_buffering = sps.num_ref_frames;
// These values are the defaults for the fields, but they are more aggressive
// than what GFE sends in 2.5.11, but it doesn't seem to cause picture problems.
sps.vuiParams.bitstreamRestriction.max_bytes_per_pic_denom = 2; sps.vuiParams.bitstreamRestriction.max_bytes_per_pic_denom = 2;
sps.vuiParams.bitstreamRestriction.max_bits_per_mb_denom = 1; sps.vuiParams.bitstreamRestriction.max_bits_per_mb_denom = 1;
sps.vuiParams.bitstreamRestriction.log2_max_mv_length_horizontal = 16;
sps.vuiParams.bitstreamRestriction.log2_max_mv_length_vertical = 16; // log2_max_mv_length_horizontal and log2_max_mv_length_vertical are set to more
sps.vuiParams.bitstreamRestriction.num_reorder_frames = 0; // conservative values by GFE 2.5.11. We'll let those values stand.
sps.vuiParams.bitstreamRestriction.max_dec_frame_buffering = 1; }
else {
// Devices that didn't/couldn't get bitstream restrictions before GFE 2.5.11
// will continue to not receive them now
sps.vuiParams.bitstreamRestriction = null;
} }
// If we need to hack this SPS to say we're baseline, do so now // If we need to hack this SPS to say we're baseline, do so now
@@ -393,7 +393,7 @@ public class ComputerManagerService extends Service {
private boolean fastPollIp(InetAddress addr) { private boolean fastPollIp(InetAddress addr) {
Socket s = new Socket(); Socket s = new Socket();
try { try {
s.connect(new InetSocketAddress(addr, NvHTTP.PORT), FAST_POLL_TIMEOUT); s.connect(new InetSocketAddress(addr, NvHTTP.HTTPS_PORT), FAST_POLL_TIMEOUT);
s.close(); s.close();
return true; return true;
} catch (IOException e) { } catch (IOException e) {
@@ -639,7 +639,7 @@ public class ComputerManagerService extends Service {
if (cacheOut != null) { if (cacheOut != null) {
cacheOut.close(); cacheOut.close();
} }
} catch (IOException e) {} } catch (IOException ignored) {}
} }
// Update the computer // Update the computer
@@ -27,7 +27,7 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
private final CachedAppAssetLoader loader; private final CachedAppAssetLoader loader;
public AppGridAdapter(Activity activity, boolean listMode, boolean small, ComputerDetails computer, String uniqueId) throws KeyManagementException, NoSuchAlgorithmException { public AppGridAdapter(Activity activity, boolean listMode, boolean small, ComputerDetails computer, String uniqueId) {
super(activity, listMode ? R.layout.simple_row : (small ? R.layout.app_grid_item_small : R.layout.app_grid_item), R.drawable.image_loading); super(activity, listMode ? R.layout.simple_row : (small ? R.layout.app_grid_item_small : R.layout.app_grid_item), R.drawable.image_loading);
int dpi = activity.getResources().getDisplayMetrics().densityDpi; int dpi = activity.getResources().getDisplayMetrics().densityDpi;
@@ -26,7 +26,7 @@ public class NetworkAssetLoader {
InputStream in = null; InputStream in = null;
try { try {
in = http.getBoxArt(tuple.app); in = http.getBoxArt(tuple.app);
} catch (IOException e) {} } catch (IOException ignored) {}
if (in != null) { if (in != null) {
LimeLog.info("Network asset load complete: " + tuple); LimeLog.info("Network asset load complete: " + tuple);
@@ -3,6 +3,7 @@ package com.limelight.preferences;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
public class PreferenceConfiguration { public class PreferenceConfiguration {
@@ -69,9 +70,18 @@ public class PreferenceConfiguration {
public static boolean getDefaultSmallMode(Context context) { public static boolean getDefaultSmallMode(Context context) {
PackageManager manager = context.getPackageManager(); PackageManager manager = context.getPackageManager();
if (manager != null && manager.hasSystemFeature(PackageManager.FEATURE_TELEVISION)) { if (manager != null) {
// TVs shouldn't use small mode by default // TVs shouldn't use small mode by default
return false; if (manager.hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
return false;
}
// API 21 uses LEANBACK instead of TELEVISION
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
if (manager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
return false;
}
}
} }
// Use small mode on anything smaller than a 7" tablet // Use small mode on anything smaller than a 7" tablet
+1 -1
View File
@@ -4,7 +4,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:1.1.0' classpath 'com.android.tools.build:gradle:1.2.3'
} }
} }
+3 -3
View File
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4"> <module external.linked.project.id="moonlight-android" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager"> <component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle"> <facet type="java-gradle" name="Java-Gradle">
<configuration> <configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" /> <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration> </configuration>
</facet> </facet>
</component> </component>
@@ -15,5 +16,4 @@
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
</module> </module>