add user profile reporting

This commit is contained in:
hssrrw 2018-09-12 16:56:09 +02:00
parent 4c876831ed
commit 58d0daabc5
4 changed files with 259 additions and 3 deletions

View File

@ -31,7 +31,7 @@ AppMetrica.reportEvent('Hello world');
```js
import AppMetrica from 'react-native-appmetrica';
// Starts the statistics collection process.
// Start the statistics collection process.
AppMetrica.activateWithApiKey('...KEY...');
// OR
AppMetrica.activateWithConfig({
@ -40,10 +40,40 @@ AppMetrica.activateWithConfig({
firstActivationAsUpdate: true,
});
// Sends a custom event message and additional parameters (optional).
// Send a custom event message and additional parameters (optional).
AppMetrica.reportEvent('My event');
AppMetrica.reportEvent('My event', { foo: 'bar' });
// Send a custom error event.
AppMetrica.reportError('My error');
// Send user profile with predefined attributes.
AppMetrica.reportUserProfile({ name: 'User 1', age: 87 });
// Send user profile with custom attributes.
AppMetrica.reportUserProfile({
likesMusic: true,
addedToFavorites: '+1',
score: 150,
});
```
### Reporting user profile
All predefined attributes are supported. Use `null` to reset them.
```js
type UserProfileAttributes = {
name?: ?string,
gender?: 'female' | 'male' | string | void,
age?: ?number,
birthDate?: Date | [number] | [number, number] | [number, number, number] | void,
notificationsEnabled?: boolean,
/** custom attributes */
[string]: string | number | boolean,
};
```
Custom attributes are supported. They can't be reset for now.
Use values like `'+1'`, `'-10'` for counters. Current limitation is any custom attribute which value started with `'+'` or `'-'` will be considered as a counter.

View File

