Compare commits

...

17 Commits

Author SHA1 Message Date
Dan Ballard 082beb5691 orbot setup instructions in README 2018-07-09 16:47:38 -05:00
Dan Ballard 66a25b0efa goRicochetMobile changed namespace: moving od work to specific namespace 2017-10-18 01:00:52 -07:00
Dan Ballard 2da20a5962 0.9.0 2017-09-23 17:05:58 -07:00
Dan Ballard 45d56ae601 cleaning up logging 2017-09-23 09:16:37 -07:00
Dan Ballard 9b1e768226 Disconnect 2017-09-23 09:04:47 -07:00
Dan Ballard 14fc595760 persist the 3 values across activity creation 2017-09-23 08:34:50 -07:00
Dan Ballard fd5b1826de wired up all basic OD client functionality (except disconnect) 2017-09-20 08:27:11 -07:00
Dan Ballard 63b4a86999 remote activity java. local vars for all controls, get device name and set 2017-09-18 08:34:54 -07:00
Dan Ballard 3ee025fb23 mocked out remote activity and transition to 2017-09-14 08:09:46 -07:00
Dan Ballard 00b8ff4fc3 remove richochet: prefix from address 2017-09-10 10:42:57 -07:00
Dan Ballard 9a1d8df640 on connect failure reenable buttons 2017-09-07 08:13:10 -07:00
Dan Ballard fc279e8062 wire in connect button 2017-09-07 08:11:44 -07:00
Dan Ballard 1e48f69691 move echobot code for testing out of the way 2017-09-06 20:33:45 -07:00
Dan Ballard 6baa48596b validate input onion address, save, enable connect button 2017-09-05 08:17:26 -07:00
Dan Ballard 3da1cc5032 add spinner/status dedicated to 'new identity' task 2017-09-04 20:58:50 -07:00
Dan Ballard c68a45f088 finish ui for connect activity. display identity. 'new identity' functionality 2017-09-04 20:37:16 -07:00
Dan Ballard 767fccef40 mock out rest of connect activity, start writing regen ident code 2017-09-03 16:02:40 -07:00
11 changed files with 631 additions and 56 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
*.iml
*.aar
*.apk
.gradle
/local.properties
/.idea/workspace.xml

View File

@ -4,3 +4,13 @@ A simple proof of concept usage of Ricochet + Tor on mobile
Uses:
- Orbot
- gomobile: to leverage the go ricochet protocol library
## Orbot setup
In Orbot's Settings, find at the bottom `Debug / Torrc Custom Config` and input:
```
ControlPort 9051
CookieAuthentication 0
DisableNetwork 0
```

View File

