diff --git a/app/build.gradle b/app/build.gradle index c1d02b1b..72c5e489 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { minSdkVersion 16 targetSdkVersion 21 - versionName "3.0-beta1.01" - versionCode = 41 + versionName "3.0-beta2" + versionCode = 43 } productFlavors { diff --git a/app/libs/limelight-common.jar b/app/libs/limelight-common.jar index 34955aaa..bb57e5a1 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 018d16e5..2d7c93de 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -64,6 +64,7 @@ WAIT_CEILING_MS) { - LockSupport.parkNanos(1); - continue; + try { + Thread.sleep(diff - WAIT_CEILING_MS); + } catch (InterruptedException e) { + return; + } + continue; } nextFrameTime = computePresentationTimeMs(targetFps); @@ -194,10 +208,14 @@ public class AndroidCpuDecoderRenderer implements VideoDecoderRenderer { @Override public void stop() { rendererThread.interrupt(); + decoderThread.interrupt(); try { - rendererThread.join(); - } catch (InterruptedException e) { } + rendererThread.join(); + } catch (InterruptedException e) { } + try { + decoderThread.join(); + } catch (InterruptedException e) { } } @Override diff --git a/app/src/main/java/com/limelight/computers/ComputerManagerService.java b/app/src/main/java/com/limelight/computers/ComputerManagerService.java index e1f9ea87..4b73d1f8 100644 --- a/app/src/main/java/com/limelight/computers/ComputerManagerService.java +++ b/app/src/main/java/com/limelight/computers/ComputerManagerService.java @@ -1,8 +1,7 @@ package com.limelight.computers; import java.net.InetAddress; -import java.util.HashMap; -import java.util.List; +import java.util.LinkedList; import java.util.concurrent.atomic.AtomicInteger; import com.limelight.LimeLog; @@ -21,7 +20,7 @@ import android.os.Binder; import android.os.IBinder; public class ComputerManagerService extends Service { - private static final int POLLING_PERIOD_MS = 5000; + private static final int POLLING_PERIOD_MS = 3000; private static final int MDNS_QUERY_PERIOD_MS = 1000; private ComputerManagerBinder binder = new ComputerManagerBinder(); @@ -30,9 +29,10 @@ public class ComputerManagerService extends Service { private AtomicInteger dbRefCount = new AtomicInteger(0); private IdentityManager idManager; - private final HashMap pollingThreads = new HashMap(); + private final LinkedList pollingTuples = new LinkedList(); private ComputerManagerListener listener = null; private AtomicInteger activePolls = new AtomicInteger(0); + private boolean pollingActive = false; private DiscoveryService.DiscoveryBinder discoveryBinder; private final ServiceConnection discoveryServiceConnection = new ServiceConnection() { @@ -101,18 +101,9 @@ public class ComputerManagerService extends Service { Thread t = new Thread() { @Override public void run() { - while (!isInterrupted()) { - ComputerDetails originalDetails = new ComputerDetails(); - originalDetails.update(details); - + while (!isInterrupted() && pollingActive) { // Check if this poll has modified the details - if (runPoll(details) && !originalDetails.equals(details)) { - // Replace our thread entry with the new one - synchronized (pollingThreads) { - pollingThreads.remove(originalDetails); - pollingThreads.put(details, this); - } - } + runPoll(details); // Wait until the next polling interval try { @@ -129,29 +120,24 @@ public class ComputerManagerService extends Service { public class ComputerManagerBinder extends Binder { public void startPolling(ComputerManagerListener listener) { + // Polling is active + pollingActive = true; + // Set the listener ComputerManagerService.this.listener = listener; // Start mDNS autodiscovery too discoveryBinder.startDiscovery(MDNS_QUERY_PERIOD_MS); - - // Start polling known machines - if (!getLocalDatabaseReference()) { - return; - } - List computerList = dbManager.getAllComputers(); - releaseLocalDatabaseReference(); - synchronized (pollingThreads) { - for (ComputerDetails computer : computerList) { + synchronized (pollingTuples) { + for (PollingTuple tuple : pollingTuples) { // This polling thread might already be there - if (!pollingThreads.containsKey(computer)) { + if (tuple.thread == null) { // Report this computer initially - listener.notifyComputerUpdated(computer); + listener.notifyComputerUpdated(tuple.computer); - Thread t = createPollingThread(computer); - pollingThreads.put(computer, t); - t.start(); + tuple.thread = createPollingThread(tuple.computer); + tuple.thread.start(); } } } @@ -205,11 +191,15 @@ public class ComputerManagerService extends Service { discoveryBinder.stopDiscovery(); // Stop polling - synchronized (pollingThreads) { - for (Thread t : pollingThreads.values()) { - t.interrupt(); + pollingActive = false; + synchronized (pollingTuples) { + for (PollingTuple tuple : pollingTuples) { + if (tuple.thread != null) { + // Interrupt and remove the thread + tuple.thread.interrupt(); + tuple.thread = null; + } } - pollingThreads.clear(); } // Remove the listener @@ -245,16 +235,37 @@ public class ComputerManagerService extends Service { fakeDetails.localIp = addr; fakeDetails.remoteIp = addr; - // Spawn a thread for this computer - synchronized (pollingThreads) { - // This polling thread might already be there - if (!pollingThreads.containsKey(fakeDetails)) { - Thread t = createPollingThread(fakeDetails); - pollingThreads.put(fakeDetails, t); - t.start(); + addTuple(fakeDetails); + } + + private void addTuple(ComputerDetails details) { + synchronized (pollingTuples) { + for (PollingTuple tuple : pollingTuples) { + // Check if this is the same computer + if (tuple.computer == details || + tuple.computer.localIp.equals(details.localIp) || + tuple.computer.remoteIp.equals(details.remoteIp) || + tuple.computer.name.equals(details.name)) { + + // Start a polling thread if polling is active + if (pollingActive && tuple.thread == null) { + tuple.thread = createPollingThread(details); + tuple.thread.start(); + } + + // Found an entry so we're done + return; + } + } + + // If we got here, we didn't find an entry + PollingTuple tuple = new PollingTuple(details, pollingActive ? createPollingThread(details) : null); + pollingTuples.add(tuple); + if (tuple.thread != null) { + tuple.thread.start(); } } - } + } public boolean addComputerBlocking(InetAddress addr) { // Setup a placeholder @@ -266,7 +277,14 @@ public class ComputerManagerService extends Service { runPoll(fakeDetails); // If the machine is reachable, it was successful - return fakeDetails.state == ComputerDetails.State.ONLINE; + if (fakeDetails.state == ComputerDetails.State.ONLINE) { + // Start a polling thread for this machine + addTuple(fakeDetails); + return true; + } + else { + return false; + } } public void removeComputer(String name) { @@ -276,6 +294,20 @@ public class ComputerManagerService extends Service { // Remove it from the database dbManager.deleteComputer(name); + + synchronized (pollingTuples) { + // Remove the computer from the computer list + for (PollingTuple tuple : pollingTuples) { + if (tuple.computer.name.equals(name)) { + if (tuple.thread != null) { + // Interrupt the thread on this entry + tuple.thread.interrupt(); + } + pollingTuples.remove(tuple); + break; + } + } + } releaseLocalDatabaseReference(); } @@ -347,7 +379,18 @@ public class ComputerManagerService extends Service { } private boolean doPollMachine(ComputerDetails details) { - return pollComputer(details, true); + if (details.reachability == ComputerDetails.Reachability.UNKNOWN || + details.reachability == ComputerDetails.Reachability.OFFLINE) { + // Always try local first to avoid potential UDP issues when + // attempting to stream via the router's external IP address + // behind its NAT + return pollComputer(details, true); + } + else { + // If we're already reached a machine via a particular IP address, + // always try that one first + return pollComputer(details, details.reachability == ComputerDetails.Reachability.LOCAL); + } } @Override @@ -362,6 +405,18 @@ public class ComputerManagerService extends Service { // Initialize the DB dbManager = new ComputerDatabaseManager(this); dbRefCount.set(1); + + // Grab known machines into our computer list + if (!getLocalDatabaseReference()) { + return; + } + + for (ComputerDetails computer : dbManager.getAllComputers()) { + // Add this computer without a thread + pollingTuples.add(new PollingTuple(computer, null)); + } + + releaseLocalDatabaseReference(); } @Override @@ -382,3 +437,13 @@ public class ComputerManagerService extends Service { return binder; } } + +class PollingTuple { + public Thread thread; + public ComputerDetails computer; + + public PollingTuple(ComputerDetails computer, Thread thread) { + this.computer = computer; + this.thread = thread; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/limelight/grid/AppGridAdapter.java b/app/src/main/java/com/limelight/grid/AppGridAdapter.java index a9cbc243..c2d1624b 100644 --- a/app/src/main/java/com/limelight/grid/AppGridAdapter.java +++ b/app/src/main/java/com/limelight/grid/AppGridAdapter.java @@ -148,6 +148,9 @@ public class AppGridAdapter extends GenericGridAdapter { @Override public boolean populateTextView(TextView txtView, AppView.AppObject obj) { + // Select the text view so it starts marquee mode + txtView.setSelected(true); + // Return false to use the app's toString method return false; } diff --git a/app/src/main/res/layout/activity_app_view.xml b/app/src/main/res/layout/activity_app_view.xml index c7b3c825..f0cc7824 100644 --- a/app/src/main/res/layout/activity_app_view.xml +++ b/app/src/main/res/layout/activity_app_view.xml @@ -14,6 +14,7 @@ android:layout_height="wrap_content" android:numColumns="auto_fit" android:columnWidth="160dp" + android:stretchMode="spacingWidth" android:gravity="center" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" @@ -26,9 +27,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" - android:textAppearance="?android:attr/textAppearanceLarge" android:layout_alignParentTop="true" android:paddingTop="0dp" - android:paddingBottom="10dp" /> + android:paddingBottom="10dp" + android:textSize="28sp"/> \ No newline at end of file diff --git a/app/src/main/res/layout/app_grid_item.xml b/app/src/main/res/layout/app_grid_item.xml index 9449f07d..6a0f3c1c 100644 --- a/app/src/main/res/layout/app_grid_item.xml +++ b/app/src/main/res/layout/app_grid_item.xml @@ -2,16 +2,19 @@ + android:padding="20dp"> + android:layout_width="150dp" + android:layout_height="175dp"> + android:singleLine="true" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" + android:scrollHorizontally="true" + android:textSize="18sp" > \ No newline at end of file diff --git a/app/src/main/res/layout/pc_grid_item.xml b/app/src/main/res/layout/pc_grid_item.xml index 18204684..8347e204 100644 --- a/app/src/main/res/layout/pc_grid_item.xml +++ b/app/src/main/res/layout/pc_grid_item.xml @@ -30,6 +30,6 @@ android:layout_marginTop="10dp" android:layout_centerHorizontal="true" android:gravity="center" - android:textSize="20sp" > + android:textSize="18sp" > \ 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 03d1c1df..c90af480 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -16,7 +16,6 @@ Inserisci il seguente PIN sul PC: PIN non corretto Accoppiamento fallito - Accoppiato con successo PC già avviato @@ -104,4 +103,5 @@ Impostazioni Avanzate Cambia decoder + Il decoder software può aumentare la latenza video quando si usano impostazioni streaming basse diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml index 64631b04..aca590d2 100644 --- a/app/src/main/res/values-v21/styles.xml +++ b/app/src/main/res/values-v21/styles.xml @@ -3,7 +3,7 @@ + + + diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 5a158cdd..c7780c06 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -52,6 +52,7 @@ android:title="@string/title_decoder_list" android:entries="@array/decoder_names" android:entryValues="@array/decoder_values" + android:summary="@string/summary_decoder_list" android:defaultValue="auto" />