@@ -24,3 +24,6 @@ proguard/ | |||
# Log Files | |||
*.log | |||
.idea | |||
*.iml |
@@ -1,2 +1,27 @@ | |||
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. |
@@ -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' | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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")); | |||
} | |||
} | |||
} |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -0,0 +1,8 @@ | |||
<resources> | |||
<!-- Base application theme. --> | |||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> | |||
<!-- Customize your theme here. --> | |||
</style> | |||
</resources> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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() | |||
} | |||
} |
@@ -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 |
@@ -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 |
@@ -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 "$@" |
@@ -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 |
@@ -0,0 +1 @@ | |||
include ':app' |