@ -8,7 +8,7 @@ android {
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
versionName "0.9.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
@ -24,9 +24,10 @@ dependencies {
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile project(':goRicochetMobile')
compile project(':goRicochetMobileOd')
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile project(':goRicochetMobile')
testCompile 'junit:junit:4.12'
}

View File

@ -12,14 +12,14 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="im.ricochet.androidod.ConnectActivity">
<activity android:name=".ConnectActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".RemoteActivity"></activity>
</application>
</manifest>

View File

@ -1,66 +1,223 @@
package im.ricochet.androidod;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.StrictMode;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import goRicochetMobile.GoRicochetMobile;
import od.Od;
public class ConnectActivity extends AppCompatActivity {
private static final int ONION_ADDRESS_LENGTH = 16;
public static final String PREFERNCE_FILE = "im.ricochet.PREFERENCE_FILE";
private static final String TAG = "InitActivity";
private static final String PRIVATE_KEY_KEY = "privateKey";
private static final String CONNECT_TO_ADDRESS_KEY = "connectToAddress";
private static final String RICOCHET_ADDRESS_PREFIX = "ricochet:";
Button regenButton;
Button connectButton;
ProgressBar connectSpinner;
ProgressBar regenIdentSpinner;
TextView connectStatusText;
TextView regenIdentStatusText;
TextView idetityText;
EditText addressText;
SharedPreferences prefs;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(im.ricochet.androidod.R.layout.activity_connect);
TextView progressText = (TextView)findViewById(im.ricochet.androidod.R.id.progressTextView);
progressText.setText("Loading private key...");
connectStatusText = (TextView)findViewById(R.id.connectStatusTextView);
regenIdentStatusText = (TextView)findViewById(R.id.regenIdentStatusText);
idetityText = (TextView)findViewById(R.id.identityTextView);
regenButton = (Button)findViewById(R.id.regenIdentButton);
connectButton = (Button)findViewById(R.id.connectButton);
connectSpinner = (ProgressBar)findViewById(R.id.connectProgressBar);
regenIdentSpinner = (ProgressBar)findViewById(R.id.regenIdentProgressBar);
addressText = (EditText)findViewById(R.id.addressText);
prefs = getSharedPreferences(PREFERNCE_FILE, MODE_PRIVATE);
regenButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
RegenIdentTask regenIdentTask = new RegenIdentTask();
regenIdentTask.execute();
}
});
connectButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
ConnectTask connectTask = new ConnectTask();
String connectToAddr = addressText.getText().toString().replaceFirst(RICOCHET_ADDRESS_PREFIX, "");
String privateKey = prefs.getString(PRIVATE_KEY_KEY, "");
connectTask.execute(privateKey, connectToAddr);
}
});
addressText.addTextChangedListener(new TextWatcher(){
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
validateOnionAddress();
}
@Override
public void afterTextChanged(Editable s) {
}
});
Log.i(TAG, "Loading private key...");
SharedPreferences prefs = getSharedPreferences(PREFERNCE_FILE, MODE_PRIVATE);
String privateKey = prefs.getString(PRIVATE_KEY_KEY, "");
Log.i(TAG, "Loaded! '" + privateKey + "'");
Log.i(TAG, "Private key loaded:\n"+privateKey);
if (privateKey.equals("")) {
Log.i(TAG, "Failed to load private key from preferences, generating new");
progressText.setText("Generating private key...");
Log.i(TAG, "Generating pricate key");
RegenIdentTask regenIdentTask = new RegenIdentTask();
regenIdentTask.execute();
} else {
Log.i(TAG, "Setting identity, Enabling regen");
setIdentity(privateKey);
Button regenButton = (Button)findViewById(R.id.regenIdentButton);
regenButton.setEnabled(true);
}
String connectToAddress = prefs.getString(CONNECT_TO_ADDRESS_KEY, RICOCHET_ADDRESS_PREFIX);
addressText.setText(connectToAddress);
validateOnionAddress();
}
private class ConnectTask extends AsyncTask<String, Void, String> {
@Override
protected void onPreExecute() {
Log.i(TAG, "ConnectTask.onPreExecute()");
regenButton.setEnabled(false);
connectButton.setEnabled(false);
connectStatusText.setText("Connecting to Onion server...");
connectSpinner.setVisibility(View.VISIBLE);
}
@Override
// execute(privateKey, connectToAddr)
// return string of exception if failed
public String doInBackground(String... params) {
Log.i(TAG, "ConntectTask.doInBackground()");
String privateKey = params[0];
String connectToAddr = params[1].replaceFirst(RICOCHET_ADDRESS_PREFIX, "");
try {
privateKey = GoRicochetMobile.generatePrivateKey();
Od.odClientConnect(privateKey, connectToAddr);
return "";
} catch (Exception e) {
Log.e(TAG, e.toString());
return e.toString();
}
Log.i(TAG, "Generated!: '" + privateKey + "'");
SharedPreferences.Editor prefsEditor = prefs.edit();
prefsEditor.putString(PRIVATE_KEY_KEY, privateKey);
prefsEditor.commit();
}
Log.i(TAG, "Ready!");
progressText.setText("Ready!");
ProgressBar progressBar = (ProgressBar)findViewById(im.ricochet.androidod.R.id.progressBar);
progressBar.setVisibility(View.GONE);
@Override
protected void onPostExecute(String exception) {
Log.i(TAG, "ConnectTask.onPostExecute(): " );
connectSpinner.setVisibility(View.INVISIBLE);
if (exception != "") {
connectStatusText.setText("ERROR connecting: " + exception);
connectButton.setEnabled(true);
regenButton.setEnabled(true);
} else {
connectStatusText.setText("Connected!");
Intent i = new Intent(getApplicationContext(), RemoteActivity.class);
// i,putExtra("key", "val")
startActivity(i);
}
}
}
private void setIdentity(String privateKey) {
Log.i(TAG, "setIdentity()");
String addr = GoRicochetMobile.getOnionAddress(privateKey);
Log.i(TAG, "setIdentity(): addr: '" + addr + "'");
idetityText.setText("ricochet:" + addr);
}
private void validateOnionAddress() {
String address = addressText.getText().toString();
if (address.startsWith(RICOCHET_ADDRESS_PREFIX) && address.length() == ONION_ADDRESS_LENGTH + RICOCHET_ADDRESS_PREFIX.length()) {
SharedPreferences.Editor prefsEditor = prefs.edit();
prefsEditor.putString(CONNECT_TO_ADDRESS_KEY, address);
prefsEditor.commit();
connectButton.setEnabled(true);
} else {
connectButton.setEnabled(false);
}
}
private class RegenIdentTask extends AsyncTask<Void, Void, String> {
@Override
protected void onPreExecute() {
Log.i(TAG, "RegenIdentTask.onPreExecute()");
regenButton.setEnabled(false);
connectButton.setEnabled(false);
regenIdentStatusText.setText("Generating new identity...");
regenIdentSpinner.setVisibility(View.VISIBLE);
}
@Override
public String doInBackground(Void... voids) {
Log.i(TAG, "RegenIdentTask.doInBackground()");
String privateKey;
try {
privateKey = GoRicochetMobile.generatePrivateKey();
SharedPreferences.Editor prefsEditor = prefs.edit();
prefsEditor.putString(PRIVATE_KEY_KEY, privateKey);
prefsEditor.commit();
return privateKey;
} catch (Exception e) {
Log.e(TAG, e.toString());
return null;
}
}
@Override
protected void onPostExecute(String privateKey) {
Log.i(TAG, "RegenIdentTask.onPostExecute(): " + privateKey);
regenIdentSpinner.setVisibility(View.INVISIBLE);
regenButton.setEnabled(true);
validateOnionAddress();
if (privateKey == null) {
regenIdentStatusText.setText("ERROR: unable to generate new identity");
} else {
regenIdentStatusText.setText("");
setIdentity(privateKey);
}
}
}
/********** TESTING by standing up an echobot ************/
private void echoBot(String privateKey) {
// Test echobot
Log.i(TAG, "Setting thread policy perms");
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
Log.i(TAG, "Starting Echo Bot");
//GoRicochetMobile.echoBot(privateKey);
new EchoBot(privateKey).execute();
}
private class EchoBot extends AsyncTask<Void, Void, Void> {

View File

@ -0,0 +1,184 @@
package im.ricochet.androidod;
import android.content.Intent;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import od.Od;
public class RemoteActivity extends AppCompatActivity {
private static final String DEVICE_NAME_KEY = "deviceName";
private static final String BATTERY_LEVEL_KEY = "batteryLevel";
private static final String VIBE_LEVEL_KEY = "vibeLEvel";
TextView deviceText;
TextView batteryText;
TextView statusText;
Button offButton;
Button lowButton;
Button medButton;
Button highButton;
Button disconnectButton;
int vibeLevel = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_remote);
deviceText = (TextView)findViewById(R.id.deviceName);
batteryText = (TextView)findViewById(R.id.batteryLevel);
statusText = (TextView)findViewById(R.id.status);
offButton = (Button)findViewById(R.id.offButton);
lowButton = (Button)findViewById(R.id.lowButton);
medButton = (Button)findViewById(R.id.medButton);
highButton = (Button)findViewById(R.id.highButton);
disconnectButton = (Button)findViewById(R.id.disconnetButton);
offButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SetLevelTask setLevelTask = new SetLevelTask();
setLevelTask.execute(0);
}
});
lowButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SetLevelTask setLevelTask = new SetLevelTask();
setLevelTask.execute(2);
}
});
medButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SetLevelTask setLevelTask = new SetLevelTask();
setLevelTask.execute(4);
}
});
highButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SetLevelTask setLevelTask = new SetLevelTask();
setLevelTask.execute(6);
}
});
disconnectButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
DisconnectTask disconnectTask = new DisconnectTask();
disconnectTask.execute();
}
});
if (savedInstanceState != null) {
deviceText.setText(savedInstanceState.getString(DEVICE_NAME_KEY));
batteryText.setText(savedInstanceState.getString(BATTERY_LEVEL_KEY));
vibeLevel = savedInstanceState.getInt(VIBE_LEVEL_KEY);
setVibeLevel(vibeLevel);
} else {
GetDeviceNameTask getDeviceNameTask = new GetDeviceNameTask();
getDeviceNameTask.execute();
GetBatteryTask getBatteryTask = new GetBatteryTask();
getBatteryTask.execute();
GetLevelTask getLevelTask = new GetLevelTask();
getLevelTask.execute();
}
}
@Override
protected void onSaveInstanceState(final Bundle outState) {
outState.putString(DEVICE_NAME_KEY, deviceText.getText().toString());
outState.putString(BATTERY_LEVEL_KEY, batteryText.getText().toString());
outState.putInt(VIBE_LEVEL_KEY, vibeLevel);
}
// current impelementation of OD server can return 0 to 6 (potentially downsampled from 12?)
// The go API respects that, further downsample to [off low med high] here
// Assuming 1 means off and 2-6 are active states...
private void setVibeLevel(int newVibeLevel) {
vibeLevel = newVibeLevel;
if (vibeLevel == 1) {
statusText.setText("Off");
} else if (vibeLevel == 2) {
statusText.setText("Low");
} else if (vibeLevel == 3 || vibeLevel == 4) {
statusText.setText("Medium");
} else if (vibeLevel == 5 || vibeLevel == 6) {
statusText.setText("High");
}
}
private class GetDeviceNameTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {
String name = Od.getDeviceName();
return name;
}
@Override
protected void onPostExecute(String deviceName) {
deviceText.setText(deviceName);
}
}
private class GetBatteryTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {
String batteryLevel = Od.getBatteryLevel();
return batteryLevel;
}
@Override
protected void onPostExecute(String batteryLevel) {
batteryText.setText(batteryLevel + "%");
}
}
private class GetLevelTask extends AsyncTask<Void, Void, Integer> {
@Override
protected Integer doInBackground(Void... params) {
Integer vibeLevel = (int) Od.getVibeLevel();
return vibeLevel;
}
@Override
protected void onPostExecute(Integer newVibeLevel) {
setVibeLevel(newVibeLevel);
}
}
private class SetLevelTask extends AsyncTask<Integer, Void, Integer> {
@Override
protected Integer doInBackground(Integer... params) {
Integer newVibeLevel = params[0];
Od.setVibeLevel(newVibeLevel);
return newVibeLevel;
}
@Override
protected void onPostExecute(Integer newVibeLevel) {
setVibeLevel(newVibeLevel);
}
}
private class DisconnectTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void ...params) {
Od.odClientDisconnect();
return null;
}
@Override
protected void onPostExecute(Void nothing) {
Intent i = new Intent(getApplicationContext(), ConnectActivity.class);
startActivity(i);
}
}
}

