mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 17:11:56 +01:00
First checkin of actual code
This commit is contained in:
parent
8dd67b146d
commit
dada70e92c
3
.gitignore
vendored
3
.gitignore
vendored
@ -24,3 +24,6 @@ proguard/
|
|||||||
|
|
||||||
# Log Files
|
# Log Files
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
25
README.md
25
README.md
@ -1,2 +1,27 @@
|
|||||||
Gadgetbridge
|
Gadgetbridge
|
||||||
============
|
============
|
||||||
|
|
||||||
|
Gadgetbridge is a Android (4.4+) Application which will allow you to use your
|
||||||
|
Gadget (Smartwatches/Fitness Bands etc) without the vendors closed source
|
||||||
|
application and without the need to create an account and sync your data to the
|
||||||
|
vendors servers.
|
||||||
|
|
||||||
|
Right now this is in very early testing stages and only supports the Pebble.
|
||||||
|
|
||||||
|
USE IT AT YOUR OWN RISK. It will problably not work. And if it works it will
|
||||||
|
annoy you more than it helps you ;)
|
||||||
|
|
||||||
|
Known Visible Issues:
|
||||||
|
|
||||||
|
* No special notifications, EVERYTHING will be send as a Chat/SMS message
|
||||||
|
* Notifications are not properly queued, if two arrive at about the same time,
|
||||||
|
one of them will get lost
|
||||||
|
* Connection to Pebble will be reopened and closed for every message (dont know
|
||||||
|
if this saves or consumes more energy)
|
||||||
|
* Android 4.4+ only, we can only change this by implementing an
|
||||||
|
AccessibiltyService. Don't know if it is worth the hassle.
|
||||||
|
* This will open the dialog to allow capturing notifications every time the
|
||||||
|
Activity gets restarted.
|
||||||
|
|
||||||
|
Apart from that there are many internal design flaws which we will discuss using
|
||||||
|
the issue tracker.
|
||||||
|
26
app/build.gradle
Normal file
26
app/build.gradle
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 21
|
||||||
|
buildToolsVersion "21.1.2"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "nodomain.freeyourgadget.gadgetbridge"
|
||||||
|
minSdkVersion 19
|
||||||
|
targetSdkVersion 21
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
compile 'com.android.support:appcompat-v7:21.0.3'
|
||||||
|
compile 'com.android.support:support-v4:21.0.3'
|
||||||
|
}
|
17
app/proguard-rules.pro
vendored
Normal file
17
app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in /home/andi/Android/Sdk/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
@ -0,0 +1,13 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.test.ApplicationTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||||
|
*/
|
||||||
|
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||||
|
public ApplicationTest() {
|
||||||
|
super(Application.class);
|
||||||
|
}
|
||||||
|
}
|
36
app/src/main/AndroidManifest.xml
Normal file
36
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="nodomain.freeyourgadget.gadgetbridge">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
|
||||||
|
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
<activity
|
||||||
|
android:name="nodomain.freeyourgadget.gadgetbridge.SettingsActivity"
|
||||||
|
android:label="@string/app_name"></activity>
|
||||||
|
<activity
|
||||||
|
android:name="nodomain.freeyourgadget.gadgetbridge.ControlCenter"
|
||||||
|
android:label="@string/title_activity_main">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name="nodomain.freeyourgadget.gadgetbridge.NotificationListener"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.service.notification.NotificationListenerService" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
@ -0,0 +1,236 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge;
|
||||||
|
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
import android.support.v7.app.ActionBarActivity;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ControlCenter extends ActionBarActivity {
|
||||||
|
// SPP Serial Device UUID
|
||||||
|
private static final UUID SERIAL_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
|
||||||
|
|
||||||
|
BluetoothAdapter mBtAdapter;
|
||||||
|
BluetoothDevice mBtDevice;
|
||||||
|
BluetoothSocket mBtSocket;
|
||||||
|
Button sendButton;
|
||||||
|
Button testNotificationButton;
|
||||||
|
EditText editTitle;
|
||||||
|
EditText editContent;
|
||||||
|
private NotificationReceiver nReceiver;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_controlcenter);
|
||||||
|
|
||||||
|
//Check the system status
|
||||||
|
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
if (mBtAdapter == null) {
|
||||||
|
Toast.makeText(this, "Bluetooth is not supported.", Toast.LENGTH_SHORT).show();
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!mBtAdapter.isEnabled()) {
|
||||||
|
Toast.makeText(this, "Bluetooth is disabled.", Toast.LENGTH_SHORT).show();
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
|
||||||
|
for (BluetoothDevice device : pairedDevices) {
|
||||||
|
if (device.getName().indexOf("Pebble") == 0) {
|
||||||
|
// Matching device found
|
||||||
|
mBtDevice = device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editTitle = (EditText) findViewById(R.id.editTitle);
|
||||||
|
editContent = (EditText) findViewById(R.id.editContent);
|
||||||
|
sendButton = (Button) findViewById(R.id.sendButton);
|
||||||
|
sendButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
|
||||||
|
if (!mBtAdapter.isEnabled() || mBtDevice == null)
|
||||||
|
return;
|
||||||
|
String title = editTitle.getText().toString();
|
||||||
|
String content = editContent.getText().toString();
|
||||||
|
try {
|
||||||
|
if (mBtSocket == null || !mBtSocket.isConnected()) {
|
||||||
|
mBtSocket = mBtDevice.createRfcommSocketToServiceRecord(SERIAL_UUID);
|
||||||
|
mBtSocket.connect();
|
||||||
|
}
|
||||||
|
ConnectedTask task = new ConnectedTask();
|
||||||
|
task.execute(mBtSocket, title, content);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
testNotificationButton = (Button) findViewById(R.id.testNotificationButton);
|
||||||
|
testNotificationButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
testNotification();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Intent enableIntent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
|
||||||
|
|
||||||
|
startActivity(enableIntent);
|
||||||
|
nReceiver = new NotificationReceiver();
|
||||||
|
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction("nodomain.freeyourgadget.gadgetbridge.NOTIFICATION_LISTENER");
|
||||||
|
registerReceiver(nReceiver, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testNotification() {
|
||||||
|
NotificationManager nManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||||
|
NotificationCompat.Builder ncomp = new NotificationCompat.Builder(this);
|
||||||
|
ncomp.setContentTitle("Test Notification");
|
||||||
|
ncomp.setContentText("This is a Test Notification from Gadgetbridge");
|
||||||
|
ncomp.setTicker("This is a Test Notificytion from Gadgetbridge");
|
||||||
|
ncomp.setSmallIcon(R.drawable.ic_launcher);
|
||||||
|
ncomp.setAutoCancel(true);
|
||||||
|
nManager.notify((int) System.currentTimeMillis(), ncomp.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
getMenuInflater().inflate(R.menu.menu_main, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
// Handle action bar item clicks here. The action bar will
|
||||||
|
// automatically handle clicks on the Home/Up button, so long
|
||||||
|
// as you specify a parent activity in AndroidManifest.xml.
|
||||||
|
int id = item.getItemId();
|
||||||
|
|
||||||
|
//noinspection SimplifiableIfStatement
|
||||||
|
if (id == R.id.action_settings) {
|
||||||
|
//Intent intent = new Intent(this, SettingsActivity.class);
|
||||||
|
//startActivity(intent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
try {
|
||||||
|
if (mBtSocket != null) {
|
||||||
|
mBtSocket.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (nReceiver != null) {
|
||||||
|
unregisterReceiver(nReceiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//AsyncTask to receive a single line of data and post
|
||||||
|
private class ConnectedTask extends
|
||||||
|
AsyncTask<Object, Void, String> {
|
||||||
|
@Override
|
||||||
|
protected String doInBackground(
|
||||||
|
Object... params) {
|
||||||
|
InputStream in = null;
|
||||||
|
OutputStream out = null;
|
||||||
|
BluetoothSocket socket = (BluetoothSocket) params[0];
|
||||||
|
String title = (String) params[1];
|
||||||
|
String content = (String) params[2];
|
||||||
|
try {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
String result;
|
||||||
|
in = socket.getInputStream();
|
||||||
|
|
||||||
|
//in.read(buffer);
|
||||||
|
//result = PebbleProtocol.decodeResponse(buffer);
|
||||||
|
|
||||||
|
out = socket.getOutputStream();
|
||||||
|
|
||||||
|
|
||||||
|
byte[] msg;
|
||||||
|
msg = PebbleProtocol.encodeSMS(title, content);
|
||||||
|
|
||||||
|
//msg = PebbleProtocol.encodeSetTime();
|
||||||
|
//msg = PebbleProtocol.encodeIncomingCall("03012323", title);
|
||||||
|
//msg = PebbleProtocol.encodeEmail(title, "subject", content);
|
||||||
|
|
||||||
|
out.write(msg);
|
||||||
|
SystemClock.sleep(500);
|
||||||
|
//in.read(buffer);
|
||||||
|
//result = PebbleProtocol.decodeResponse(buffer);
|
||||||
|
result = "ok";
|
||||||
|
//Close the connection
|
||||||
|
return result.trim();
|
||||||
|
} catch (Exception exc) {
|
||||||
|
return "error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(String result) {
|
||||||
|
Toast.makeText(ControlCenter.this, result,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
try {
|
||||||
|
mBtSocket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotificationReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (!mBtAdapter.isEnabled() || mBtDevice == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String title = intent.getStringExtra("notification_title");
|
||||||
|
String content = intent.getStringExtra("notification_content");
|
||||||
|
try {
|
||||||
|
if (mBtSocket == null || !mBtSocket.isConnected()) {
|
||||||
|
mBtSocket = mBtDevice.createRfcommSocketToServiceRecord(SERIAL_UUID);
|
||||||
|
mBtSocket.connect();
|
||||||
|
}
|
||||||
|
ConnectedTask task = new ConnectedTask();
|
||||||
|
task.execute(mBtSocket, title, content);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.service.notification.NotificationListenerService;
|
||||||
|
import android.service.notification.StatusBarNotification;
|
||||||
|
|
||||||
|
public class NotificationListener extends NotificationListenerService {
|
||||||
|
|
||||||
|
private String TAG = this.getClass().getSimpleName();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNotificationPosted(StatusBarNotification sbn) {
|
||||||
|
Intent i = new Intent("nodomain.freeyourgadget.gadgetbridge.NOTIFICATION_LISTENER");
|
||||||
|
Notification notification = sbn.getNotification();
|
||||||
|
Bundle extras = notification.extras;
|
||||||
|
String title = extras.getCharSequence(Notification.EXTRA_TITLE).toString();
|
||||||
|
String content = extras.getCharSequence(Notification.EXTRA_TEXT).toString();
|
||||||
|
i.putExtra("notification_title", title);
|
||||||
|
i.putExtra("notification_content", content);
|
||||||
|
sendBroadcast(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNotificationRemoved(StatusBarNotification sbn) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.SimpleTimeZone;
|
||||||
|
|
||||||
|
public class PebbleProtocol {
|
||||||
|
static final short ENDPOINT_FIRMWARE = 1;
|
||||||
|
static final short ENDPOINT_TIME = 11;
|
||||||
|
static final short ENDPOINT_FIRMWAREVERSION = 16;
|
||||||
|
static final short ENDPOINT_PHONEVERSION = 17;
|
||||||
|
static final short ENDPOINT_SYSTEMMESSAGE = 18;
|
||||||
|
static final short ENDPOINT_MUSICCONTROL = 32;
|
||||||
|
static final short ENDPOINT_PHONECONTROL = 33;
|
||||||
|
static final short ENDPOINT_APPLICATIONMESSAGE = 48;
|
||||||
|
static final short ENDPOINT_LAUNCHER = 49;
|
||||||
|
static final short ENDPOINT_LOGS = 2000;
|
||||||
|
static final short ENDPOINT_PING = 2001;
|
||||||
|
static final short ENDPOINT_LOGDUMP = 2002;
|
||||||
|
static final short ENDPOINT_RESET = 2003;
|
||||||
|
static final short ENDPOINT_APP = 2004;
|
||||||
|
static final short ENDPOINT_APPLOGS = 2006;
|
||||||
|
static final short ENDPOINT_NOTIFICATION = 3000;
|
||||||
|
static final short ENDPOINT_RESOURCE = 4000;
|
||||||
|
static final short ENDPOINT_SYSREG = 5000;
|
||||||
|
static final short ENDPOINT_FCTREG = 5001;
|
||||||
|
static final short ENDPOINT_APPMANAGER = 6000;
|
||||||
|
static final short ENDPOINT_RUNKEEPER = 7000;
|
||||||
|
static final short ENDPOINT_SCREENSHOT = 8000;
|
||||||
|
static final short ENDPOINT_PUTBYTES = (short) 48879;
|
||||||
|
|
||||||
|
static final byte NOTIFICATION_EMAIL = 0;
|
||||||
|
static final byte NOTIFICATION_SMS = 1;
|
||||||
|
static final byte NOTIFICATION_TWITTER = 2;
|
||||||
|
static final byte NOTIFICATION_FACEBOOK = 3;
|
||||||
|
|
||||||
|
static final byte PHONECONTROL_ANSWER = 1;
|
||||||
|
static final byte PHONECONTROL_HANGUP = 2;
|
||||||
|
static final byte PHONECONTROL_GETSTATE = 3;
|
||||||
|
static final byte PHONECONTROL_INCOMINGCALL = 4;
|
||||||
|
static final byte PHONECONTROL_OUTGOINGCALL = 5;
|
||||||
|
static final byte PHONECONTROL_MISSEDCALL = 6;
|
||||||
|
static final byte PHONECONTROL_RING = 7;
|
||||||
|
static final byte PHONECONTROL_START = 8;
|
||||||
|
static final byte PHONECONTROL_END = 9;
|
||||||
|
|
||||||
|
static final byte TIME_GETTIME = 0;
|
||||||
|
static final byte TIME_SETTIME = 2;
|
||||||
|
|
||||||
|
static final byte LENGTH_PREFIX = 4;
|
||||||
|
static final byte LENGTH_SETTIME = 9;
|
||||||
|
|
||||||
|
static byte[] encodeMessage(short endpoint, byte type, String[] parts) {
|
||||||
|
// Calculate length first
|
||||||
|
int length = LENGTH_PREFIX + 1;
|
||||||
|
for (String s : parts) {
|
||||||
|
length += (1 + s.getBytes().length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode Prefix
|
||||||
|
ByteBuffer buf = ByteBuffer.allocate(length);
|
||||||
|
buf.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
buf.putShort((short) (length - LENGTH_PREFIX));
|
||||||
|
buf.putShort(endpoint);
|
||||||
|
buf.put(type);
|
||||||
|
|
||||||
|
// Encode Pascal-Style Strings
|
||||||
|
for (String s : parts) {
|
||||||
|
|
||||||
|
int partlength = s.getBytes().length;
|
||||||
|
if (partlength > 255) partlength = 255;
|
||||||
|
buf.put((byte) partlength);
|
||||||
|
buf.put(s.getBytes(), 0, partlength);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encodeSMS(String from, String body) {
|
||||||
|
Long ts = System.currentTimeMillis() / 1000;
|
||||||
|
String tsstring = ts.toString(); // SIC
|
||||||
|
String[] parts = {from, body, tsstring};
|
||||||
|
|
||||||
|
return encodeMessage(ENDPOINT_NOTIFICATION, NOTIFICATION_SMS, parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encodeEmail(String from, String subject, String body) {
|
||||||
|
Long ts = System.currentTimeMillis() / 1000;
|
||||||
|
String tsstring = ts.toString(); // SIC
|
||||||
|
String[] parts = {from, body, tsstring, subject};
|
||||||
|
|
||||||
|
return encodeMessage(ENDPOINT_NOTIFICATION, NOTIFICATION_EMAIL, parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encodeSetTime() {
|
||||||
|
long ts = System.currentTimeMillis() / 1000;
|
||||||
|
ts += SimpleTimeZone.getDefault().getOffset(ts) / 1000;
|
||||||
|
ByteBuffer buf = ByteBuffer.allocate(LENGTH_SETTIME);
|
||||||
|
buf.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
buf.putShort((short) (LENGTH_SETTIME - LENGTH_PREFIX));
|
||||||
|
buf.putShort(ENDPOINT_TIME);
|
||||||
|
buf.put(TIME_SETTIME);
|
||||||
|
buf.putInt((int) ts);
|
||||||
|
|
||||||
|
return buf.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] encodeIncomingCall(String number, String name) {
|
||||||
|
String cookie = "000"; // That's a dirty trick to make the cookie part 4 bytes long :P
|
||||||
|
String[] parts = {cookie, number, name};
|
||||||
|
return encodeMessage(ENDPOINT_PHONECONTROL, PHONECONTROL_INCOMINGCALL, parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: that should return data into some unified struct/Class
|
||||||
|
public static String decodeResponse(byte[] responseData) {
|
||||||
|
ByteBuffer buf = ByteBuffer.wrap(responseData);
|
||||||
|
buf.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
short length = buf.getShort();
|
||||||
|
short endpoint = buf.getShort();
|
||||||
|
byte extra = 0;
|
||||||
|
|
||||||
|
switch (endpoint) {
|
||||||
|
case ENDPOINT_PHONECONTROL:
|
||||||
|
extra = buf.get();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
String ret = Short.toString(length) + "/" + Short.toString(endpoint) + "/" + Byte.toString(extra);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,258 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.media.Ringtone;
|
||||||
|
import android.media.RingtoneManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.ListPreference;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.PreferenceActivity;
|
||||||
|
import android.preference.PreferenceCategory;
|
||||||
|
import android.preference.PreferenceFragment;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.preference.RingtonePreference;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link PreferenceActivity} that presents a set of application settings. On
|
||||||
|
* handset devices, settings are presented as a single list. On tablets,
|
||||||
|
* settings are split by category, with category headers shown to the left of
|
||||||
|
* the list of settings.
|
||||||
|
* <p/>
|
||||||
|
* See <a href="http://developer.android.com/design/patterns/settings.html">
|
||||||
|
* Android Design: Settings</a> for design guidelines and the <a
|
||||||
|
* href="http://developer.android.com/guide/topics/ui/settings.html">Settings
|
||||||
|
* API Guide</a> for more information on developing a Settings UI.
|
||||||
|
*/
|
||||||
|
public class SettingsActivity extends PreferenceActivity {
|
||||||
|
/**
|
||||||
|
* Determines whether to always show the simplified settings UI, where
|
||||||
|
* settings are presented in a single list. When false, settings are shown
|
||||||
|
* as a master/detail two-pane view on tablets. When true, a single pane is
|
||||||
|
* shown on tablets.
|
||||||
|
*/
|
||||||
|
private static final boolean ALWAYS_SIMPLE_PREFS = false;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostCreate(Bundle savedInstanceState) {
|
||||||
|
super.onPostCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setupSimplePreferencesScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the simplified settings UI if the device configuration if the
|
||||||
|
* device configuration dictates that a simplified, single-pane UI should be
|
||||||
|
* shown.
|
||||||
|
*/
|
||||||
|
private void setupSimplePreferencesScreen() {
|
||||||
|
if (!isSimplePreferences(this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the simplified UI, fragments are not used at all and we instead
|
||||||
|
// use the older PreferenceActivity APIs.
|
||||||
|
|
||||||
|
// Add 'general' preferences.
|
||||||
|
addPreferencesFromResource(R.xml.pref_general);
|
||||||
|
|
||||||
|
// Add 'notifications' preferences, and a corresponding header.
|
||||||
|
PreferenceCategory fakeHeader = new PreferenceCategory(this);
|
||||||
|
fakeHeader.setTitle(R.string.pref_header_notifications);
|
||||||
|
getPreferenceScreen().addPreference(fakeHeader);
|
||||||
|
addPreferencesFromResource(R.xml.pref_notification);
|
||||||
|
|
||||||
|
// Add 'data and sync' preferences, and a corresponding header.
|
||||||
|
fakeHeader = new PreferenceCategory(this);
|
||||||
|
fakeHeader.setTitle(R.string.pref_header_data_sync);
|
||||||
|
getPreferenceScreen().addPreference(fakeHeader);
|
||||||
|
addPreferencesFromResource(R.xml.pref_data_sync);
|
||||||
|
|
||||||
|
// Bind the summaries of EditText/List/Dialog/Ringtone preferences to
|
||||||
|
// their values. When their values change, their summaries are updated
|
||||||
|
// to reflect the new value, per the Android Design guidelines.
|
||||||
|
bindPreferenceSummaryToValue(findPreference("example_text"));
|
||||||
|
bindPreferenceSummaryToValue(findPreference("example_list"));
|
||||||
|
bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone"));
|
||||||
|
bindPreferenceSummaryToValue(findPreference("sync_frequency"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean onIsMultiPane() {
|
||||||
|
return isXLargeTablet(this) && !isSimplePreferences(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to determine if the device has an extra-large screen. For
|
||||||
|
* example, 10" tablets are extra-large.
|
||||||
|
*/
|
||||||
|
private static boolean isXLargeTablet(Context context) {
|
||||||
|
return (context.getResources().getConfiguration().screenLayout
|
||||||
|
& Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the simplified settings UI should be shown. This is
|
||||||
|
* true if this is forced via {@link #ALWAYS_SIMPLE_PREFS}, or the device
|
||||||
|
* doesn't have newer APIs like {@link PreferenceFragment}, or the device
|
||||||
|
* doesn't have an extra-large screen. In these cases, a single-pane
|
||||||
|
* "simplified" settings UI should be shown.
|
||||||
|
*/
|
||||||
|
private static boolean isSimplePreferences(Context context) {
|
||||||
|
return ALWAYS_SIMPLE_PREFS
|
||||||
|
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB
|
||||||
|
|| !isXLargeTablet(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
public void onBuildHeaders(List<Header> target) {
|
||||||
|
if (!isSimplePreferences(this)) {
|
||||||
|
loadHeadersFromResource(R.xml.pref_headers, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A preference value change listener that updates the preference's summary
|
||||||
|
* to reflect its new value.
|
||||||
|
*/
|
||||||
|
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object value) {
|
||||||
|
String stringValue = value.toString();
|
||||||
|
|
||||||
|
if (preference instanceof ListPreference) {
|
||||||
|
// For list preferences, look up the correct display value in
|
||||||
|
// the preference's 'entries' list.
|
||||||
|
ListPreference listPreference = (ListPreference) preference;
|
||||||
|
int index = listPreference.findIndexOfValue(stringValue);
|
||||||
|
|
||||||
|
// Set the summary to reflect the new value.
|
||||||
|
preference.setSummary(
|
||||||
|
index >= 0
|
||||||
|
? listPreference.getEntries()[index]
|
||||||
|
: null);
|
||||||
|
|
||||||
|
} else if (preference instanceof RingtonePreference) {
|
||||||
|
// For ringtone preferences, look up the correct display value
|
||||||
|
// using RingtoneManager.
|
||||||
|
if (TextUtils.isEmpty(stringValue)) {
|
||||||
|
// Empty values correspond to 'silent' (no ringtone).
|
||||||
|
preference.setSummary(R.string.pref_ringtone_silent);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Ringtone ringtone = RingtoneManager.getRingtone(
|
||||||
|
preference.getContext(), Uri.parse(stringValue));
|
||||||
|
|
||||||
|
if (ringtone == null) {
|
||||||
|
// Clear the summary if there was a lookup error.
|
||||||
|
preference.setSummary(null);
|
||||||
|
} else {
|
||||||
|
// Set the summary to reflect the new ringtone display
|
||||||
|
// name.
|
||||||
|
String name = ringtone.getTitle(preference.getContext());
|
||||||
|
preference.setSummary(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// For all other preferences, set the summary to the value's
|
||||||
|
// simple string representation.
|
||||||
|
preference.setSummary(stringValue);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a preference's summary to its value. More specifically, when the
|
||||||
|
* preference's value is changed, its summary (line of text below the
|
||||||
|
* preference title) is updated to reflect the value. The summary is also
|
||||||
|
* immediately updated upon calling this method. The exact display format is
|
||||||
|
* dependent on the type of preference.
|
||||||
|
*
|
||||||
|
* @see #sBindPreferenceSummaryToValueListener
|
||||||
|
*/
|
||||||
|
private static void bindPreferenceSummaryToValue(Preference preference) {
|
||||||
|
// Set the listener to watch for value changes.
|
||||||
|
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
|
||||||
|
|
||||||
|
// Trigger the listener immediately with the preference's
|
||||||
|
// current value.
|
||||||
|
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
|
||||||
|
PreferenceManager
|
||||||
|
.getDefaultSharedPreferences(preference.getContext())
|
||||||
|
.getString(preference.getKey(), ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This fragment shows general preferences only. It is used when the
|
||||||
|
* activity is showing a two-pane settings UI.
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
public static class GeneralPreferenceFragment extends PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
addPreferencesFromResource(R.xml.pref_general);
|
||||||
|
|
||||||
|
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
|
||||||
|
// to their values. When their values change, their summaries are
|
||||||
|
// updated to reflect the new value, per the Android Design
|
||||||
|
// guidelines.
|
||||||
|
bindPreferenceSummaryToValue(findPreference("example_text"));
|
||||||
|
bindPreferenceSummaryToValue(findPreference("example_list"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This fragment shows notification preferences only. It is used when the
|
||||||
|
* activity is showing a two-pane settings UI.
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
public static class NotificationPreferenceFragment extends PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
addPreferencesFromResource(R.xml.pref_notification);
|
||||||
|
|
||||||
|
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
|
||||||
|
// to their values. When their values change, their summaries are
|
||||||
|
// updated to reflect the new value, per the Android Design
|
||||||
|
// guidelines.
|
||||||
|
bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This fragment shows data and sync preferences only. It is used when the
|
||||||
|
* activity is showing a two-pane settings UI.
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
public static class DataSyncPreferenceFragment extends PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
addPreferencesFromResource(R.xml.pref_data_sync);
|
||||||
|
|
||||||
|
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
|
||||||
|
// to their values. When their values change, their summaries are
|
||||||
|
// updated to reflect the new value, per the Android Design
|
||||||
|
// guidelines.
|
||||||
|
bindPreferenceSummaryToValue(findPreference("sync_frequency"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
app/src/main/res/drawable-hdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.2 KiB |
BIN
app/src/main/res/drawable-mdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_launcher.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
54
app/src/main/res/layout/activity_controlcenter.xml
Normal file
54
app/src/main/res/layout/activity_controlcenter.xml
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
tools:context="nodomain.freeyourgadget.gadgetbridge.ControlCenter">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:text="From:" />
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/editTitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/label"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:inputType="textEmailAddress" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:layout_below="@+id/editTitle"
|
||||||
|
android:text="Message"/>
|
||||||
|
<EditText
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/editContent"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignRight="@+id/editTitle"
|
||||||
|
android:layout_alignEnd="@+id/editTitle"
|
||||||
|
android:layout_below="@+id/textView" />
|
||||||
|
<Button
|
||||||
|
android:id="@+id/sendButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Send to Pebble"
|
||||||
|
android:layout_above="@+id/testNotificationButton"
|
||||||
|
android:layout_alignParentStart="true" />
|
||||||
|
<Button
|
||||||
|
android:id="@+id/testNotificationButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Create test Notification"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_alignParentStart="true" />
|
||||||
|
</RelativeLayout>
|
7
app/src/main/res/menu/menu_main.xml
Normal file
7
app/src/main/res/menu/menu_main.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<menu 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"
|
||||||
|
tools:context="nodomain.freeyourgadget.gadgetbridge.ControlCenter">
|
||||||
|
<item android:id="@+id/action_settings" android:title="@string/action_settings"
|
||||||
|
android:orderInCategory="100" app:showAsAction="never" />
|
||||||
|
</menu>
|
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||||
|
(such as screen margins) for screens with more than 820dp of available width. This
|
||||||
|
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||||
|
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||||
|
</resources>
|
5
app/src/main/res/values/dimens.xml
Normal file
5
app/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
</resources>
|
9
app/src/main/res/values/strings.xml
Normal file
9
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">Gadgetbridge</string>
|
||||||
|
<string name="title_activity_main">Gadgetbridge Control Center</string>
|
||||||
|
<string name="hello_world">Hello world!</string>
|
||||||
|
<string name="action_settings">Settings</string>
|
||||||
|
|
||||||
|
</resources>
|
60
app/src/main/res/values/strings_activity_settings.xml
Normal file
60
app/src/main/res/values/strings_activity_settings.xml
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Strings related to Settings -->
|
||||||
|
|
||||||
|
<!-- Example General settings -->
|
||||||
|
<string name="pref_header_general">General</string>
|
||||||
|
|
||||||
|
<string name="pref_title_social_recommendations">Enable social recommendations</string>
|
||||||
|
<string name="pref_description_social_recommendations">Recommendations for people to contact
|
||||||
|
based on your message history
|
||||||
|
</string>
|
||||||
|
|
||||||
|
<string name="pref_title_display_name">Display name</string>
|
||||||
|
<string name="pref_default_display_name">John Smith</string>
|
||||||
|
|
||||||
|
<string name="pref_title_add_friends_to_messages">Add friends to messages</string>
|
||||||
|
<string-array name="pref_example_list_titles">
|
||||||
|
<item>Always</item>
|
||||||
|
<item>When possible</item>
|
||||||
|
<item>Never</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="pref_example_list_values">
|
||||||
|
<item>1</item>
|
||||||
|
<item>0</item>
|
||||||
|
<item>-1</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<!-- Example settings for Data & Sync -->
|
||||||
|
<string name="pref_header_data_sync">Data & sync</string>
|
||||||
|
|
||||||
|
<string name="pref_title_sync_frequency">Sync frequency</string>
|
||||||
|
<string-array name="pref_sync_frequency_titles">
|
||||||
|
<item>15 minutes</item>
|
||||||
|
<item>30 minutes</item>
|
||||||
|
<item>1 hour</item>
|
||||||
|
<item>3 hours</item>
|
||||||
|
<item>6 hours</item>
|
||||||
|
<item>Never</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="pref_sync_frequency_values">
|
||||||
|
<item>15</item>
|
||||||
|
<item>30</item>
|
||||||
|
<item>60</item>
|
||||||
|
<item>180</item>
|
||||||
|
<item>360</item>
|
||||||
|
<item>-1</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string name="pref_title_system_sync_settings">System sync settings</string>
|
||||||
|
|
||||||
|
<!-- Example settings for Notifications -->
|
||||||
|
<string name="pref_header_notifications">Notifications</string>
|
||||||
|
|
||||||
|
<string name="pref_title_new_message_notifications">New message notifications</string>
|
||||||
|
|
||||||
|
<string name="pref_title_ringtone">Ringtone</string>
|
||||||
|
<string name="pref_ringtone_silent">Silent</string>
|
||||||
|
|
||||||
|
<string name="pref_title_vibrate">Vibrate</string>
|
||||||
|
</resources>
|
8
app/src/main/res/values/styles.xml
Normal file
8
app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
21
app/src/main/res/xml/pref_data_sync.xml
Normal file
21
app/src/main/res/xml/pref_data_sync.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
|
||||||
|
dismiss it. -->
|
||||||
|
<!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
|
||||||
|
<ListPreference
|
||||||
|
android:key="sync_frequency"
|
||||||
|
android:title="@string/pref_title_sync_frequency"
|
||||||
|
android:entries="@array/pref_sync_frequency_titles"
|
||||||
|
android:entryValues="@array/pref_sync_frequency_values"
|
||||||
|
android:defaultValue="180"
|
||||||
|
android:negativeButtonText="@null"
|
||||||
|
android:positiveButtonText="@null" />
|
||||||
|
|
||||||
|
<!-- This preference simply launches an intent when selected. Use this UI sparingly, per
|
||||||
|
design guidelines. -->
|
||||||
|
<Preference android:title="@string/pref_title_system_sync_settings">
|
||||||
|
<intent android:action="android.settings.SYNC_SETTINGS" />
|
||||||
|
</Preference>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
33
app/src/main/res/xml/pref_general.xml
Normal file
33
app/src/main/res/xml/pref_general.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="example_checkbox"
|
||||||
|
android:title="@string/pref_title_social_recommendations"
|
||||||
|
android:summary="@string/pref_description_social_recommendations"
|
||||||
|
android:defaultValue="true" />
|
||||||
|
|
||||||
|
<!-- NOTE: EditTextPreference accepts EditText attributes. -->
|
||||||
|
<!-- NOTE: EditTextPreference's summary should be set to its value by the activity code. -->
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="example_text"
|
||||||
|
android:title="@string/pref_title_display_name"
|
||||||
|
android:defaultValue="@string/pref_default_display_name"
|
||||||
|
android:selectAllOnFocus="true"
|
||||||
|
android:inputType="textCapWords"
|
||||||
|
android:capitalize="words"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:maxLines="1" />
|
||||||
|
|
||||||
|
<!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
|
||||||
|
dismiss it. -->
|
||||||
|
<!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
|
||||||
|
<ListPreference
|
||||||
|
android:key="example_list"
|
||||||
|
android:title="@string/pref_title_add_friends_to_messages"
|
||||||
|
android:defaultValue="-1"
|
||||||
|
android:entries="@array/pref_example_list_titles"
|
||||||
|
android:entryValues="@array/pref_example_list_values"
|
||||||
|
android:negativeButtonText="@null"
|
||||||
|
android:positiveButtonText="@null" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
15
app/src/main/res/xml/pref_headers.xml
Normal file
15
app/src/main/res/xml/pref_headers.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- These settings headers are only used on tablets. -->
|
||||||
|
|
||||||
|
<header android:fragment="nodomain.freeyourgadget.gadgetbridge.SettingsActivity$GeneralPreferenceFragment"
|
||||||
|
android:title="@string/pref_header_general" />
|
||||||
|
|
||||||
|
<header
|
||||||
|
android:fragment="nodomain.freeyourgadget.gadgetbridge.SettingsActivity$NotificationPreferenceFragment"
|
||||||
|
android:title="@string/pref_header_notifications" />
|
||||||
|
|
||||||
|
<header android:fragment="nodomain.freeyourgadget.gadgetbridge.SettingsActivity$DataSyncPreferenceFragment"
|
||||||
|
android:title="@string/pref_header_data_sync" />
|
||||||
|
|
||||||
|
</preference-headers>
|
27
app/src/main/res/xml/pref_notification.xml
Normal file
27
app/src/main/res/xml/pref_notification.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- A 'parent' preference, which enables/disables child preferences (below)
|
||||||
|
when checked/unchecked. -->
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="notifications_new_message"
|
||||||
|
android:title="@string/pref_title_new_message_notifications"
|
||||||
|
android:defaultValue="true" />
|
||||||
|
|
||||||
|
<!-- Allows the user to choose a ringtone in the 'notification' category. -->
|
||||||
|
<!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
|
||||||
|
<!-- NOTE: RingtonePreference's summary should be set to its value by the activity code. -->
|
||||||
|
<RingtonePreference
|
||||||
|
android:dependency="notifications_new_message"
|
||||||
|
android:key="notifications_new_message_ringtone"
|
||||||
|
android:title="@string/pref_title_ringtone"
|
||||||
|
android:ringtoneType="notification"
|
||||||
|
android:defaultValue="content://settings/system/notification_sound" />
|
||||||
|
|
||||||
|
<!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:dependency="notifications_new_message"
|
||||||
|
android:key="notifications_new_message_vibrate"
|
||||||
|
android:title="@string/pref_title_vibrate"
|
||||||
|
android:defaultValue="true" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
19
build.gradle
Normal file
19
build.gradle
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:1.0.0'
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
}
|
18
gradle.properties
Normal file
18
gradle.properties
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||||
|
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
|
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Wed Apr 10 15:27:10 PDT 2013
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
|
164
gradlew
vendored
Executable file
164
gradlew
vendored
Executable file
@ -0,0 +1,164 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn ( ) {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die ( ) {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||||
|
if $cygwin ; then
|
||||||
|
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >&-
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >&-
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||||
|
function splitJvmOpts() {
|
||||||
|
JVM_OPTS=("$@")
|
||||||
|
}
|
||||||
|
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||||
|
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
90
gradlew.bat
vendored
Normal file
90
gradlew.bat
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windowz variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
goto execute
|
||||||
|
|
||||||
|
:4NT_args
|
||||||
|
@rem Get arguments from the 4NT Shell from JP Software
|
||||||
|
set CMD_LINE_ARGS=%$
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@ -0,0 +1 @@
|
|||||||
|
include ':app'
|
Loading…
Reference in New Issue
Block a user