mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-26 06:22:45 +00:00
268 lines
9.6 KiB
Java
268 lines
9.6 KiB
Java
package com.limelight.preferences;
|
|
|
|
import java.io.IOException;
|
|
import java.net.Inet4Address;
|
|
import java.net.InetAddress;
|
|
import java.net.InterfaceAddress;
|
|
import java.net.NetworkInterface;
|
|
import java.net.SocketException;
|
|
import java.net.UnknownHostException;
|
|
import java.util.Collections;
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
|
|
import com.limelight.binding.PlatformBinding;
|
|
import com.limelight.computers.ComputerManagerService;
|
|
import com.limelight.R;
|
|
import com.limelight.nvstream.http.ComputerDetails;
|
|
import com.limelight.nvstream.http.NvHTTP;
|
|
import com.limelight.nvstream.jni.MoonBridge;
|
|
import com.limelight.utils.Dialog;
|
|
import com.limelight.utils.ServerHelper;
|
|
import com.limelight.utils.SpinnerDialog;
|
|
import com.limelight.utils.UiHelper;
|
|
|
|
import android.app.Activity;
|
|
import android.app.Service;
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.ServiceConnection;
|
|
import android.os.Bundle;
|
|
import android.os.IBinder;
|
|
import android.view.KeyEvent;
|
|
import android.view.View;
|
|
import android.view.inputmethod.EditorInfo;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
public class AddComputerManually extends Activity {
|
|
private TextView hostText;
|
|
private ComputerManagerService.ComputerManagerBinder managerBinder;
|
|
private final LinkedBlockingQueue<String> computersToAdd = new LinkedBlockingQueue<>();
|
|
private Thread addThread;
|
|
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
|
public void onServiceConnected(ComponentName className, final IBinder binder) {
|
|
managerBinder = ((ComputerManagerService.ComputerManagerBinder)binder);
|
|
startAddThread();
|
|
}
|
|
|
|
public void onServiceDisconnected(ComponentName className) {
|
|
joinAddThread();
|
|
managerBinder = null;
|
|
}
|
|
};
|
|
|
|
private boolean isWrongSubnetSiteLocalAddress(String address) {
|
|
try {
|
|
InetAddress targetAddress = InetAddress.getByName(address);
|
|
if (!(targetAddress instanceof Inet4Address) || !targetAddress.isSiteLocalAddress()) {
|
|
return false;
|
|
}
|
|
|
|
// We have a site-local address. Look for a matching local interface.
|
|
for (NetworkInterface iface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
|
|
for (InterfaceAddress addr : iface.getInterfaceAddresses()) {
|
|
if (!(addr.getAddress() instanceof Inet4Address) || !addr.getAddress().isSiteLocalAddress()) {
|
|
// Skip non-site-local or non-IPv4 addresses
|
|
continue;
|
|
}
|
|
|
|
byte[] targetAddrBytes = targetAddress.getAddress();
|
|
byte[] ifaceAddrBytes = addr.getAddress().getAddress();
|
|
|
|
// Compare prefix to ensure it's the same
|
|
boolean addressMatches = true;
|
|
for (int i = 0; i < addr.getNetworkPrefixLength(); i++) {
|
|
if ((ifaceAddrBytes[i / 8] & (1 << (i % 8))) != (targetAddrBytes[i / 8] & (1 << (i % 8)))) {
|
|
addressMatches = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (addressMatches) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Couldn't find a matching interface
|
|
return true;
|
|
} catch (SocketException e) {
|
|
e.printStackTrace();
|
|
return false;
|
|
} catch (UnknownHostException e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void doAddPc(String host) {
|
|
boolean wrongSiteLocal = false;
|
|
boolean success;
|
|
int portTestResult;
|
|
|
|
SpinnerDialog dialog = SpinnerDialog.displayDialog(this, getResources().getString(R.string.title_add_pc),
|
|
getResources().getString(R.string.msg_add_pc), false);
|
|
|
|
try {
|
|
ComputerDetails details = new ComputerDetails();
|
|
details.manualAddress = host;
|
|
success = managerBinder.addComputerBlocking(details);
|
|
} catch (IllegalArgumentException e) {
|
|
// This can be thrown from OkHttp if the host fails to canonicalize to a valid name.
|
|
// https://github.com/square/okhttp/blob/okhttp_27/okhttp/src/main/java/com/squareup/okhttp/HttpUrl.java#L705
|
|
e.printStackTrace();
|
|
success = false;
|
|
}
|
|
if (!success){
|
|
wrongSiteLocal = isWrongSubnetSiteLocalAddress(host);
|
|
}
|
|
if (!success && !wrongSiteLocal) {
|
|
// Run the test before dismissing the spinner because it can take a few seconds.
|
|
portTestResult = MoonBridge.testClientConnectivity(ServerHelper.CONNECTION_TEST_SERVER, 443,
|
|
MoonBridge.ML_PORT_FLAG_TCP_47984 | MoonBridge.ML_PORT_FLAG_TCP_47989);
|
|
} else {
|
|
// Don't bother with the test if we succeeded or the IP address was bogus
|
|
portTestResult = MoonBridge.ML_TEST_RESULT_INCONCLUSIVE;
|
|
}
|
|
|
|
dialog.dismiss();
|
|
|
|
if (wrongSiteLocal) {
|
|
Dialog.displayDialog(this, getResources().getString(R.string.conn_error_title), getResources().getString(R.string.addpc_wrong_sitelocal), false);
|
|
}
|
|
else if (!success) {
|
|
String dialogText;
|
|
if (portTestResult != MoonBridge.ML_TEST_RESULT_INCONCLUSIVE && portTestResult != 0) {
|
|
dialogText = getResources().getString(R.string.nettest_text_blocked);
|
|
}
|
|
else {
|
|
dialogText = getResources().getString(R.string.addpc_fail);
|
|
}
|
|
Dialog.displayDialog(this, getResources().getString(R.string.conn_error_title), dialogText, false);
|
|
}
|
|
else {
|
|
AddComputerManually.this.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Toast.makeText(AddComputerManually.this, getResources().getString(R.string.addpc_success), Toast.LENGTH_LONG).show();
|
|
|
|
if (!isFinishing()) {
|
|
// Close the activity
|
|
AddComputerManually.this.finish();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
private void startAddThread() {
|
|
addThread = new Thread() {
|
|
@Override
|
|
public void run() {
|
|
while (!isInterrupted()) {
|
|
String computer;
|
|
|
|
try {
|
|
computer = computersToAdd.take();
|
|
} catch (InterruptedException e) {
|
|
return;
|
|
}
|
|
|
|
doAddPc(computer);
|
|
}
|
|
}
|
|
};
|
|
addThread.setName("UI - AddComputerManually");
|
|
addThread.start();
|
|
}
|
|
|
|
private void joinAddThread() {
|
|
if (addThread != null) {
|
|
addThread.interrupt();
|
|
|
|
try {
|
|
addThread.join();
|
|
} catch (InterruptedException ignored) {}
|
|
|
|
addThread = null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onStop() {
|
|
super.onStop();
|
|
|
|
Dialog.closeDialogs();
|
|
SpinnerDialog.closeDialogs(this);
|
|
}
|
|
|
|
@Override
|
|
protected void onDestroy() {
|
|
super.onDestroy();
|
|
|
|
if (managerBinder != null) {
|
|
joinAddThread();
|
|
unbindService(serviceConnection);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
UiHelper.setLocale(this);
|
|
|
|
setContentView(R.layout.activity_add_computer_manually);
|
|
|
|
UiHelper.notifyNewRootView(this);
|
|
|
|
this.hostText = findViewById(R.id.hostTextView);
|
|
hostText.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
|
hostText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
|
@Override
|
|
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
|
|
if (actionId == EditorInfo.IME_ACTION_DONE ||
|
|
(keyEvent != null &&
|
|
keyEvent.getAction() == KeyEvent.ACTION_DOWN &&
|
|
keyEvent.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
|
|
return handleDoneEvent();
|
|
}
|
|
else if (actionId == EditorInfo.IME_ACTION_PREVIOUS) {
|
|
// This is how the Fire TV dismisses the keyboard
|
|
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
imm.hideSoftInputFromWindow(hostText.getWindowToken(), 0);
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
});
|
|
|
|
findViewById(R.id.addPcButton).setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View view) {
|
|
handleDoneEvent();
|
|
}
|
|
});
|
|
|
|
// Bind to the ComputerManager service
|
|
bindService(new Intent(AddComputerManually.this,
|
|
ComputerManagerService.class), serviceConnection, Service.BIND_AUTO_CREATE);
|
|
}
|
|
|
|
// Returns true if the event should be eaten
|
|
private boolean handleDoneEvent() {
|
|
String hostAddress = hostText.getText().toString().trim();
|
|
|
|
if (hostAddress.length() == 0) {
|
|
Toast.makeText(AddComputerManually.this, getResources().getString(R.string.addpc_enter_ip), Toast.LENGTH_LONG).show();
|
|
return true;
|
|
}
|
|
|
|
computersToAdd.add(hostAddress);
|
|
return false;
|
|
}
|
|
}
|