View File

@ -7,64 +7,72 @@
tools:context="im.ricochet.androidod.im.ricochet.androidod.ConnectActivity">
<ProgressBar
android:id="@+id/progressBar"
android:id="@+id/connectProgressBar"
style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="32dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/connect" />
android:layout_marginTop="7dp"
android:visibility="invisible"
app:layout_constraintLeft_toRightOf="@+id/connectButton"
app:layout_constraintTop_toBottomOf="@+id/addressText"
android:layout_marginStart="8dp" />
<EditText
android:id="@+id/addressText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="241dp"
android:layout_height="42dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="9dp"
android:ems="10"
android:inputType="text"
android:minEms="26"
android:text="ricochet:"
app:layout_constraintHorizontal_bias="0.503"
app:layout_constraintHorizontal_bias="0.518"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
app:layout_constraintTop_toBottomOf="@+id/textView"
tools:layout_editor_absoluteX="50dp" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Onion Address:"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"
android:layout_marginLeft="0dp"
app:layout_constraintLeft_toLeftOf="@+id/addressText" />
android:text="Connect to Onion address:"
app:layout_constraintLeft_toLeftOf="@+id/addressText"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/connect"
android:id="@+id/connectButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:layout_marginTop="7dp"
android:enabled="false"
android:text="Connect"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/addressText" />
app:layout_constraintTop_toBottomOf="@+id/addressText"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
<TextView
android:id="@+id/progressTextView"
android:id="@+id/connectStatusTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/connectButton"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressBar" />
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
<TextView
android:id="@+id/textView2"
@ -72,11 +80,14 @@
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="32dp"
android:text="Required step: Orbot/Settings/Torrc Custom Config:"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressTextView" />
app:layout_constraintHorizontal_bias="0.511"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="@+id/connectStatusTextView"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
<TextView
android:id="@+id/textView3"
@ -88,7 +99,9 @@
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent" />
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
<TextView
android:id="@+id/textView4"
@ -97,6 +110,71 @@
android:text="CookieAuthentication 0"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/textView3"
android:layout_marginLeft="0dp"
app:layout_constraintLeft_toLeftOf="@+id/textView3" />
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DisableNetwork 0"
app:layout_constraintLeft_toLeftOf="@+id/textView4"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/textView4" />
<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="Identity:"
app:layout_constraintTop_toBottomOf="@+id/textView5"
app:layout_constraintLeft_toLeftOf="@+id/regenIdentButton" />
<TextView
android:id="@+id/identityTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ricochet:"
app:layout_constraintLeft_toRightOf="@+id/textView6"
android:layout_marginLeft="8dp"
app:layout_constraintTop_toTopOf="@+id/textView6"
android:layout_marginStart="8dp" />
<Button
android:id="@+id/regenIdentButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="7dp"
android:enabled="false"
android:text="New Identity"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView6"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
<ProgressBar
android:id="@+id/regenIdentProgressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="0dp"
android:visibility="invisible"
app:layout_constraintLeft_toRightOf="@+id/regenIdentButton"
app:layout_constraintTop_toTopOf="@+id/regenIdentButton" />
<TextView
android:id="@+id/regenIdentStatusText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/regenIdentButton"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>

