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

Collect and send custom attributes for capture feature #89

Merged
merged 34 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
80d1bc1
chore(sdk): add constant values for collect custom attributes
satsukies Jul 22, 2024
6be4756
feat(sdk): add interface for set/remove custom attributes from user
satsukies Jul 22, 2024
58aa865
feat(sdk): manage custom attributes with HashMap and implement valida…
satsukies Jul 22, 2024
fd54751
chore(sdk): bumpup version to 4.8.0
satsukies Jul 22, 2024
e4a3d40
fix(sdk): add import
satsukies Jul 22, 2024
ce47071
fix(sdk): fix test failed
satsukies Jul 22, 2024
b94250d
test(sdk): add test for CustomAttributes
satsukies Jul 22, 2024
d8bcc73
fix(sdk): add CustomAttributes for capture feature
satsukies Jul 22, 2024
c5c8f3c
test(sdk): add CustomAttributesTest#toJsonString()
satsukies Jul 22, 2024
9be4d92
feat(sdk): write custom attributes when collect device state event re…
satsukies Jul 22, 2024
480de52
chore(sample): update sample code set custom attributes
satsukies Jul 22, 2024
6b56334
fix(sdk): fix build failed due to missing some methods in sdkMock
satsukies Jul 22, 2024
11d51ab
fix(sdk): don't send values when it is null
satsukies Jul 22, 2024
2b8ecee
fix(sdk): don't send package name from SDK
satsukies Jul 22, 2024
95dad51
fix(sdk): provide getter only
satsukies Jul 22, 2024
4889e37
fix(sdk): fix too general naming of some constant values
satsukies Jul 22, 2024
823463e
fix(sdk): class became final and hide constructor from outside
satsukies Jul 22, 2024
71e158f
fix(sdk): simplify redundant condition
satsukies Jul 22, 2024
9bcb4f1
fix(sdk): improve put operation for thread safety
satsukies Jul 22, 2024
9c16ebf
fix(sdk): use Logger instead of Log
satsukies Jul 22, 2024
f1a9aff
test(sdk): add CustomAttributesInterfaceTest
satsukies Jul 22, 2024
854288a
chore(sample): improvement sample codes
satsukies Jul 22, 2024
c829658
test(sdk): fix symbolic link
satsukies Jul 23, 2024
1c5407d
test(sdk): fix testcase method naming
satsukies Jul 23, 2024
45ac031
fix(sdk): all public method always return non-null value
satsukies Jul 23, 2024
45084e1
test(sdk): add testcase checking behavior of isEmpty() and size()
satsukies Jul 23, 2024
4519aa2
fix(sdk): use try-catch insert value via content resolver
satsukies Jul 23, 2024
024b6cc
fix(sdk): append prefix to keys when call toJsonString()
satsukies Jul 23, 2024
1627bff
fix(sdk): use synchronized when access CustomAttributes
satsukies Jul 23, 2024
6f0ebd8
test(sdk): use different values, check return type
satsukies Jul 24, 2024
7a510d6
fix(sdk): fix CustomAttributes class become thread-safe
satsukies Jul 24, 2024
7391f7f
fix(sdk): send attributes collect by SDK
satsukies Jul 24, 2024
110a00e
chore(sdk): documentation
satsukies Jul 24, 2024
063fc76
test(sdk): add testcase of toJSONString()
satsukies Jul 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ abstract class BaseSdkPlugin : Plugin<Project> {
/**
* sdk/java/com/deploygate/sdk/HostAppTest.java needs to be changed for a new release
*/
const val ARTIFACT_VERSION = "4.7.1"
const val ARTIFACT_VERSION = "4.8.0"

val JAVA_VERSION = JavaVersion.VERSION_1_7
}
Expand Down
7 changes: 7 additions & 0 deletions sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
<!-- only for sample app -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<!-- collecting stats data from app&device -->
<uses-permission android:name="com.deploygate.permission.ACCESS_SDK" />

<!--
If you have multiple processes in your application, or you want to customize SDK initializer,
you need to add your own Application class here. In this example, the class is
Expand Down Expand Up @@ -67,4 +70,8 @@
<!-- SDK will be initialized through ContentProvider by default -->
<!-- Please refer to stablereal/AndroidManifest.xml -->
</application>

<queries>
<provider android:authorities="com.deploygate.external.sdk" />
</queries>
</manifest>
16 changes: 16 additions & 0 deletions sample/src/main/java/com/deploygate/sample/App.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.deploygate.sample;

import android.app.Application;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;

import com.deploygate.sdk.CustomAttributes;
import com.deploygate.sdk.DeployGate;
import com.deploygate.sdk.DeployGateCallback;

Expand Down Expand Up @@ -34,6 +36,10 @@ public void onInitialized(boolean isServiceAvailable) {
if (isServiceAvailable) {
Log.i(TAG, "SDK is available");
DeployGate.logInfo("SDK is available");

CustomAttributes attrs = DeployGate.getBuildEnvironment();
attrs.putString("build_type", BuildConfig.BUILD_TYPE);
attrs.putString("flavor", BuildConfig.FLAVOR);
} else {
Log.i(TAG, "SDK is unavailable");
DeployGate.logInfo("SDK is unavailable"); // this fails silently
Expand Down Expand Up @@ -84,4 +90,14 @@ public void onUpdateAvailable(
//
// You can use DeployGate.isAuthorized() later to check the installation is valid or not.
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);

CustomAttributes attrs = DeployGate.getRuntimeExtra();
attrs.putString("locale", newConfig.locale.toString());
attrs.putInt("orientation", newConfig.orientation);
attrs.putFloat("font_scale", newConfig.fontScale);
}
}
10 changes: 10 additions & 0 deletions sample/src/main/java/com/deploygate/sample/SampleActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import android.widget.TextView;
import android.widget.Toast;