@ -8,15 +8,23 @@ import android.util.Log;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import java.lang.Exception;
import java.util.Calendar;
import java.util.Date;
import org.json.JSONObject;
import com.yandex.metrica.YandexMetrica;
import com.yandex.metrica.YandexMetricaConfig;
import com.yandex.metrica.profile.UserProfile;
import com.yandex.metrica.profile.Attribute;
import com.yandex.metrica.profile.GenderAttribute;
import static com.facebook.react.bridge.ReadableType.Array;
public class AppMetricaModule extends ReactContextBaseJavaModule {
final static String ModuleName = "AppMetrica";
@ -82,8 +90,125 @@ public class AppMetricaModule extends ReactContextBaseJavaModule {
YandexMetrica.setUserProfileID(profileID);
}
@ReactMethod
public void reportUserProfile(ReadableMap params) {
UserProfile.Builder userProfileBuilder = UserProfile.newBuilder();
ReadableMapKeySetIterator iterator = params.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
switch (key) {
// predefined attributes
case "name":
userProfileBuilder.apply(
params.isNull(key)
? Attribute.name().withValueReset()
: Attribute.name().withValue(params.getString(key))
);
break;
case "gender":
userProfileBuilder.apply(
params.isNull(key)
? Attribute.gender().withValueReset()
: Attribute.gender().withValue(
params.getString(key).equals("female")
? GenderAttribute.Gender.FEMALE
: params.getString(key).equals("male")
? GenderAttribute.Gender.MALE
: GenderAttribute.Gender.OTHER
)
);
break;
case "age":
userProfileBuilder.apply(
params.isNull(key)
? Attribute.birthDate().withValueReset()
: Attribute.birthDate().withAge(params.getInt(key))
);
break;
case "birthDate":
if (params.isNull(key)) {
userProfileBuilder.apply(
Attribute.birthDate().withValueReset()
);
} else if (params.getType(key) == Array) {
// an array of [ year[, month][, day] ]
ReadableArray date = params.getArray(key);
if (date.size() == 1) {
userProfileBuilder.apply(
Attribute.birthDate().withBirthDate(
date.getInt(0)
)
);
} else if (date.size() == 2) {
userProfileBuilder.apply(
Attribute.birthDate().withBirthDate(
date.getInt(0),
date.getInt(1)
)
);
} else {
userProfileBuilder.apply(
Attribute.birthDate().withBirthDate(
date.getInt(0),
date.getInt(1),
date.getInt(2)
)
);
}
} else {
// number of milliseconds since Unix epoch
Date date = new Date((long)params.getInt(key));
Calendar cal = Calendar.getInstance();
cal.setTime(date);
userProfileBuilder.apply(
Attribute.birthDate().withBirthDate(cal)
);
}
break;
case "notificationsEnabled":
userProfileBuilder.apply(
params.isNull(key)
? Attribute.notificationsEnabled().withValueReset()
: Attribute.notificationsEnabled().withValue(params.getBoolean(key))
);
break;
// custom attributes
default:
// TODO: come up with a syntax solution to reset custom attributes. `null` will break type checking here
switch (params.getType(key)) {
case Boolean:
userProfileBuilder.apply(
Attribute.customBoolean(key).withValue(params.getBoolean(key))
);
break;
case Number:
userProfileBuilder.apply(
Attribute.customNumber(key).withValue(params.getDouble(key))
);
break;
case String:
String value = params.getString(key);
if (value.startsWith("+") || value.startsWith("-")) {
userProfileBuilder.apply(
Attribute.customCounter(key).withDelta(Double.parseDouble(value))
);
} else {
userProfileBuilder.apply(
Attribute.customString(key).withValue(value)
);
}
break;
}
}
}
YandexMetrica.reportUserProfile(userProfileBuilder.build());
}
private String convertReadableMapToJson(final ReadableMap readableMap) {
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
JSONObject json = new JSONObject();
try {

View File

@ -9,6 +9,15 @@ type ActivationConfig = {
firstActivationAsUpdate?: boolean,
};
type UserProfileAttributes = {
name?: ?string,
gender?: 'female' | 'male' | string | void,
age?: ?number,
birthDate?: Date | [number] | [number, number] | [number, number, number] | void,
notificationsEnabled?: boolean,
[string]: string | number | boolean,
};
export default {
/**
@ -52,4 +61,24 @@ export default {
setUserProfileID(userProfileId: string) {
AppMetrica.setUserProfileID(userProfileId);
},
/**
* Sets attributes of the user profile.
* @param {object} attributes
*/
reportUserProfile(attributes: UserProfileAttributes) {
const readyAttributes = {};
Object.keys(attributes).forEach(key => {
if (
key === 'birthDate' &&
typeof attributes.birthDate === 'object' &&
typeof attributes.birthDate.getTime === 'function'
) {
readyAttributes.birthDate = attributes.birthDate.getTime();
} else {
readyAttributes[key] = attributes[key];
}
});
AppMetrica.reportUserProfile(readyAttributes);
},
};

View File

@ -42,4 +42,76 @@ RCT_EXPORT_METHOD(reportError:(NSString *)message) {
RCT_EXPORT_METHOD(setUserProfileID:(NSString *)userProfileID) {
[YMMYandexMetrica setUserProfileID:userProfileID];
}
RCT_EXPORT_METHOD(reportUserProfile:(NSDictionary *)attributes) {
YMMMutableUserProfile *profile = [[YMMMutableUserProfile alloc] init];
NSMutableArray *attrsArray = [NSMutableArray array];
for (NSString* key in attributes) {
// predefined attributes
if ([key isEqual: @"name"]) {
if (attributes[key] == [NSNull null]) {
[attrsArray addObject:[[YMMProfileAttribute name] withValueReset]];
} else {
[attrsArray addObject:[[YMMProfileAttribute name] withValue:[attributes[key] stringValue]]];
}
} else if ([key isEqual: @"gender"]) {
if (attributes[key] == [NSNull null]) {
[attrsArray addObject:[[YMMProfileAttribute gender] withValueReset]];
} else {
[attrsArray addObject:[[YMMProfileAttribute gender] withValue:[[attributes[key] stringValue] isEqual: @"female"] ? YMMGenderTypeFemale : [[attributes[key] stringValue] isEqual: @"male"] ? YMMGenderTypeMale : YMMGenderTypeOther]];
}
} else if ([key isEqual: @"age"]) {
if (attributes[key] == [NSNull null]) {
[attrsArray addObject:[[YMMProfileAttribute birthDate] withValueReset]];
} else {
[attrsArray addObject:[[YMMProfileAttribute birthDate] withAge:[attributes[key] intValue]]];
}
} else if ([key isEqual: @"birthDate"]) {
if (attributes[key] == [NSNull null]) {
[attrsArray addObject:[[YMMProfileAttribute birthDate] withValueReset]];
} else if ([attributes[key] isKindOfClass:[NSArray class]]) {
NSArray *date = [attributes[key] array];
if ([date count] == 1) {
[attrsArray addObject:[[YMMProfileAttribute birthDate] withYear:[[date objectAtIndex:0] intValue]]];
} else if ([[attributes[key] array] count] == 2) {
[attrsArray addObject:[[YMMProfileAttribute birthDate] withYear:[[date objectAtIndex:0] intValue] month:[[date objectAtIndex:1] intValue]]];
} else if ([[attributes[key] array] count] == 3) {
[attrsArray addObject:[[YMMProfileAttribute birthDate] withYear:[[date objectAtIndex:0] intValue] month:[[date objectAtIndex:1] intValue] day:[[date objectAtIndex:2] intValue]]];
}
} else {
// number of milliseconds since Unix epoch
NSDate *date = [attributes[key] date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *dateComponents =
[gregorian components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:date];
[attrsArray addObject:[[YMMProfileAttribute birthDate] withDateComponents:dateComponents]];
}
} else if ([key isEqual: @"notificationsEnabled"]) {
if (attributes[key] == [NSNull null]) {
[attrsArray addObject:[[YMMProfileAttribute notificationsEnabled] withValueReset]];
} else {
[attrsArray addObject:[[YMMProfileAttribute notificationsEnabled] withValue:[attributes[key] boolValue]]];
}
// custom attributes
} else {
// TODO: come up with a syntax solution to reset custom attributes. `null` will break type checking here
if ([attributes[key] isEqual: @YES] || [attributes[key] isEqual: @NO]) {
[attrsArray addObject:[[YMMProfileAttribute customBool:key] withValue:[attributes[key] boolValue]]];
} else if ([attributes[key] isKindOfClass:[NSNumber class]]) {
[attrsArray addObject:[[YMMProfileAttribute customNumber:key] withValue:[attributes[key] doubleValue]]];
// [NSNumber numberWithInt:[attributes[key] intValue]]
} else if ([attributes[key] isKindOfClass:[NSString class]]) {
if ([attributes[key] hasPrefix:@"+"] || [attributes[key] hasPrefix:@"-"]) {
[attrsArray addObject:[[YMMProfileAttribute customCounter:key] withDelta:[attributes[key] doubleValue]]];
} else {
[attrsArray addObject:[[YMMProfileAttribute customString:key] withValue:attributes[key]]];
}
}
}
}
[profile applyFromArray: attrsArray];
[YMMYandexMetrica reportUserProfile:[profile copy] onFailure:NULL];
}
@end