View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="im.ricochet.androidod.RemoteActivity">
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Device:"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/deviceName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/textView7"
android:layout_marginLeft="8dp"
app:layout_constraintTop_toTopOf="@+id/textView7"
android:layout_marginTop="0dp" />
<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="Battery:"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView7" />
<TextView
android:id="@+id/batteryLevel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:text="0%"
app:layout_constraintLeft_toRightOf="@+id/textView9"
app:layout_constraintTop_toTopOf="@+id/textView9"
android:layout_marginTop="0dp" />
<TextView
android:id="@+id/TextView10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="Status:"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView9" />
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toRightOf="@+id/TextView10"
app:layout_constraintTop_toTopOf="@+id/TextView10"
android:layout_marginTop="0dp" />
<Button
android:id="@+id/offButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Off"
app:layout_constraintTop_toBottomOf="@+id/TextView10"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent" />
<Button
android:id="@+id/lowButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
android:text="Low"
app:layout_constraintTop_toBottomOf="@+id/offButton"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent" />
<Button
android:id="@+id/medButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="Medium"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/lowButton" />
<Button
android:id="@+id/highButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="High"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/medButton" />
<Button
android:id="@+id/disconnetButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="64dp"
android:text="Disconnect"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/highButton" />
</android.support.constraint.ConstraintLayout>

View File

@ -8,4 +8,5 @@ gobind {
GOMOBILE = "/home/user/src/go/bin/gomobile"
GOBIND = "/home/user/src/go/bin/gobind"
GO = "/home/user/go/bin/go"
}
}

View File

@ -0,0 +1,11 @@
plugins {
id "org.golang.mobile.bind" version "0.2.7"
}
gobind {
pkg = "github.com/dballard/goRicochetMobile/od"
GOPATH = "/home/user/src/go"
GOMOBILE = "/home/user/src/go/bin/gomobile"
GOBIND = "/home/user/src/go/bin/gobind"
GO = "/home/user/go/bin/go"
}

View File

@ -1 +1 @@
include ':app', 'goRicochetMobile'
include ':app', 'goRicochetMobile', 'goRicochetMobileOd'