I'm measuring my Android app checkout performance by using the google-analytics SDK. I created a Wrapper that I use in order to send hits (and it works) and exceptions (it works as well). I just can't make it work with eCommerce data.
In order to send ecommerce data i create a product and a productAction
Product product = new Product()
.setId(ID)
.setCategory(category)
.setBrand(brandID)
.setCustomDimension(1, typology)
.setCustomDimension(2, currency)
.setPrice(getTotal())
.setQuantity(1);
// Add the step number and additional info about the checkout to the action.
ProductAction productAction = new ProductAction(ProductAction.ACTION_PURCHASE)
.setCheckoutStep(4)
.setCheckoutOptions("Perform payment");
and then
sendEcommerceCheckoutStep(product, productAction, "performPayment", getApplicationContext())
the body of said method is
public void sendEcommerceCheckoutStep(Product product, ProductAction productAction, String checkoutStepName, Context context) {
HitBuilders.ScreenViewBuilder builder = new HitBuilders.ScreenViewBuilder()
.addProduct(product)
.setProductAction(productAction)
.addImpression(product, checkoutStepName);
mTracker.setScreenName(checkoutStepName);
mTracker.send(builder.build());
mTracker.setScreenName(null);
}
Now, I'd expect data to flow through analytics (and it does, I checked the adb logs) but I can't see it in analytics web interface.
This is what I see on analytics web interface:
As you can see the only column which got data is the "Cart-to-Detail Rate" one. But how can I have a cart-to-detail rate if I don't have any data in any other column?
This was the "product performance" screen. This is the "Product list performance":
all other columns are 0 as well. Why did it list the "add to cart" actions but not the others?
The following code is working on my app. I have followed the official transaction guide.
I found a few differences with yours. E.g. the name of the screen name, I don't set it to null later, I don't set the checkout step, I don't set custom dimensions nor impressions.
Feel free to try it:
public void trackPurchase(#NonNull TrackingPurchase trackingPurchase) {
HitBuilders.ScreenViewBuilder builder = new HitBuilders.ScreenViewBuilder();
for (TrackingProduct trackingProduct : trackingPurchase.getTrackingProducts()) {
builder.addProduct(this.createProduct(trackingProduct));
}
builder.setProductAction(this.createProductAction(trackingPurchase));
googleAnalyticsTracker.setScreenName("transaction");
googleAnalyticsTracker.set("&cu", "USD");
googleAnalyticsTracker.send(builder.build());
}
#NonNull
private Product createProduct(#NonNull TrackingProduct trackingProduct) {
return new Product()
.setId(trackingProduct.getSku())
.setName(trackingProduct.getName())
.setCategory(trackingProduct.getCategory())
.setPrice(trackingProduct.getPrice())
.setQuantity(trackingProduct.getQuantity());
}
#NonNull
private ProductAction createProductAction(#NonNull TrackingPurchase trackingPurchase) {
return new ProductAction(ProductAction.ACTION_PURCHASE)
.setTransactionId(trackingPurchase.getSaleId())
.setTransactionAffiliation("Android App")
.setTransactionRevenue(trackingPurchase.getRevenue())
.setTransactionTax(0)
.setTransactionShipping(trackingPurchase.getShippingCost())
.setTransactionCouponCode(trackingPurchase.getCouponCode());
}
TrackingPurchase is a class that just contains the various TrackingProduct which are data to be tracked.
I can see this tracked by checking here:
For example, you will see revenue and top sellers.
Related
I am implementing an Android app that is responsible for some data exchange with other services such as credentials. I then want to use that information to automatically fill in the input fields of other applications on the device such as Spotify.
Is there any way to fill the input fields of another app, like the username and password to remove the chore for the user to manually input it?
Also I noticed that at least on iOS, Spotify recognizes 1Password to be installed and displays a small icon next to the input fields with which I can fill the fields from the data stored in 1Password - how is this done as it seems to be another solution to my problem?
Thanks in advance
You might want to implement Autofill Service https://developer.android.com/guide/topics/text/autofill-services.html
There is a ready to use sample app which will get you started https://github.com/googlesamples/android-AutofillFramework
Android will invoke onFillRequest() method giving your service a chance to show autofill suggestions. Here is a sample code from above link:
#Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback) {
// Get the structure from the request
List<FillContext> context = request.getFillContexts();
AssistStructure structure = context.get(context.size() - 1).getStructure();
// Traverse the structure looking for nodes to fill out.
ParsedStructure parsedStructure = parseStructure(structure);
// Fetch user data that matches the fields.
UserData userData = fetchUserData(parsedStructure);
// Build the presentation of the datasets
RemoteViews usernamePresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
usernamePresentation.setTextViewText(android.R.id.text1, "my_username");
RemoteViews passwordPresentation = new RemoteViews(getPackageName(), android.R.layout.simple_list_item_1);
passwordPresentation.setTextViewText(android.R.id.text1, "Password for my_username");
// Add a dataset to the response
FillResponse fillResponse = new FillResponse.Builder()
.addDataset(new Dataset.Builder()
.setValue(parsedStructure.usernameId,
AutofillValue.forText(userData.username), usernamePresentation)
.setValue(parsedStructure.passwordId,
AutofillValue.forText(userData.password), passwordPresentation)
.build())
.build();
// If there are no errors, call onSuccess() and pass the response
callback.onSuccess(fillResponse);
}
class ParsedStructure {
AutofillId usernameId;
AutofillId passwordId;
}
class UserData {
String username;
String password;
}
I am developing an android application where older kids can pick up younger kids and walk to school. With the application the authenticated (email and password) younger kid can choose between three adresses to get picked up. As of right now my realtime database looks like this:
I want to retrieve the different addresses and the users who picked the addresses. I am thinking I have to use recyclerview to get the data, but I am unsure on if it is possible to do with my database structure.
Using the FirebaseUI database package makes it simple to bind data from the Firebase Realtime Database to your app's UI. Specifically using FirebaseUI with indexed data is applicable for your current database structure.
For example, you'd use something similar to:
// keyQuery - the Firebase location containing the list of keys to be found in dataRef
// dataRef - the Firebase location to watch for data changes. Each key found at
// keyRef's location represents a list item.
Query keyQuery = FirebaseDatabase.getInstance().getReference("/Addresses/Street 10/users");
DatabaseReference dataRef = FirebaseDatabase.getInstance().getReference("/User");
FirebaseRecyclerOptions<User> options = new FirebaseRecyclerOptions.Builder<User>()
.setIndexedQuery(keyQuery, dataRef, User.class)
.build();
Where your User class is:
public class User {
private String username;
public User() {}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
// ...
}
You can then use the above created options variable to create a FirebaseRecyclerAdapter instance and then call startListening() on it*.
Using FirebaseUI in this way will automatically handle matching the keys under /Addresses/Street 10/users to the /User node.
* Version 2.x of FirebaseUI uses FirebaseIndexRecyclerAdapter instead and starts listening automatically so doesn't require a startListening() call. The above example is applicable for version 3.0, see FirebaseUI 3.0 upgrade guide.
Following on from my previous answer, this one should accommodate your requirement to create a list of all addresses and their associated users, which may be closer to what you're looking for.
Again you can use the FirebaseUI database package to simplify the RecyclerView creation.
You'll need to start denormalizing your data, so your data structure should also include usernames in the addresses node:
{
"Addresses" : {
"Street 10" : {
"name" : "Street 10",
"users" : {
"VAzdMWafK6cyhmJnOI4br5xiQg93" : "John"
}
}
},
"User" : {
"VAzdMWafK6cyhmJnOI4br5xiQg93" : {
"username" : "John",
"address" : "Street 10"
}
}
}
Note: you only need to add user IDs to their chosen address (and remove the node if they change selection), so don't use "VAzdMWafK6cyhmJnOI4br5xiQg93" : false for addresses the user has not selected as this could cause confusion.
Then you can use:
Query query = FirebaseDatabase.getInstance().getReference("/Addresses");
FirebaseRecyclerOptions<Address> options = new FirebaseRecyclerOptions.Builder<Address>()
.setQuery(query, Address.class)
.build();
Where Address is something like:
public class Address {
private String name;
private Map<String, String> users;
public Address() {}
public Map<String, String> getUsers() {
return this.users;
}
// ...
}
And create a FirebaseRecyclerAdapter instance from the options variable. Then when binding the viewholder in the adapter, you can access the users map to list each user that has selected this address, without the need to load the entire User object unnecessarily.
This pattern is called denormalization and is the suggested approach when using NoSQL databases (like Firebase Realtime Database). The main downside to this is data duplication: so for example, when a user changes their selected address, you'll need to change:
The address value under the user, and
the users list under the address.
Likewise, if a user is allowed to change their username, you'll need to update the username under their chosen address as well as in the user's node.
For details on dealing with this, see this answer which explains a number of methods (although the examples are in JavaScript, the premise still applies).
I am new to Android but I am attempting to build an app that would allow a User to add other users to a Group .
I have made a User and Group class, and I am trying to add a user to this group . . I am hoping that maybe someone here can point me in the right direction.
At the moment I have a User object and I am attempting to add this to a List within my Group object. Is this how users would be managed for an android app with user groups?
I feel like there is something obvious I am missing!
I am using Firebase Realtime Database also and need to be able to store users added to a particular group so it is consistent on all users devices.
e.g. User 1 adds User 2 to Group A, both User1 and User 2 will see on their device that User 2 is in Group A
Code below:
Group Class
public class Group {
public List<User> memberList = new List<User>() {....};
public Group() {
}
public Group(List<User> users) {
this.memberList = users;
}
public void addMember(User member) {
this.memberList.add(member);
}
public List<User> getMemberList() {
return memberList;
}
}
Main Class
User user = new User(uId, name);
Group group = new Group();
group.addMember(user);
groupsDatabase.child("test_group").push().setValue(group.memberList.get(0));
Currently I'm using Parse.com in order to create multiple ParseUsers. This works perfectly and each user can login individually. However from here I want to expand my app to allow Users to create groups of users and therefore have data that is only relevant and shared between these Users. This will mean that when the User logs in, they can see a List of the groups they are members of and from there can share data simply just to those users of that individual group. What would be the best way to tackle this and does anybody have any examples or tutorials that I could follow in order to understand this concept?
I've considered creating a Group class and then making this store User's IDs in an array and then allow each User to store an array of the Group IDs that they're currently members of. I'm just not really sure how to broach this issue.
Thanks in advance!
I ended up doing as shown below:
ParseQuery<ParseRole> query = ParseRole.getQuery();
Intent intent = getActivity().getIntent();
String groupId = intent.getStringExtra("groupId");
query.whereEqualTo("objectId", groupId);
groupUsers = new ArrayList<String>();
query.findInBackground(new FindCallback<ParseRole>() {
#Override
public void done(List<ParseRole> objects, ParseException e) {
if(e == null) {
for(ParseRole role : objects) {
ParseRelation<ParseUser> usersRelation = role.getRelation("users");
ParseQuery<ParseUser> usersQuery = usersRelation.getQuery();
usersQuery.findInBackground(new FindCallback<ParseUser>() {
#Override
public void done(List<ParseUser> objects, ParseException e) {
for(ParseUser user : objects) {
groupUsers.add(user.getUsername());
}
}
});
}
} else {
Toast.makeText(getActivity(), "ERROR", Toast.LENGTH_SHORT).show();
}
}
});
I passed in the group ID from the Intent that sent me to that Fragment that I was checking and then populated my ListView with the list that I've returned from the query on the Parse database with the specific group ID. I hope this helps anyone else who had the same issue as me. Good luck!
Since you probably want to use it for security as well as making it easier for code/users, look at the Roles security feature.
You can add/remove Users from Roles, and assign ACL permissions to Roles instead of Users. This way when people are added-to/removed-from the Role the permissions don't require any changes.
Initially there was a limit to the number of Roles you were allowed to create based on account type, but this restriction was removed last year I believe.
I'm using Google's mobile backend starter for a project and I want to set the key name myself for some of entities instead of using the auto-generated one.
If I were doing this without the backend I could do something like it describes in the datastore documentation which creates an employee entity with the key name "asalieri":
Entity employee = new Entity("Employee", "asalieri");
Here's the code I'm using to create the entity. I've been trying to use the CloudEntity.setId() function. Upc is a string and it doesn't work when I use a hardcoded string either.
CloudEntity avg = new CloudEntity("Aggregate");
avg.setId(upc);
avg.put("averagePrice", sum/count);
insertAverage(avg);
private void insertAverage(CloudEntity avg) {
CloudCallbackHandler<CloudEntity> handler = new CloudCallbackHandler<CloudEntity>() {
#Override
public void onComplete(final CloudEntity result) {
Toast.makeText(AddProduct.this, "Average updated.", Toast.LENGTH_LONG).show();
}
#Override
public void onError(final IOException exception) {
handleEndpointException(exception);
}
};
// execute the insertion with the handler
getCloudBackend().insert(avg, handler);
}
When I run the app everything works fine except that the new entity doesn't have the custom ID that I set.
The only thing I can think of is that setId() isn't supposed to do what I think it does but I've been digging through the code and haven't been able to find another way to do what I want.
Does anyone know why this isn't working?
I'm a Googler on the MBS project. I recreated your issue and first glance shows this as a bug on our side. I'll edit my response with updates.
Would this workaround be ok until we push a fix?
avg.put("samId", upc)