import com.deploygate.sdk.CustomAttributes;
import com.deploygate.sdk.DeployGate;
import com.deploygate.sdk.DeployGateCallback;

Expand Down Expand Up @@ -53,6 +54,15 @@ public void onCreate(Bundle savedInstanceState) {
mUpdateButton = (Button) findViewById(R.id.updateButton);
mLogMessage = (EditText) findViewById(R.id.message);
mDistributionComments = (LinearLayout) findViewById(R.id.distributionComments);


CustomAttributes attrs = DeployGate.getRuntimeExtra();
attrs.putString("string", "value");
attrs.putInt("int", 123);
attrs.putBoolean("boolean", true);
attrs.putFloat("float", 1.23f);
attrs.putDouble("double", 1.23);
attrs.putLong("long", 123L);
}

@Override
Expand Down
193 changes: 193 additions & 0 deletions sdk/src/main/java/com/deploygate/sdk/CustomAttributes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package com.deploygate.sdk;

import com.deploygate.sdk.internal.Logger;

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

import java.util.regex.Pattern;

/**
* This class provides store key-value pairs.
* These methods are thread-safe.
*/
public final class CustomAttributes {

private static final String TAG = "CustomAttributes";

private static final int MAX_ATTRIBUTES_SIZE = 8;
private static final Pattern VALID_KEY_PATTERN = Pattern.compile("^[a-z][_a-z0-9]{2,31}$");
private static final int MAX_VALUE_LENGTH = 64;

private final Object mLock;
private JSONObject attributes;

CustomAttributes() {
mLock = new Object();
attributes = new JSONObject();
}

/**
* Put a string value with the key.
* If the key already exists, the value will be overwritten.
* @param key key must be non-null and match the valid pattern.
* @param value value must be non-null and its length must be less than 64.
* @return true if the value is put successfully, otherwise false.
* @see CustomAttributes#VALID_KEY_PATTERN
*/
public boolean putString(String key, String value) {
return putInternal(key, value);
}

/**
* Put an int value with the key.
* If the key already exists, the value will be overwritten.
* @param key key must be non-null and match the valid pattern.
* @param value int value
* @return true if the value is put successfully, otherwise false.
* @see CustomAttributes#VALID_KEY_PATTERN
*/
public boolean putInt(String key, int value) {
return putInternal(key, value);
}

/**
* Put a long value with the key.
* If the key already exists, the value will be overwritten.
* @param key key must be non-null and match the valid pattern.
* @param value long value
* @return true if the value is put successfully, otherwise false.
* @see CustomAttributes#VALID_KEY_PATTERN
*/
public boolean putLong(String key, long value) {
return putInternal(key, value);
}

/**
* Put a float value with the key.
* If the key already exists, the value will be overwritten.
* @param key key must be non-null and match the valid pattern.
* @param value float value
* @return true if the value is put successfully, otherwise false.
* @see CustomAttributes#VALID_KEY_PATTERN
*/
public boolean putFloat(String key, float value) {
return putInternal(key, value);
}

/**
* Put a double value with the key.
* If the key already exists, the value will be overwritten.
* @param key key must be non-null and match the valid pattern.
* @param value double value
* @return true if the value is put successfully, otherwise false.
* @see CustomAttributes#VALID_KEY_PATTERN
*/
public boolean putDouble(String key, double value) {
return putInternal(key, value);
}

/**
* Put a boolean value with the key.
* If the key already exists, the value will be overwritten.
* @param key key must be non-null and match the valid pattern.
* @param value boolean value
* @return true if the value is put successfully, otherwise false.
* @see CustomAttributes#VALID_KEY_PATTERN
*/
public boolean putBoolean(String key, boolean value) {
return putInternal(key, value);
}

/**
* Remove the value with the key.
* @param key name of the key to be removed.
*/
public void remove(String key) {
synchronized (mLock) {
attributes.remove(key);
}
}

/**
* Remove all key-value pairs.
*/
public void removeAll() {
synchronized (mLock) {
// recreate new object instead of removing all keys
attributes = new JSONObject();
}
}

int size() {
synchronized (mLock) {
return attributes.length();
}
}

String getJSONString() {
synchronized (mLock) {
return attributes.toString();
}
}

private boolean putInternal(String key, Object value) {
if (!isValidKey(key)) {
return false;
}

if (!isValidValue(value)) {
return false;
}

synchronized (mLock) {
try {
attributes.put(key, value);

if (attributes.length() > MAX_ATTRIBUTES_SIZE) {
// rollback put operation
attributes.remove(key);
Logger.w(TAG, "Attributes already reached max size. Ignored: " + key);
return false;
}
} catch (JSONException e) {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Logger.w(TAG, "Failed to put attribute: " + key, e);
return false;
}
}

return true;
}

private boolean isValidKey(String key) {
if (key == null || key.equals("true") || key.equals("false") || key.equals("null")) {
Logger.w(TAG, "Not allowed key: " + key);
return false;
}

if (!VALID_KEY_PATTERN.matcher(key).matches()) {
Logger.w(TAG, "Invalid key: " + key);
return false;
}

return true;
}

private boolean isValidValue(Object value) {
if (value == null) {
Logger.w(TAG, "Value is null");
return false;
}

if (value instanceof String && ((String) value).length() > MAX_VALUE_LENGTH) {
Logger.w(TAG, "Value too long: " + value);
return false;
} else if (value instanceof String || value instanceof Number || value instanceof Boolean) {
return true;
} else {
// dead code
Logger.w(TAG, "Invalid value: " + value);
return false;
}
}
}
Loading
Loading