Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't send null values #96

Merged
merged 9 commits into from
Jan 18, 2016
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ android:
- android-4
- android-10
- android-19
- android-23
- extra-android-m2repository

env:
matrix:
Expand Down
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ android {
}
}

dependencies {
compile "com.android.support:support-annotations:23.1.1"
}

// Task to generate a .jar file
task jarRelease(type: Copy) {
def jarFile = POM_ARTIFACT_ID + '-' + VERSION_NAME + '.jar'
Expand Down
4 changes: 2 additions & 2 deletions src/androidTest/java/com/bugsnag/android/AppDataTest.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.bugsnag.android;

import java.io.IOException;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;

public class AppDataTest extends BugsnagTestCase {
public void testManifestData() throws JSONException, IOException {
Configuration config = new Configuration("some-api-key");
Expand Down
11 changes: 8 additions & 3 deletions src/androidTest/java/com/bugsnag/android/DeviceDataTest.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
package com.bugsnag.android;

import java.io.IOException;
import android.util.DisplayMetrics;

import org.json.JSONException;
import org.json.JSONObject;

import android.util.DisplayMetrics;
import java.io.IOException;

public class DeviceDataTest extends BugsnagTestCase {
public void testSaneValues() throws JSONException, IOException {
Configuration config = new Configuration("some-api-key");
DeviceData deviceData = new DeviceData(getContext());
JSONObject deviceDataJson = streamableToJson(deviceData);

assertEquals("android", deviceDataJson.getString("osName"));
assertTrue(deviceDataJson.getString("manufacturer").length() > 1);
assertTrue(deviceDataJson.getString("brand").length() > 1);
assertTrue(deviceDataJson.getString("model").length() > 1);

assertTrue(deviceDataJson.getDouble("screenDensity") > 0);
assertTrue(deviceDataJson.getDouble("dpi") >= DisplayMetrics.DENSITY_LOW);
assertTrue(deviceDataJson.getString("screenResolution").matches("^\\d+x\\d+$"));
assertTrue(deviceDataJson.getLong("totalMemory") > 0);
assertNotNull(deviceDataJson.getBoolean("jailbroken"));
assertNotNull(deviceDataJson.getString("locale"));

if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.FROYO) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.FROYO) {
// Emulators returned null for android id before android 2.2
assertNotNull(deviceDataJson.getString("id"));
}
Expand Down
95 changes: 51 additions & 44 deletions src/main/java/com/bugsnag/android/AppData.java
Original file line number Diff line number Diff line change
@@ -1,68 +1,71 @@
package com.bugsnag.android;

import java.io.IOException;

import android.content.Context;
import android.content.pm.ApplicationInfo;;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import java.io.IOException;

/**
* Information about the running Android app which doesn't change over time,
* including app name, version and release stage.
*
* <p/>
* App information in this class is cached during construction for faster
* subsequent lookups and to reduce GC overhead.
*/
class AppData implements JsonStream.Streamable {
private Configuration config;
private Context appContext;
private final Configuration config;

private String packageName;
private String appName;
private Integer versionCode;
private String versionName;
private String guessedReleaseStage;
private final String packageName;
private final String appName;
private final Integer versionCode;
private final String versionName;
private final String guessedReleaseStage;

AppData(Context appContext, Configuration config) {
AppData(@NonNull Context appContext, @NonNull Configuration config) {
this.config = config;
this.appContext = appContext;

packageName = getPackageName();
appName = getAppName();
versionCode = getVersionCode();
versionName = getVersionName();
guessedReleaseStage = guessReleaseStage();
packageName = getPackageName(appContext);
appName = getAppName(appContext);
versionCode = getVersionCode(appContext);
versionName = getVersionName(appContext);
guessedReleaseStage = guessReleaseStage(appContext);
}

public void toStream(JsonStream writer) throws IOException {
public void toStream(@NonNull JsonStream writer) throws IOException {
writer.beginObject();
writer.name("id").value(packageName);
writer.name("name").value(appName);
writer.name("packageName").value(packageName);
writer.name("versionName").value(versionName);
writer.name("versionCode").value(versionCode);

if (config.buildUUID != null) {
writer.name("buildUUID").value(config.buildUUID);
}

// Prefer user-configured appVersion + releaseStage
writer.name("id").value(packageName);
writer.name("name").value(appName);
writer.name("packageName").value(packageName);
writer.name("versionName").value(versionName);
writer.name("versionCode").value(versionCode);

if (config.buildUUID != null)
writer.name("buildUUID").value(config.buildUUID);

// Prefer user-configured appVersion + releaseStage
if (getAppVersion() != null)
writer.name("version").value(getAppVersion());
writer.name("releaseStage").value(getReleaseStage());
writer.name("releaseStage").value(getReleaseStage());

writer.endObject();
}

@NonNull
public String getReleaseStage() {
if(config.releaseStage != null) {
if (config.releaseStage != null) {
return config.releaseStage;
} else {
return guessedReleaseStage;
}
}

@Nullable
public String getAppVersion() {
if(config.appVersion != null) {
if (config.appVersion != null) {
return config.appVersion;
} else {
return versionName;
Expand All @@ -72,34 +75,36 @@ public String getAppVersion() {
/**
* The package name of the running Android app, eg: com.example.myapp
*/
private String getPackageName() {
@NonNull
private static String getPackageName(Context appContext) {
return appContext.getPackageName();
}

/**
* The name of the running Android app, from android:label in
* AndroidManifest.xml
*/
private String getAppName() {
@Nullable
private static String getAppName(Context appContext) {
try {
PackageManager packageManager = appContext.getPackageManager();
ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName, 0);
ApplicationInfo appInfo = packageManager.getApplicationInfo(appContext.getPackageName(), 0);

return (String)packageManager.getApplicationLabel(appInfo);
return (String) packageManager.getApplicationLabel(appInfo);
} catch (PackageManager.NameNotFoundException e) {
Logger.warn("Could not get app name");
}

return null;
}

/**
* The version code of the running Android app, from android:versionCode
* in AndroidManifest.xml
*/
private Integer getVersionCode() {
@Nullable
private static Integer getVersionCode(Context appContext) {
try {
return appContext.getPackageManager().getPackageInfo(packageName, 0).versionCode;
return appContext.getPackageManager().getPackageInfo(appContext.getPackageName(), 0).versionCode;
} catch (PackageManager.NameNotFoundException e) {
Logger.warn("Could not get versionCode");
}
Expand All @@ -110,9 +115,10 @@ private Integer getVersionCode() {
* The version code of the running Android app, from android:versionName
* in AndroidManifest.xml
*/
private String getVersionName() {
@Nullable
private static String getVersionName(Context appContext) {
try {
return appContext.getPackageManager().getPackageInfo(packageName, 0).versionName;
return appContext.getPackageManager().getPackageInfo(appContext.getPackageName(), 0).versionName;
} catch (PackageManager.NameNotFoundException e) {
Logger.warn("Could not get versionName");
}
Expand All @@ -123,10 +129,11 @@ private String getVersionName() {
* Guess the release stage of the running Android app by checking the
* android:debuggable flag from AndroidManifest.xml
*/
private String guessReleaseStage() {
@NonNull
private static String guessReleaseStage(Context appContext) {
try {
int appFlags = appContext.getPackageManager().getApplicationInfo(packageName, 0).flags;
if((appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
int appFlags = appContext.getPackageManager().getApplicationInfo(appContext.getPackageName(), 0).flags;
if ((appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
return "development";
}
} catch (PackageManager.NameNotFoundException e) {
Expand Down
76 changes: 43 additions & 33 deletions src/main/java/com/bugsnag/android/AppState.java
Original file line number Diff line number Diff line change
@@ -1,54 +1,59 @@
package com.bugsnag.android;

import java.io.IOException;
import java.util.List;

import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import java.io.IOException;
import java.util.List;

/**
* Information about the running Android app which can change over time,
* including memory usage and active screen information.
*
* App information in this class is not cached, and is recalcuated every
* <p/>
* App information in this class is not cached, and is recalculated every
* time toStream is called.
*/
class AppState implements JsonStream.Streamable {
private static Long startTime = SystemClock.elapsedRealtime();
private Context appContext;

private Long duration;
private Boolean inForeground;
private String activeScreen;
private Long memoryUsage;
private Boolean lowMemory;
private static final Long startTime = SystemClock.elapsedRealtime();

static void init() {}
private final Long duration;
private final Boolean inForeground;
private final String activeScreen;
private final Long memoryUsage;
private final Boolean lowMemory;

AppState(Context appContext) {
this.appContext = appContext;
static void init() {
}

AppState(@NonNull Context appContext) {
duration = getDuration();
inForeground = isInForeground();
activeScreen = getActiveScreen();
inForeground = isInForeground(appContext);
activeScreen = getActiveScreen(appContext);
memoryUsage = getMemoryUsage();
lowMemory = isLowMemory();
lowMemory = isLowMemory(appContext);
}

public void toStream(JsonStream writer) throws IOException {
public void toStream(@NonNull JsonStream writer) throws IOException {
writer.beginObject();
writer.name("duration").value(duration);

writer.name("duration").value(duration);
if (inForeground != null)
writer.name("inForeground").value(inForeground);
if (activeScreen != null)
writer.name("activeScreen").value(activeScreen);
writer.name("memoryUsage").value(memoryUsage);
writer.name("memoryUsage").value(memoryUsage);
if (lowMemory != null)
writer.name("lowMemory").value(lowMemory);

writer.endObject();
}

public String getActiveScreenClass() {
if(activeScreen != null) {
@Nullable
public static String getActiveScreenClass(@Nullable String activeScreen) {
if (activeScreen != null) {
return activeScreen.substring(activeScreen.lastIndexOf('.') + 1);
} else {
return null;
Expand All @@ -59,16 +64,18 @@ public String getActiveScreenClass() {
* Get the actual memory used by the VM (which may not be the total used
* by the app in the case of NDK usage).
*/
private long getMemoryUsage() {
@NonNull
private static Long getMemoryUsage() {
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}

/**
* Check if the device is currently running low on memory.
*/
private Boolean isLowMemory() {
@Nullable
private static Boolean isLowMemory(Context appContext) {
try {
ActivityManager activityManager = (ActivityManager)appContext.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memInfo);

Expand All @@ -83,9 +90,10 @@ private Boolean isLowMemory() {
* Get the name of the top-most activity. Requires the GET_TASKS permission,
* which defaults to true in Android 5.0+.
*/
private String getActiveScreen() {
@Nullable
private static String getActiveScreen(Context appContext) {
try {
ActivityManager activityManager = (ActivityManager)appContext.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);
ActivityManager.RunningTaskInfo runningTask = tasks.get(0);
return runningTask.topActivity.getClassName();
Expand All @@ -99,9 +107,10 @@ private String getActiveScreen() {
* Get the name of the top-most activity. Requires the GET_TASKS permission,
* which defaults to true in Android 5.0+.
*/
private Boolean isInForeground() {
@Nullable
private static Boolean isInForeground(Context appContext) {
try {
ActivityManager activityManager = (ActivityManager)appContext.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);
if (tasks.isEmpty()) {
return false;
Expand All @@ -120,7 +129,8 @@ private Boolean isInForeground() {
* Get the time in milliseconds since Bugsnag was initialized, which is a
* good approximation for how long the app has been running.
*/
private Long getDuration() {
@NonNull
private static Long getDuration() {
return SystemClock.elapsedRealtime() - startTime;
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/bugsnag/android/BeforeNotify.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ public interface BeforeNotify {
* @param error the error to be sent to Bugsnag
* @see Error
*/
public boolean run(Error error);
boolean run(Error error);
}
Loading