diff --git a/CHANGELOG.md b/CHANGELOG.md
index 059f7956..bc66f9c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
###Changelog
+####Version 0.5.1
+* Pebble: support taking screenshot from Pebble Time
+* Fix broken "find lost device" which was broken in 0.5.0
+
####Version 0.5.0
* Mi Band: fix setting wear location
* Pebble: experimental watchapp installation support for FW 3.x/Pebble Time
diff --git a/README.md b/README.md
index 1e936587..c226c409 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,8 @@ need to create an account and transmit any of your data to the vendor's servers.
[![Gadgetbridge on F-Droid](/Get_it_on_F-Droid.svg.png?raw=true "Download from F-Droid")](https://f-droid.org/repository/browse/?fdid=nodomain.freeyourgadget.gadgetbridge)
+[List of changes](CHANGELOG.md)
+
## Features (Pebble)
* Incoming calls notification and display (caller, phone number)
diff --git a/app/build.gradle b/app/build.gradle
index 899f5c54..e088675d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -12,8 +12,8 @@ android {
applicationId "nodomain.freeyourgadget.gadgetbridge"
minSdkVersion 19
targetSdkVersion 21
- versionCode 21
- versionName "0.5.0"
+ versionCode 22
+ versionName "0.5.1"
}
buildTypes {
release {
@@ -31,9 +31,16 @@ android {
// optional path to report (default will be lint-results.html in the builddir)
htmlOutput file("$project.buildDir/reports/lint/lint.html")
}
+
+ testOptions {
+ unitTests.returnDefaultValues = true
+ }
}
dependencies {
+ testCompile 'junit:junit:4.12'
+ testCompile "org.mockito:mockito-core:1.9.5"
+
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.android.support:support-v4:21.0.3'
@@ -41,9 +48,6 @@ dependencies {
compile 'org.slf4j:slf4j-api:1.7.7'
compile 'com.github.PhilJay:MPAndroidChart:2.1.0'
compile 'com.github.pfichtner:durationformatter:0.1.1'
-
- testCompile 'junit:junit:4.12'
-// testCompile "org.mockito:mockito-core:1.9.5"
}
check.dependsOn 'findbugs', 'pmd', 'lint'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 957a6be6..06d5efd5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -25,6 +25,10 @@
android:name="android.hardware.bluetooth_le"
android:required="false" />
+
+
mServiceClass;
- public GBDeviceService(Context context, Class extends Service> serviceClass) {
+ public GBDeviceService(Context context) {
mContext = context;
- mServiceClass = serviceClass;
+ mServiceClass = DeviceCommunicationService.class;
}
- private Intent createIntent() {
+ protected Intent createIntent() {
Intent startIntent = new Intent(mContext, mServiceClass);
return startIntent;
}
@@ -174,7 +175,7 @@ public class GBDeviceService implements DeviceService {
@Override
public void onFindDevice(boolean start) {
Intent intent = createIntent().setAction(ACTION_FIND_DEVICE)
- .putExtra(EXTRA_APP_UUID, start);
+ .putExtra(EXTRA_FIND_START, start);
invokeService(intent);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
index 1c4a906a..04d33fb7 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
@@ -3,5 +3,5 @@ package nodomain.freeyourgadget.gadgetbridge.model;
public enum DeviceType {
UNKNOWN,
PEBBLE,
- MIBAND
+ TEST, MIBAND
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java
index c91200a7..7fa301c3 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java
@@ -13,6 +13,7 @@ import android.net.Uri;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
+import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.widget.Toast;
@@ -27,13 +28,48 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
-import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.*;
+
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CALLSTATE;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DISCONNECT;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FETCH_ACTIVITY_DATA;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FIND_DEVICE;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_INSTALL;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_NOTIFICATION_EMAIL;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_NOTIFICATION_GENERIC;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_NOTIFICATION_SMS;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REBOOT;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REQUEST_APPINFO;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REQUEST_DEVICEINFO;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REQUEST_SCREENSHOT;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SETMUSICINFO;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SETTIME;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SET_ALARMS;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_START;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_STARTAPP;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_ALARMS;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_UUID;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_COMMAND;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_PHONENUMBER;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_DEVICE_ADDRESS;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_TRACK;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_BODY;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SENDER;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SUBJECT;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TITLE;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_PERFORM_PAIR;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_URI;
public class DeviceCommunicationService extends Service {
private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class);
private boolean mStarted = false;
+ private DeviceSupportFactory mFactory;
private GBDevice mGBDevice = null;
private DeviceSupport mDeviceSupport;
@@ -60,6 +96,7 @@ public class DeviceCommunicationService extends Service {
LOG.debug("DeviceCommunicationService is being created");
super.onCreate();
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED));
+ mFactory = new DeviceSupportFactory(this);
}
@Override
@@ -107,32 +144,29 @@ public class DeviceCommunicationService extends Service {
start(); // ensure started
String btDeviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS);
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
- if (btDeviceAddress == null) {
- btDeviceAddress = sharedPrefs.getString("last_device_address", null);
- } else {
- sharedPrefs.edit().putString("last_device_address", btDeviceAddress).apply();
+ if (sharedPrefs != null) { // may be null in test cases
+ if (btDeviceAddress == null) {
+ btDeviceAddress = sharedPrefs.getString("last_device_address", null);
+ } else {
+ sharedPrefs.edit().putString("last_device_address", btDeviceAddress).apply();
+ }
}
if (btDeviceAddress != null && !isConnecting() && !isConnected()) {
- if (mDeviceSupport != null) {
- mDeviceSupport.dispose();
- mDeviceSupport = null;
- }
+ setDeviceSupport(null);
try {
- DeviceSupportFactory factory = new DeviceSupportFactory(this);
- mDeviceSupport = factory.createDeviceSupport(btDeviceAddress);
- if (mDeviceSupport != null) {
- mGBDevice = mDeviceSupport.getDevice();
+ DeviceSupport deviceSupport = mFactory.createDeviceSupport(btDeviceAddress);
+ if (deviceSupport != null) {
+ setDeviceSupport(deviceSupport);
if (pair) {
- mDeviceSupport.pair();
+ deviceSupport.pair();
} else {
- mDeviceSupport.connect();
+ deviceSupport.connect();
}
}
} catch (Exception e) {
GB.toast(this, getString(R.string.cannot_connect, e.getMessage()), Toast.LENGTH_SHORT, GB.ERROR);
- mDeviceSupport = null;
- mGBDevice = null;
+ setDeviceSupport(null);
}
} else if (mGBDevice != null) {
// send an update at least
@@ -231,6 +265,29 @@ public class DeviceCommunicationService extends Service {
return START_STICKY;
}
+ /**
+ * For testing!
+ * @param factory
+ */
+ public void setDeviceSupportFactory(DeviceSupportFactory factory) {
+ mFactory = factory;
+ }
+
+ /**
+ * Disposes the current DeviceSupport instance (if any) and sets a new device support instance
+ * (if not null).
+ * @param deviceSupport
+ */
+ private void setDeviceSupport(@Nullable DeviceSupport deviceSupport) {
+ if (deviceSupport != mDeviceSupport && mDeviceSupport != null) {
+ mDeviceSupport.dispose();
+ mDeviceSupport = null;
+ mGBDevice = null;
+ }
+ mDeviceSupport = deviceSupport;
+ mGBDevice = mDeviceSupport != null ? mDeviceSupport.getDevice() : null;
+ }
+
private void start() {
if (!mStarted) {
startForeground(GB.NOTIFICATION_ID, GB.createNotification(getString(R.string.gadgetbridge_running), this));
@@ -238,6 +295,10 @@ public class DeviceCommunicationService extends Service {
}
}
+ public boolean isStarted() {
+ return mStarted;
+ }
+
private boolean isConnected() {
return mGBDevice != null && mGBDevice.isConnected();
}
@@ -258,9 +319,7 @@ public class DeviceCommunicationService extends Service {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
GB.setReceiversEnableState(false, this); // disable BroadcastReceivers
- if (mDeviceSupport != null) {
- mDeviceSupport.dispose();
- }
+ setDeviceSupport(null);
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel(GB.NOTIFICATION_ID); // need to do this because the updated notification wont be cancelled when service stops
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
index bd3b1bfb..47530d2f 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
@@ -5,6 +5,7 @@ import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.widget.Toast;
+import java.lang.reflect.Constructor;
import java.util.EnumSet;
import nodomain.freeyourgadget.gadgetbridge.GBException;
@@ -26,10 +27,17 @@ public class DeviceSupportFactory {
public synchronized DeviceSupport createDeviceSupport(String deviceAddress) throws GBException {
DeviceSupport deviceSupport;
- if (deviceAddress.indexOf(":") == deviceAddress.lastIndexOf(":")) { // only one colon
- deviceSupport = createTCPDeviceSupport(deviceAddress);
+ int indexFirstColon = deviceAddress.indexOf(":");
+ if (indexFirstColon > 0) {
+ if (indexFirstColon == deviceAddress.lastIndexOf(":")) { // only one colon
+ deviceSupport = createTCPDeviceSupport(deviceAddress);
+ } else {
+ // multiple colons -- bt?
+ deviceSupport = createBTDeviceSupport(deviceAddress);
+ }
} else {
- deviceSupport = createBTDeviceSupport(deviceAddress);
+ // no colon at all, maybe a class name?
+ deviceSupport = createClassNameDeviceSupport(deviceAddress);
}
if (deviceSupport != null) {
@@ -41,6 +49,21 @@ public class DeviceSupportFactory {
return null;
}
+ private DeviceSupport createClassNameDeviceSupport(String className) throws GBException {
+ try {
+ Class> deviceSupportClass = Class.forName(className);
+ Constructor> constructor = deviceSupportClass.getConstructor();
+ DeviceSupport support = (DeviceSupport) constructor.newInstance();
+ // has to create the device itself
+ support.setContext(null, null, mContext);
+ return support;
+ } catch (ClassNotFoundException e) {
+ return null; // not a class, or not known at least
+ } catch (Exception e) {
+ throw new GBException("Error creating DeviceSupport instance for " + className, e);
+ }
+ }
+
private void checkBtAvailability() {
if (mBtAdapter == null) {
GB.toast(mContext.getString(R.string.bluetooth_is_not_supported_), Toast.LENGTH_SHORT, GB.WARN);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java
index 82329a9f..0a60245d 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java
@@ -202,6 +202,97 @@ public class PebbleProtocol extends GBDeviceProtocol {
GBDeviceEventScreenshot mDevEventScreenshot = null;
int mScreenshotRemaining = -1;
+ //monochrome black + white
+ static final byte[] clut_pebble = {
+ 0x00, 0x00, 0x00, 0x00,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00
+ };
+
+ // linear BGR222 (6 bit, 64 entries)
+ static final byte[] clut_pebbletime = new byte[]{
+ 0x00, 0x00, 0x00, 0x00,
+ 0x55, 0x00, 0x00, 0x00,
+ (byte) 0xaa, 0x00, 0x00, 0x00,
+ (byte) 0xff, 0x00, 0x00, 0x00,
+
+ 0x00, 0x55, 0x00, 0x00,
+ 0x55, 0x55, 0x00, 0x00,
+ (byte) 0xaa, 0x55, 0x00, 0x00,
+ (byte) 0xff, 0x55, 0x00, 0x00,
+
+ 0x00, (byte) 0xaa, 0x00, 0x00,
+ 0x55, (byte) 0xaa, 0x00, 0x00,
+ (byte) 0xaa, (byte) 0xaa, 0x00, 0x00,
+ (byte) 0xff, (byte) 0xaa, 0x00, 0x00,
+
+ 0x00, (byte) 0xff, 0x00, 0x00,
+ 0x55, (byte) 0xff, 0x00, 0x00,
+ (byte) 0xaa, (byte) 0xff, 0x00, 0x00,
+ (byte) 0xff, (byte) 0xff, 0x00, 0x00,
+
+ 0x00, 0x00, 0x55, 0x00,
+ 0x55, 0x00, 0x55, 0x00,
+ (byte) 0xaa, 0x00, 0x55, 0x00,
+ (byte) 0xff, 0x00, 0x55, 0x00,
+
+ 0x00, 0x55, 0x55, 0x00,
+ 0x55, 0x55, 0x55, 0x00,
+ (byte) 0xaa, 0x55, 0x55, 0x00,
+ (byte) 0xff, 0x55, 0x55, 0x00,
+
+ 0x00, (byte) 0xaa, 0x55, 0x00,
+ 0x55, (byte) 0xaa, 0x55, 0x00,
+ (byte) 0xaa, (byte) 0xaa, 0x55, 0x00,
+ (byte) 0xff, (byte) 0xaa, 0x55, 0x00,
+
+ 0x00, (byte) 0xff, 0x55, 0x00,
+ 0x55, (byte) 0xff, 0x55, 0x00,
+ (byte) 0xaa, (byte) 0xff, 0x55, 0x00,
+ (byte) 0xff, (byte) 0xff, 0x55, 0x00,
+
+ 0x00, 0x00, (byte) 0xaa, 0x00,
+ 0x55, 0x00, (byte) 0xaa, 0x00,
+ (byte) 0xaa, 0x00, (byte) 0xaa, 0x00,
+ (byte) 0xff, 0x00, (byte) 0xaa, 0x00,
+
+ 0x00, 0x55, (byte) 0xaa, 0x00,
+ 0x55, 0x55, (byte) 0xaa, 0x00,
+ (byte) 0xaa, 0x55, (byte) 0xaa, 0x00,
+ (byte) 0xff, 0x55, (byte) 0xaa, 0x00,
+
+ 0x00, (byte) 0xaa, (byte) 0xaa, 0x00,
+ 0x55, (byte) 0xaa, (byte) 0xaa, 0x00,
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, 0x00,
+ (byte) 0xff, (byte) 0xaa, (byte) 0xaa, 0x00,
+
+ 0x00, (byte) 0xff, (byte) 0xaa, 0x00,
+ 0x55, (byte) 0xff, (byte) 0xaa, 0x00,
+ (byte) 0xaa, (byte) 0xff, (byte) 0xaa, 0x00,
+ (byte) 0xff, (byte) 0xff, (byte) 0xaa, 0x00,
+
+ 0x00, 0x00, (byte) 0xff, 0x00,
+ 0x55, 0x00, (byte) 0xff, 0x00,
+ (byte) 0xaa, 0x00, (byte) 0xff, 0x00,
+ (byte) 0xff, 0x00, (byte) 0xff, 0x00,
+
+ 0x00, 0x55, (byte) 0xff, 0x00,
+ 0x55, 0x55, (byte) 0xff, 0x00,
+ (byte) 0xaa, 0x55, (byte) 0xff, 0x00,
+ (byte) 0xff, 0x55, (byte) 0xff, 0x00,
+
+ 0x00, (byte) 0xaa, (byte) 0xff, 0x00,
+ 0x55, (byte) 0xaa, (byte) 0xff, 0x00,
+ (byte) 0xaa, (byte) 0xaa, (byte) 0xff, 0x00,
+ (byte) 0xff, (byte) 0xaa, (byte) 0xff, 0x00,
+
+ 0x00, (byte) 0xff, (byte) 0xff, 0x00,
+ 0x55, (byte) 0xff, (byte) 0xff, 0x00,
+ (byte) 0xaa, (byte) 0xff, (byte) 0xff, 0x00,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00,
+
+ };
+
+
byte last_id = -1;
private ArrayList tmpUUIDS = new ArrayList<>();
@@ -921,34 +1012,38 @@ public class PebbleProtocol extends GBDeviceProtocol {
byte result = buf.get();
mDevEventScreenshot = new GBDeviceEventScreenshot();
int version = buf.getInt();
- if (result != 0 || version != 1) { // pebble time not yet
+ if (result != 0) {
return null;
}
mDevEventScreenshot.width = buf.getInt();
mDevEventScreenshot.height = buf.getInt();
- mDevEventScreenshot.bpp = 1;
- mDevEventScreenshot.clut = new byte[]{
- 0x00, 0x00, 0x00, 0x00, (byte) 0xff,
- (byte) 0xff, (byte) 0xff, 0x00
- };
- mScreenshotRemaining = (mDevEventScreenshot.width * mDevEventScreenshot.height) / 8;
- if (mScreenshotRemaining > 50000) {
- mScreenshotRemaining = -1; // ignore too big values
- return null;
+ if (version == 1) {
+ mDevEventScreenshot.bpp = 1;
+ mDevEventScreenshot.clut = clut_pebble;
+ } else {
+ mDevEventScreenshot.bpp = 8;
+ mDevEventScreenshot.clut = clut_pebbletime;
}
+
+ mScreenshotRemaining = (mDevEventScreenshot.width * mDevEventScreenshot.height * mDevEventScreenshot.bpp) / 8;
+
mDevEventScreenshot.data = new byte[mScreenshotRemaining];
length -= 13;
}
if (mScreenshotRemaining == -1) {
return null;
}
-
for (int i = 0; i < length; i++) {
- byte corrected = reverseBits(buf.get());
+ byte corrected = buf.get();
+ if (mDevEventScreenshot.bpp == 1) {
+ corrected = reverseBits(corrected);
+ } else {
+ corrected = (byte) (corrected & 0b00111111);
+ }
+
mDevEventScreenshot.data[mDevEventScreenshot.data.length - mScreenshotRemaining + i] = corrected;
}
-
mScreenshotRemaining -= length;
LOG.info("Screenshot remaining bytes " + mScreenshotRemaining);
if (mScreenshotRemaining == 0) {
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java
index d96dbf4a..fb73f9b0 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java
@@ -24,6 +24,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot;
@@ -43,6 +44,7 @@ public class GB {
public static final int INFO = 1;
public static final int WARN = 2;
public static final int ERROR = 3;
+ public static GBEnvironment environment;
public static Notification createNotification(String text, Context context) {
Intent notificationIntent = new Intent(context, ControlCenter.class);
@@ -152,16 +154,16 @@ public class GB {
headerbuf.putInt(screenshot.width);
headerbuf.putInt(-screenshot.height);
headerbuf.putShort((short) 1); // planes
- headerbuf.putShort((short) 1); // bit count
+ headerbuf.putShort((short) screenshot.bpp);
headerbuf.putInt(0); // compression
headerbuf.putInt(0); // length of pixeldata in byte (uncompressed=0)
headerbuf.putInt(0); // pixels per meter (x)
headerbuf.putInt(0); // pixels per meter (y)
- headerbuf.putInt(2); // number of colors in CLUT
- headerbuf.putInt(2); // numbers of used colors
+ headerbuf.putInt(screenshot.clut.length / 4); // number of colors in CLUT
+ headerbuf.putInt(0); // numbers of used colors
headerbuf.put(screenshot.clut);
fos.write(headerbuf.array());
- int rowbytes = screenshot.width / 8;
+ int rowbytes = (screenshot.width * screenshot.bpp) / 8;
byte[] pad = new byte[rowbytes % 4];
for (int i = 0; i < screenshot.height; i++) {
fos.write(screenshot.data, rowbytes * i, rowbytes);
@@ -225,6 +227,9 @@ public class GB {
* @param ex optional exception to be logged
*/
public static void toast(final Context context, final String message, final int displayTime, final int severity, final Throwable ex) {
+ if (env().isLocalTest()) {
+ return;
+ }
Looper mainLooper = Looper.getMainLooper();
if (Thread.currentThread() == mainLooper.getThread()) {
log(message, severity, ex);
@@ -321,4 +326,8 @@ public class GB {
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(NOTIFICATION_ID_LOW_BATTERY, notification);
}
+
+ public static GBEnvironment env() {
+ return environment;
+ }
}
diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractServiceTestCase.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractServiceTestCase.java
new file mode 100644
index 00000000..ea82165d
--- /dev/null
+++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractServiceTestCase.java
@@ -0,0 +1,97 @@
+package nodomain.freeyourgadget.gadgetbridge.service;
+
+import android.app.Application;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+
+import nodomain.freeyourgadget.gadgetbridge.test.GBMockApplication;
+import nodomain.freeyourgadget.gadgetbridge.test.GBMockContext;
+import nodomain.freeyourgadget.gadgetbridge.test.GBMockPackageManager;
+import nodomain.freeyourgadget.gadgetbridge.test.MockHelper;
+
+public abstract class AbstractServiceTestCase {
+ private static final int ID = -1; // currently not supported
+ private Class mServiceClass;
+ private T mServiceInstance;
+ private Context mContext;
+ private Application mApplication;
+ private boolean wasStarted;
+ private PackageManager mPackageManager;
+ private NotificationManager mNotificationManager;
+ private MockHelper mMockHelper;
+
+ protected AbstractServiceTestCase(Class serviceClass) {
+ mServiceClass = serviceClass;
+ Assert.assertNotNull(serviceClass);
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public T getServiceInstance() {
+ return mServiceInstance;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mMockHelper = new MockHelper();
+ mPackageManager = createPackageManager();
+ mApplication = createApplication(mPackageManager);
+ mContext = createContext(mApplication);
+ mNotificationManager = mMockHelper.createNotificationManager(mContext);
+ mServiceInstance = createService(mServiceClass, mApplication, mNotificationManager);
+ mServiceInstance.onCreate();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mServiceInstance != null) {
+ stopService();
+ }
+ }
+
+ public void startService(Intent intent) {
+ wasStarted = true;
+ mServiceInstance.onStartCommand(intent, Service.START_FLAG_REDELIVERY, ID);
+ }
+
+ public void stopService() {
+ mServiceInstance.onDestroy();
+ mServiceInstance = null;
+ }
+
+ protected Application createApplication(PackageManager packageManager) {
+ return new GBMockApplication(packageManager);
+ }
+
+ protected PackageManager createPackageManager() {
+ return new GBMockPackageManager();
+ }
+
+ protected Application getApplication() {
+ return mApplication;
+ }
+
+ protected Context createContext(final Application application) {
+ return new GBMockContext(application);
+ }
+
+ private T createService(Class serviceClass, Application application, NotificationManager notificationManager) throws Exception {
+ T service = mMockHelper.createService(serviceClass, application);
+ mMockHelper.addSystemServiceTo(service, Context.NOTIFICATION_SERVICE, getNotificationService());
+ return service;
+ }
+
+ private NotificationManager getNotificationService() {
+ return mNotificationManager;
+ }
+}
diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java
new file mode 100644
index 00000000..f32f6f9b
--- /dev/null
+++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java
@@ -0,0 +1,82 @@
+package nodomain.freeyourgadget.gadgetbridge.service;
+
+import android.content.Context;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import nodomain.freeyourgadget.gadgetbridge.GBException;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class DeviceCommunicationServiceTestCase extends AbstractServiceTestCase {
+ private static final java.lang.String TEST_DEVICE_ADDRESS = TestDeviceSupport.class.getName();
+
+ /**
+ * Factory that always returns the mockSupport instance
+ */
+ private class TestDeviceSupportFactory extends DeviceSupportFactory {
+ public TestDeviceSupportFactory(Context context) {
+ super(context);
+ }
+
+ @Override
+ public synchronized DeviceSupport createDeviceSupport(String deviceAddress) throws GBException {
+ return mockSupport;
+ }
+ }
+
+ private TestDeviceService mDeviceService;
+ @Mock
+ private TestDeviceSupport realSupport;
+ private TestDeviceSupport mockSupport;
+
+ public DeviceCommunicationServiceTestCase() {
+ super(DeviceCommunicationService.class);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ mockSupport = null;
+ realSupport = new TestDeviceSupport();
+ realSupport.setContext(new GBDevice(TEST_DEVICE_ADDRESS, "Test Device", DeviceType.TEST), null, getContext());
+ mockSupport = Mockito.spy(realSupport);
+ getServiceInstance().setDeviceSupportFactory(new TestDeviceSupportFactory(getContext()));
+
+ mDeviceService = new TestDeviceService(this);
+ }
+
+ @Test
+ public void testStart() {
+ assertFalse("Service was already", getServiceInstance().isStarted());
+ mDeviceService.start();
+ assertTrue("Service should be started", getServiceInstance().isStarted());
+ }
+
+ @Test
+ public void ensureConnected() {
+ mDeviceService.connect(TEST_DEVICE_ADDRESS);
+ Mockito.verify(mockSupport, Mockito.times(1)).connect();
+ assertTrue(realSupport.getDevice().isInitialized());
+ }
+
+ @Test
+ public void testFindDevice() {
+ ensureConnected();
+
+ InOrder inOrder = Mockito.inOrder(mockSupport);
+ mDeviceService.onFindDevice(true);
+ mDeviceService.onFindDevice(false);
+ inOrder.verify(mockSupport, Mockito.times(1)).onFindDevice(true);
+ inOrder.verify(mockSupport, Mockito.times(1)).onFindDevice(false);
+ inOrder.verifyNoMoreInteractions();
+ }
+}
diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceService.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceService.java
new file mode 100644
index 00000000..ad4a11be
--- /dev/null
+++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceService.java
@@ -0,0 +1,25 @@
+package nodomain.freeyourgadget.gadgetbridge.service;
+
+import android.content.Intent;
+
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService;
+import nodomain.freeyourgadget.gadgetbridge.test.GBMockIntent;
+
+public class TestDeviceService extends GBDeviceService {
+ private final AbstractServiceTestCase> mTestCase;
+
+ public TestDeviceService(AbstractServiceTestCase> testCase) throws Exception {
+ super(testCase.getContext());
+ mTestCase = testCase;
+ }
+
+ @Override
+ protected Intent createIntent() {
+ return new GBMockIntent();
+ }
+
+ @Override
+ protected void invokeService(Intent intent) {
+ mTestCase.startService(intent);
+ }
+}
diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java
new file mode 100644
index 00000000..61de0b47
--- /dev/null
+++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java
@@ -0,0 +1,124 @@
+package nodomain.freeyourgadget.gadgetbridge.service;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.UUID;
+
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
+import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand;
+import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
+
+public class TestDeviceSupport extends AbstractDeviceSupport {
+
+ public TestDeviceSupport() {
+ }
+
+ @Override
+ public void setContext(GBDevice gbDevice, BluetoothAdapter btAdapter, Context context) {
+ gbDevice = new GBDevice(getClass().getName(), "Test Device", DeviceType.TEST);
+ super.setContext(gbDevice, btAdapter, context);
+ }
+
+ @Override
+ public boolean connect() {
+ gbDevice.setState(GBDevice.State.INITIALIZED);
+ gbDevice.sendDeviceUpdateIntent(getContext());
+ return true;
+ }
+
+ @Override
+ public void dispose() {
+
+ }
+
+ @Override
+ public boolean useAutoConnect() {
+ return false;
+ }
+
+ @Override
+ public void pair() {
+
+ }
+
+ @Override
+ public void onSMS(String from, String body) {
+
+ }
+
+ @Override
+ public void onEmail(String from, String subject, String body) {
+
+ }
+
+ @Override
+ public void onGenericNotification(String title, String details) {
+
+ }
+
+ @Override
+ public void onSetTime() {
+
+ }
+
+ @Override
+ public void onSetAlarms(ArrayList extends Alarm> alarms) {
+
+ }
+
+ @Override
+ public void onSetCallState(@Nullable String number, @Nullable String name, ServiceCommand command) {
+
+ }
+
+ @Override
+ public void onSetMusicInfo(String artist, String album, String track) {
+
+ }
+
+ @Override
+ public void onInstallApp(Uri uri) {
+
+ }
+
+ @Override
+ public void onAppInfoReq() {
+
+ }
+
+ @Override
+ public void onAppStart(UUID uuid) {
+
+ }
+
+ @Override
+ public void onAppDelete(UUID uuid) {
+
+ }
+
+ @Override
+ public void onFetchActivityData() {
+
+ }
+
+ @Override
+ public void onReboot() {
+
+ }
+
+ @Override
+ public void onFindDevice(boolean start) {
+
+ }
+
+ @Override
+ public void onScreenshotReq() {
+
+ }
+}
diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockApplication.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockApplication.java
new file mode 100644
index 00000000..b9f16b12
--- /dev/null
+++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockApplication.java
@@ -0,0 +1,27 @@
+package nodomain.freeyourgadget.gadgetbridge.test;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.test.mock.MockApplication;
+
+import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
+
+public class GBMockApplication extends MockApplication {
+ private final PackageManager mPackageManager;
+
+ public GBMockApplication(PackageManager packageManager) {
+ GB.environment = GBEnvironment.createDeviceEnvironment().createLocalTestEnvironment();
+ mPackageManager = packageManager;
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return this;
+ }
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+}
diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockContext.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockContext.java
new file mode 100644
index 00000000..ce7d5332
--- /dev/null
+++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockContext.java
@@ -0,0 +1,24 @@
+package nodomain.freeyourgadget.gadgetbridge.test;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.test.mock.MockContext;
+
+public class GBMockContext extends MockContext {
+ private final Application mApplication;
+
+ public GBMockContext(Application application) {
+ mApplication = application;
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return mApplication;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mApplication.getPackageManager();
+ }
+}
diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockIntent.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockIntent.java
new file mode 100644
index 00000000..a6bb9857
--- /dev/null
+++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockIntent.java
@@ -0,0 +1,387 @@
+package nodomain.freeyourgadget.gadgetbridge.test;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class GBMockIntent extends Intent {
+ private String mAction;
+ private Map extras = new HashMap<>();
+
+ @NonNull
+ @Override
+ public Intent setAction(String action) {
+ mAction = action;
+ return this;
+ }
+
+ @Override
+ public String getAction() {
+ return mAction;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, boolean value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, byte value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, char value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, short value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, int value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, long value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, float value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, double value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, String value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, CharSequence value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, Parcelable value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, Parcelable[] value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @Override
+ public Intent putParcelableArrayListExtra(String name, ArrayList extends Parcelable> value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putIntegerArrayListExtra(String name, ArrayList value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putStringArrayListExtra(String name, ArrayList value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putCharSequenceArrayListExtra(String name, ArrayList value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, Serializable value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, boolean[] value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, byte[] value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, short[] value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, char[] value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, int[] value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, long[] value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, float[] value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, double[] value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, String[] value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, CharSequence[] value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public Intent putExtra(String name, Bundle value) {
+ extras.put(name, value);
+ return this;
+ }
+
+ @Override
+ public boolean getBooleanExtra(String name, boolean defaultValue) {
+ if (extras.containsKey(name)) {
+ return (boolean) extras.get(name);
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public byte getByteExtra(String name, byte defaultValue) {
+ if (extras.containsKey(name)) {
+ return (byte) extras.get(name);
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public short getShortExtra(String name, short defaultValue) {
+ if (extras.containsKey(name)) {
+ return (short) extras.get(name);
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public char getCharExtra(String name, char defaultValue) {
+ if (extras.containsKey(name)) {
+ return (char) extras.get(name);
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public int getIntExtra(String name, int defaultValue) {
+ if (extras.containsKey(name)) {
+ return (int) extras.get(name);
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public long getLongExtra(String name, long defaultValue) {
+ if (extras.containsKey(name)) {
+ return (long) extras.get(name);
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public float getFloatExtra(String name, float defaultValue) {
+ if (extras.containsKey(name)) {
+ return (float) extras.get(name);
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public double getDoubleExtra(String name, double defaultValue) {
+ if (extras.containsKey(name)) {
+ return (double) extras.get(name);
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public CharSequence getCharSequenceExtra(String name) {
+ return (CharSequence) extras.get(name);
+ }
+
+ @Override
+ public T getParcelableExtra(String name) {
+ return (T) extras.get(name);
+ }
+
+ @Override
+ public Parcelable[] getParcelableArrayExtra(String name) {
+ return (Parcelable[]) extras.get(name);
+ }
+
+ @Override
+ public ArrayList getParcelableArrayListExtra(String name) {
+ return (ArrayList) extras.get(name);
+ }
+
+ @Override
+ public Serializable getSerializableExtra(String name) {
+ return (Serializable) extras.get(name);
+ }
+
+ @Override
+ public ArrayList getIntegerArrayListExtra(String name) {
+ return (ArrayList) extras.get(name);
+ }
+
+ @Override
+ public ArrayList getStringArrayListExtra(String name) {
+ return (ArrayList) extras.get(name);
+ }
+
+ @Override
+ public ArrayList getCharSequenceArrayListExtra(String name) {
+ return (ArrayList) extras.get(name);
+ }
+
+ @Override
+ public boolean[] getBooleanArrayExtra(String name) {
+ return (boolean[]) extras.get(name);
+ }
+
+ @Override
+ public byte[] getByteArrayExtra(String name) {
+ return (byte[]) extras.get(name);
+ }
+
+ @Override
+ public short[] getShortArrayExtra(String name) {
+ return (short[]) extras.get(name);
+ }
+
+ @Override
+ public char[] getCharArrayExtra(String name) {
+ return (char[]) extras.get(name);
+ }
+
+ @Override
+ public int[] getIntArrayExtra(String name) {
+ return (int[]) extras.get(name);
+ }
+
+ @Override
+ public long[] getLongArrayExtra(String name) {
+ return (long[]) extras.get(name);
+ }
+
+ @Override
+ public float[] getFloatArrayExtra(String name) {
+ return (float[]) extras.get(name);
+ }
+
+ @Override
+ public double[] getDoubleArrayExtra(String name) {
+ return (double[]) extras.get(name);
+ }
+
+ @Override
+ public String[] getStringArrayExtra(String name) {
+ return (String[]) extras.get(name);
+ }
+
+ @Override
+ public CharSequence[] getCharSequenceArrayExtra(String name) {
+ return (CharSequence[]) extras.get(name);
+ }
+
+ @Override
+ public String getStringExtra(String name) {
+ return (String) extras.get(name);
+ }
+
+ @Override
+ public String toString() {
+ return "GBMockIntent: " + mAction;
+ }
+}
diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockPackageManager.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockPackageManager.java
new file mode 100644
index 00000000..7799532d
--- /dev/null
+++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockPackageManager.java
@@ -0,0 +1,11 @@
+package nodomain.freeyourgadget.gadgetbridge.test;
+
+import android.content.ComponentName;
+import android.test.mock.MockPackageManager;
+
+public class GBMockPackageManager extends MockPackageManager {
+ @Override
+ public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) {
+ // do nothing
+ }
+}
diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/MockHelper.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/MockHelper.java
new file mode 100644
index 00000000..9461cab1
--- /dev/null
+++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/MockHelper.java
@@ -0,0 +1,35 @@
+package nodomain.freeyourgadget.gadgetbridge.test;
+
+import android.app.Application;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+
+import junit.framework.Assert;
+
+import org.mockito.Mockito;
+
+import java.lang.reflect.Constructor;
+
+public class MockHelper {
+ public NotificationManager createNotificationManager(Context mContext) throws Exception {
+ Constructor>[] constructors = NotificationManager.class.getDeclaredConstructors();
+ constructors[0].setAccessible(true);
+ Class>[] parameterTypes = constructors[0].getParameterTypes();
+ return (NotificationManager) constructors[0].newInstance();
+ }
+
+ public T createService(Class serviceClass, Application application) throws Exception {
+ Constructor constructor = serviceClass.getConstructor();
+ Assert.assertNotNull(constructor);
+ T realService = constructor.newInstance();
+ T mockedService = Mockito.spy(realService);
+ Mockito.when(mockedService.getApplicationContext()).thenReturn(application);
+ Mockito.when(mockedService.getPackageManager()).thenReturn(application.getPackageManager());
+ return mockedService;
+ }
+
+ public void addSystemServiceTo(Context context, String serviceName, Object service) {
+ Mockito.when(context.getSystemService(serviceName)).thenReturn(service);
+ }
+}