Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

Add Metrics #27

Merged
merged 3 commits into from
Feb 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,18 @@ subprojects {

dependencyManagement {
imports {
mavenBom 'org.jboss.aerogear:aerogear-android-sdk-bom:1.1.9'
mavenBom 'org.jboss.aerogear:aerogear-android-sdk-bom:1.1.10'
}
}
}

// We want to expose the SDK version and name to the metrics subproject
project(':core') {
project.ext {
versionName = VERSION_NAME
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}
5 changes: 2 additions & 3 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ android {
minSdkVersion project.ext.minSdkVersion
targetSdkVersion project.ext.targetSdkVersion

versionCode 1
versionName "1.0"
// Required for metrics, exposed by the parent project
versionName project.ext.versionName

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -43,7 +43,6 @@ tasks.withType(JavaCompile) {
options.compilerArgs = ["-Xlint:unchecked"]
}


dependencies {
implementation "com.android.support:appcompat-v7"
implementation "com.squareup.okhttp3:okhttp"
Expand Down
69 changes: 57 additions & 12 deletions core/src/main/java/org/aerogear/mobile/core/MobileCore.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.aerogear.mobile.core;

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

import org.aerogear.android.core.BuildConfig;
import org.aerogear.mobile.core.configuration.MobileCoreJsonParser;
import org.aerogear.mobile.core.configuration.ServiceConfiguration;
import org.aerogear.mobile.core.exception.ConfigurationNotFoundException;
Expand All @@ -23,9 +25,11 @@
*/
public final class MobileCore {

private static Logger logger = new LoggerAdapter();

private final String appVersion;
private final String configFileName;
private final HttpServiceModule httpLayer;
private final Logger logger;
private final Map<String, ServiceConfiguration> servicesConfig;
private final Map<Class<? extends ServiceModule>, ServiceModule> services = new HashMap<>();

Expand All @@ -36,7 +40,11 @@ public final class MobileCore {
*/
private MobileCore(Context context, Options options) throws InitializationException {
this.configFileName = options.configFileName;
this.logger = options.logger;

// -- Allow to override the default logger
if (options.logger != null) {
this.logger = options.logger;
}

// -- Parse JSON config file
try (InputStream configStream = context.getAssets().open(configFileName)) {
Expand All @@ -46,8 +54,10 @@ private MobileCore(Context context, Options options) throws InitializationExcept
throw new InitializationException(message, exception);
}

// -- Setting default http layer
// -- Set the app version variable
this.appVersion = getAppVersion(context);

// -- Setting default http layer
if (options.httpServiceModule == null) {
OkHttpServiceModule httpServiceModule = new OkHttpServiceModule();

Expand Down Expand Up @@ -95,16 +105,18 @@ public void destroy() {
}
}

public ServiceModule getInstance(Class<? extends ServiceModule> serviceClass) {
return getInstance(serviceClass, null);
@SuppressWarnings("unchecked")
public <T extends ServiceModule> T getInstance(Class<T> serviceClass) {
return (T) getInstance(serviceClass, null);
}

public ServiceModule getInstance(Class<? extends ServiceModule> serviceClass,
ServiceConfiguration serviceConfiguration)
@SuppressWarnings("unchecked")
public <T extends ServiceModule> T getInstance(Class<T> serviceClass,
ServiceConfiguration serviceConfiguration)
throws InitializationException {

if (services.containsKey(serviceClass)) {
return services.get(serviceClass);
return (T) services.get(serviceClass);
}

try {
Expand All @@ -118,7 +130,7 @@ public ServiceModule getInstance(Class<? extends ServiceModule> serviceClass,

services.put(serviceClass, serviceModule);

return serviceModule;
return (T) serviceModule;

} catch (IllegalAccessException | InstantiationException e) {
throw new InitializationException(e.getMessage(), e);
Expand All @@ -140,15 +152,50 @@ private ServiceConfiguration getServiceConfiguration(String type) {
return serviceConfiguration;
}

/**
* Get the user app version from the package manager
*
* @param context Android application context
* @return String app version name
*/
private String getAppVersion(final Context context) throws InitializationException {
try {
return context
.getPackageManager()
.getPackageInfo(context.getPackageName(), 0)
.versionName;
} catch (PackageManager.NameNotFoundException e) {
// Wrap in Initialization exception
throw new InitializationException("Failed to read app version", e);
}
}

public HttpServiceModule getHttpLayer() {
return this.httpLayer;
}

public Logger getLogger() {
public static Logger getLogger() {
return logger;
}

/**
* Get the version name of the SDK itself
*
* @return String SDK version
*/
public static String getSdkVersion() {
return BuildConfig.VERSION_NAME;
}

/**
* Get the version of the user app
*
* @return String App version name
*/
public String getAppVersion() {
return appVersion;
}

public static final class Options {

private String configFileName = "mobile-services.json";
Expand Down Expand Up @@ -178,7 +225,5 @@ public Options setLogger(Logger logger) {
this.logger = logger;
return this;
}

}

}
2 changes: 1 addition & 1 deletion gradle-mvn-push.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ afterEvaluate { project ->
source = variant.javaCompile.source
ext.androidJar = project.files(android.getBootClasspath().join(File.pathSeparator))
classpath = files(variant.javaCompile.classpath.files) + files(ext.androidJar)
exclude '**/BuildConfig.java'
exclude '**/BuildConfig'
Copy link
Contributor Author

@pb82 pb82 Feb 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason those exclusions don't work with .java and gradle gives out warnings. That's why i've changed it, it seems to work as expected this way.

exclude '**/R.java'
}

Expand Down
1 change: 1 addition & 0 deletions metrics/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
37 changes: 37 additions & 0 deletions metrics/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
apply plugin: 'com.android.library'

android {
compileSdkVersion project.ext.targetSdkVersion

defaultConfig {
minSdkVersion project.ext.minSdkVersion
targetSdkVersion project.ext.targetSdkVersion
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
}
}

testOptions {
unitTests.returnDefaultValues = true
unitTests.includeAndroidResources = true
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {
implementation project(path: ':core')

testImplementation 'junit:junit'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.json:json'
}

apply from: '../gradle-mvn-push.gradle'
3 changes: 3 additions & 0 deletions metrics/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_NAME=AeroGear Android SDK
POM_ARTIFACT_ID=metrics
POM_PACKAGING=aar
2 changes: 2 additions & 0 deletions metrics/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.aerogear.mobile.metrics" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package org.aerogear.mobile.metrics;

import android.content.Context;
import android.content.SharedPreferences;

import org.aerogear.mobile.core.MobileCore;
import org.aerogear.mobile.core.ServiceModule;
import org.aerogear.mobile.core.configuration.ServiceConfiguration;
import org.aerogear.mobile.core.http.HttpRequest;
import org.aerogear.mobile.core.http.HttpResponse;
import org.aerogear.mobile.core.http.HttpServiceModule;
import org.aerogear.mobile.core.logging.Logger;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.UUID;

public class MetricsService implements ServiceModule {
public final static String STORAGE_NAME = "org.aerogear.mobile.metrics";
public final static String STORAGE_KEY = "metrics-sdk-installation-id";

private final static String MODULE_NAME = "metrics";
private final static String TAG = "AEROGEAR/METRICS";

private HttpServiceModule httpService;
private Logger logger;

private String appVersion = null;
private String metricsUrl = null;

/**
* Get or create the client ID that identifies a device as long as the user doesn't
* reinstall the app or delete the app storage. A random UUID is created and stored in the
* application shared preferences.
*
* Can be overridden to provide a different implementation for identification.
*
* @param context Android app context
* @return String Client ID
*/
protected String getOrCreateClientId(final Context context) {
final SharedPreferences preferences = context
.getSharedPreferences(STORAGE_NAME, Context.MODE_PRIVATE);

String clientId = preferences.getString(STORAGE_KEY, null);
if (clientId == null) {
clientId = UUID.randomUUID().toString();

logger.info(TAG, "Generated a new client ID: " + clientId);

SharedPreferences.Editor editor = preferences.edit();
editor.putString(STORAGE_KEY, clientId);
editor.commit();
}

return clientId;
}

/**
* This method is called to create the JSON object containing the metrics data.
* Can be overridden to add more data points.
*
* @param context Android app context
* @return JSONObject Metrics data
* @throws JSONException when any of the data results in invalid JSON
*/
protected JSONObject metricsData(final Context context) throws JSONException {
final JSONObject result = new JSONObject();
result.put("clientId", getOrCreateClientId(context));
result.put("appId", context.getPackageName());
result.put("appVersion", appVersion);
result.put("sdkVersion", MobileCore.getSdkVersion());
return result;
}

/**
* Send the metrics data to the server. The data is contained in a JSON object with the
* following properties: clientId, appId, sdkVersion and appVersion
*
* Should not be overridden. Users can change the target URL in mobile-services.json
*
* @param context Android application context
*/
public final void init(final Context context) {
try {
final JSONObject data = metricsData(context);

// Send request to backend
HttpRequest request = httpService.newRequest();
request.post(metricsUrl, data.toString().getBytes());
HttpResponse response = request.execute();

// Async response handling
response.onComplete(() -> {
if (response.getStatus() != 200) {
logger.error(TAG, "Error sending metrics data");
}
});
} catch (JSONException e) {
logger.error(TAG, e);
}
}

@Override
public void configure(final MobileCore core, final ServiceConfiguration serviceConfiguration) {
metricsUrl = serviceConfiguration.getUri();
appVersion = core.getAppVersion();
httpService = core.getHttpLayer();
logger = MobileCore.getLogger();
}

@Override
public String type() {
return MODULE_NAME;
}

@Override
public void destroy() {
}
}