From 5b538054fd738a2d54cabc0da1ecb20c6e433625 Mon Sep 17 00:00:00 2001 From: kj13ennett Date: Wed, 17 Jul 2019 18:42:12 -0400 Subject: [PATCH] Filter parity with iOS + ecomm analytics * filter parity with iOS * sort type consitency * add analytics class supporting ATC and Conversion methods, update readme, update example app * PR changes * ints * update examples * pdp albums set album_id --- README.md | 48 ++++++++++- .../pixleeandroidsdk/SampleActivity.java | 80 +++++++++++++++++- .../java/com/pixlee/pixleesdk/PXLAlbum.java | 5 +- .../pixleesdk/PXLAlbumFilterOptions.java | 29 +++++++ .../pixlee/pixleesdk/PXLAlbumSortOptions.java | 6 +- .../pixlee/pixleesdk/PXLAlbumSortType.java | 3 +- .../com/pixlee/pixleesdk/PXLAnalytics.java | 84 +++++++++++++++++++ .../pixlee/pixleesdk/PXLAnalyticsEvents.java | 4 +- .../java/com/pixlee/pixleesdk/PXLClient.java | 1 + .../com/pixlee/pixleesdk/PXLPdpAlbum.java | 31 +------ .../java/com/pixlee/pixleesdk/PXLPhoto.java | 4 +- 11 files changed, 256 insertions(+), 39 deletions(-) create mode 100644 pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAnalytics.java diff --git a/README.md b/README.md index 3b07c1b1..180c6d22 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ PXLAlbumFilterOptions filterOptions = new PXLAlbumFilterOptions(); filterOptions.minTwitterFollowers = 1000; filterOptions.minInstagramFollowers = 2000; PXLAlbumSortOptions sortOptions = new PXLAlbumSortOptions(); -sortOptions.sortType = PXLAlbumSortType.PHOTORANK; +sortOptions.sortType = PXLAlbumSortType.DYNAMIC; sortOptions.descending = true; album.setPerPage(15); album.setFilterOptions(filterOptions); @@ -83,6 +83,52 @@ To fire an opened ligtbox event, simply call the `openedLightbox` method of the photo.openedLightbox(context); ``` +### Ecommerce Analytics + +For triggering all ecommerce analytics events within your app, you'll want to use the `PXLAnalytics` class. Instantiate one with the application context: +``` +#!Java + +Context c = this.getApplicationContext(); +PXLAnalytics pixleeAnalytics = new PXLAnalytics(c); +``` + +#### Add To Cart +To fire an Add To Cart event, simply call the `addToCart` method of the PXLAnalytics object with the necessary parameters, and an "Add To Cart" event will be fired containing all of the necessary analytics information. +The parameters for this method are: +- [Required] sku (String) +- [Required] price (String) +- [Required] quantity (Integer) +- [Optional] currency (String) + +``` +#!java + +pixleeAnalytics.addToCart("sku123", "123", 4); +``` + +#### Conversion +To fire a Conversion event, simply call the `conversion` method of the PXLAnalytics object with the necessary parameters, and a "Conversion" event will be fired containing all of the necessary analytics information. +The parameters for this method are: +- [Required] cartContents (ArrayList>) +- [Required] cartTotal (String) +- [Required] cartTotalQuantity (Integer) +- [Optional] orderId (String) +- [Optional] currency (String) + +``` +#!java + +ArrayList> cartContents = new ArrayList(); +HashMap cart1 = new HashMap(); +cart1.put("price", "123"); +cart1.put("product_sku", "test123"); +cart1.put("quantity", "4"); + +cartContents.add(cart1); +pixleeAnalytics.conversion(cartContents, "123", 4); +``` + To help you get up and running quickly, we've also built an sample application featuring a grid view, list view, and detail view. The adapters simply maintain an ArrayList of PXLPhoto, which is updated via calls to `loadNextPageOfPhotos`. Since the data source contains the full PXLPhoto object, you can easily customize your own widgets to display the desired images and text. The sample also implements a scroll listener which times calls to `loadNextPageOfPhotos` to provide the endless scroll effect. An example of the proper usage of an opened lightbox event is also included in the sample app! diff --git a/app/src/main/java/com/pixlee/pixleeandroidsdk/SampleActivity.java b/app/src/main/java/com/pixlee/pixleeandroidsdk/SampleActivity.java index 2c5aa885..e76cc649 100644 --- a/app/src/main/java/com/pixlee/pixleeandroidsdk/SampleActivity.java +++ b/app/src/main/java/com/pixlee/pixleeandroidsdk/SampleActivity.java @@ -18,6 +18,9 @@ import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.NetworkImageView; import com.pixlee.pixleesdk.PXLAlbum; +import com.pixlee.pixleesdk.PXLAnalytics; +import com.pixlee.pixleesdk.PXLContentSource; +import com.pixlee.pixleesdk.PXLContentType; import com.pixlee.pixleesdk.PXLPdpAlbum; import com.pixlee.pixleesdk.PXLAlbumFilterOptions; import com.pixlee.pixleesdk.PXLAlbumSortOptions; @@ -26,8 +29,11 @@ import com.pixlee.pixleesdk.PXLPhoto; import com.pixlee.pixleesdk.PXLPhotoSize; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; +import java.util.Map; public class SampleActivity extends AppCompatActivity implements PXLAlbum.RequestHandlers { private ArrayList photoList; @@ -173,6 +179,55 @@ private void createAlbum() { PXLAlbumFilterOptions fo = new PXLAlbumFilterOptions(); fo.minTwitterFollowers = 0; fo.minInstagramFollowers = 0; + + /* ~~~ content source and content filter examples ~~~ + ArrayList contentSource = new ArrayList(); + contentSource.add(PXLContentSource.INSTAGRAM); + fo.contentSource = contentSource; + + ArrayList contentType = new ArrayList(); + contentType.add(PXLContentType.IMAGE); + fo.contentType = contentType; + */ + + + /* ~~~ date filter examples ~~~ + fo.submittedDateEnd = new Date(2019, 7, 16); + fo.submittedDateStart = new Date(2019, 7, 17); + */ + + + // fo.filterByRadius = "21.3069,-157.8583,20"; radius filter example + + + /* ~~~ in_categories filter example ~~~ + ArrayList incategories = new ArrayList(); + incategories.add(1234); + incategories.add(5678); + fo.inCategories = incategories; + */ + + /* ~~~ filter_by_userhandle filter example ~~~ + + HashMap userHandleFilter = new HashMap (); + userHandleFilter.put("contains", new String[] {"test1", "test2"}); + fo.filterByUserhandle = userHandleFilter; + + */ + + /* ~~~ computer_vision filter example ~~~ + + HashMap computerVisionFilter = new HashMap (); + computerVisionFilter.put("contains", new String[] {"hat"}); + fo.computerVision = computerVisionFilter; + + */ + + + // fo.hasProduct = false; + // fo.hasPermission = false; + // fo.inStockOnly = false; + PXLAlbumSortOptions so = new PXLAlbumSortOptions(); so.sortType = PXLAlbumSortType.RECENCY; so.descending = true; @@ -191,6 +246,7 @@ private void loadMorePhotos() { } private void updateDetailView(PXLPhoto photo) { + Context c = this.getApplicationContext(); this.detailSourceIcon.setImageResource(photo.sourceIconImage()); this.detailImage.setImageUrl(photo.getUrlForSize(PXLPhotoSize.MEDIUM).toString(), PXLClient.getInstance(this).getImageLoader()); this.detailText.setText(photo.photoTitle); @@ -220,7 +276,29 @@ private void updateDetailView(PXLPhoto photo) { } else { actionLinksLayout.setVisibility(View.GONE); } - photo.openedLightbox(getApplicationContext()); + + photo.openedLightbox(c); // Opened Lightbox Analytics Example + + /* ~~~ Add to cart analytics example ~~~ + + PXLAnalytics pixleeAnalytics = new PXLAnalytics(c); + pixleeAnalytics.addToCart("sku123", "123", 4); + + */ + + + /* ~~~ Conversion analytics example ~~~ + + ArrayList> cartContents = new ArrayList(); + HashMap cart1 = new HashMap(); + cart1.put("price", "123"); + cart1.put("product_sku", "test123"); + cart1.put("quantity", "4"); + + cartContents.add(cart1); + pixleeAnalytics.conversion(cartContents, "123", 4); + */ + this.populateDetailActions(photo); } diff --git a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbum.java b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbum.java index 4df86c50..359adafe 100644 --- a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbum.java +++ b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbum.java @@ -46,6 +46,9 @@ public void JsonReceived(JSONObject response) { this.page = response.getInt("page"); this.perPage = response.getInt(("per_page")); this.hasMore = response.getBoolean(("next")); + if(this.id == null){ + this.id = String.valueOf(response.getInt("album_id")); + } //add placeholders for photos if they haven't been loaded yet if (this.photos.size() < (this.page - 1) * this.perPage) { for (int i = this.photos.size(); i < (this.page - 1) * this.perPage; i++) { @@ -199,7 +202,7 @@ public boolean openedWidget() { } try{ - body.put("album_id", this.id); + body.put("album_id", Integer.parseInt(this.id)); body.put("per_page", this.perPage); body.put("page", this.page); body.put("photos", stringBuilder.toString()); diff --git a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumFilterOptions.java b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumFilterOptions.java index 7eacabfb..a76ecb80 100644 --- a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumFilterOptions.java +++ b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumFilterOptions.java @@ -6,6 +6,9 @@ import java.util.ArrayList; import java.util.Date; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Map; /*** * Represents the filter options for a PXLAlbum. Initialize an instance then set the desired @@ -28,6 +31,11 @@ public class PXLAlbumFilterOptions { public Boolean hasPermission; public Boolean hasProduct; public Boolean inStockOnly; + public ArrayList inCategories; + public HashMap filterByUserhandle; + public HashMap computerVision; + public HashMap filterByLocation; + public String filterByRadius; /*** * Generates the JSON string representing the filter options. @@ -42,6 +50,7 @@ public String toParamString() { jsonFilters.putOpt("starred_photos", starredPhotos); jsonFilters.putOpt("deleted_photos", deletedPhotos); jsonFilters.putOpt("flagged_photos", flaggedPhotos); + jsonFilters.putOpt("filter_by_radius", filterByRadius); //TODO: handle the arrays and dates properly if (contentSource != null && contentSource.size() > 0) { JSONArray sources = new JSONArray(); @@ -68,6 +77,26 @@ public String toParamString() { jsonFilters.putOpt("has_permission", hasPermission); jsonFilters.putOpt("has_product", hasProduct); jsonFilters.putOpt("in_stock_only", inStockOnly); + + if (inCategories != null && inCategories.size() > 0) { + JSONArray categoriesJson = new JSONArray(inCategories); + jsonFilters.put("in_categories", categoriesJson); + } + + if(filterByUserhandle != null && !filterByUserhandle.isEmpty()){ + JSONObject userhandleJson = new JSONObject(filterByUserhandle); + jsonFilters.put("filter_by_userhandle", userhandleJson); + } + + if(computerVision != null && !computerVision.isEmpty()){ + JSONObject computerVisionJson = new JSONObject(computerVision); + jsonFilters.put("computer_vision", computerVisionJson); + } + + if(filterByLocation != null && !filterByLocation.isEmpty()){ + JSONObject filterByLocationJson = new JSONObject(filterByLocation); + jsonFilters.put("filter_by_location", filterByLocationJson); + } } catch (JSONException e) { e.printStackTrace(); } diff --git a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumSortOptions.java b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumSortOptions.java index 966c2a26..c1cbf01c 100644 --- a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumSortOptions.java +++ b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumSortOptions.java @@ -20,8 +20,10 @@ public class PXLAlbumSortOptions { public String toParamString() { JSONObject params = new JSONObject(); try { - params.put(sortType.value, true); - params.put(KeyDesc, descending); + if(sortType != PXLAlbumSortType.NONE) { + params.put(sortType.value, true); + params.put(KeyDesc, descending); + } } catch (JSONException e) { e.printStackTrace(); } diff --git a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumSortType.java b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumSortType.java index 34bbc445..38f5c112 100644 --- a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumSortType.java +++ b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbumSortType.java @@ -9,7 +9,8 @@ public enum PXLAlbumSortType { PIXLEE_SHARES ("pixlee_shares"), PIXLEE_LIKES ("pixlee_likes"), POPULARITY ("popularity"), - PHOTORANK ("photorank"); + DYNAMIC ("dynamic"), + NONE ("none"); public final String value; diff --git a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAnalytics.java b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAnalytics.java new file mode 100644 index 00000000..6dafa5a8 --- /dev/null +++ b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAnalytics.java @@ -0,0 +1,84 @@ +package com.pixlee.pixleesdk; + +import android.content.Context; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; + + +public class PXLAnalytics { + private static final String TAG = "PXLAnalytics"; + + protected Context context; + + + /*** + * Constructor requires the context, which will be passed along to the PXLClient + * for volley configuration. + * @param context - context which will be used for volley configuration + */ + public PXLAnalytics(Context context) { + this.context = context; + } + + public void addToCart(String sku, String price, Integer quantity, String currency) { + PXLClient pxlClient = PXLClient.getInstance(this.context); + JSONObject body = new JSONObject(); + + try{ + body.put("product_sku", sku); + body.put("price", price); + body.put("quantity", quantity); + if(currency != null){ + body.put("currency", currency); + } + + + } catch (JSONException e) { + e.printStackTrace(); + } + + pxlClient.makeAnalyticsCall("events/addToCart", body); + } + + public void addToCart(String sku, String price, Integer quantity) { + this.addToCart(sku, price, quantity, null); + } + + + public void conversion(ArrayList> cartContents, String cartTotal, Integer cartTotalQuantity, String orderId, String currency){ + PXLClient pxlClient = PXLClient.getInstance(this.context); + JSONObject body = new JSONObject(); + + try{ + JSONArray cartContentsJson = new JSONArray(cartContents); + body.put("cart_contents", cartContentsJson); + body.put("cart_total", cartTotal); + body.put("cart_total_quantity", cartTotalQuantity); + if(currency != null){ + body.put("currency", currency); + } + if(orderId != null){ + body.put("order_id", orderId); + } + + } catch (JSONException e) { + e.printStackTrace(); + } + + pxlClient.makeAnalyticsCall("events/conversion", body); + + } + public void conversion(ArrayList> cartContents, String cartTotal, Integer cartTotalQuantity, String orderId){ + this.conversion(cartContents, cartTotal, cartTotalQuantity, orderId, null); + } + public void conversion(ArrayList> cartContents, String cartTotal, Integer cartTotalQuantity) { + this.conversion(cartContents, cartTotal, cartTotalQuantity, null, null); + + } + +} diff --git a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAnalyticsEvents.java b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAnalyticsEvents.java index 65304009..ac343a6e 100644 --- a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAnalyticsEvents.java +++ b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAnalyticsEvents.java @@ -7,7 +7,9 @@ public enum PXLAnalyticsEvents { OPENED_WIDGET ("opened widget"), - OPENED_LIGHTBOX ("opened lightbox"); + OPENED_LIGHTBOX ("opened lightbox"), + ADD_TO_CART ("add to cart"), + CONVERSION ("conversion"); public final String value; diff --git a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLClient.java b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLClient.java index a1fc7926..0b6fd1bf 100644 --- a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLClient.java +++ b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLClient.java @@ -189,6 +189,7 @@ public boolean makeAnalyticsCall(final String requestPath, final JSONObject body StringRequest sr = new StringRequest(Request.Method.POST, finalUrl, new Response.Listener() { @Override public void onResponse(String response) { + Log.d("ANALYTICS CALL Body", requestBody); Log.d("ANALYTICS CALL ", response); } }, new Response.ErrorListener() { diff --git a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLPdpAlbum.java b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLPdpAlbum.java index 44ab7b9f..eac76dca 100644 --- a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLPdpAlbum.java +++ b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLPdpAlbum.java @@ -22,6 +22,7 @@ public class PXLPdpAlbum extends PXLAlbum { public PXLPdpAlbum(String sku, Context context) { super(sku, context); this.sku = sku; + this.id = null; this.page = 0; this.perPage = DefaultPerPage; this.hasMore = true; @@ -74,34 +75,4 @@ protected HashMap getRequestParams(int desiredPage) { paramMap.put(PXLClient.KeySku, sku); return paramMap; } - - @Override - public boolean openedWidget() { - PXLClient pxlClient = PXLClient.getInstance(context); - JSONObject body = new JSONObject(); - StringBuilder stringBuilder = new StringBuilder(); - for (int i = 0; i < this.photos.size(); i++) { - try { - stringBuilder.append(this.photos.get(i).id); - if(i != this.photos.size() - 1){ - stringBuilder.append(","); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - try{ - body.put("sku", this.sku); - body.put("per_page", this.perPage); - body.put("page", this.page); - body.put("photos", stringBuilder.toString()); - - } catch (JSONException e) { - e.printStackTrace(); - } - - pxlClient.makeAnalyticsCall("events/openedWidget", body); - return true; - } } diff --git a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLPhoto.java b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLPhoto.java index 2c2f6dfb..5eb89e67 100644 --- a/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLPhoto.java +++ b/pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLPhoto.java @@ -282,8 +282,8 @@ public boolean openedLightbox(Context context) { PXLClient pxlClient = PXLClient.getInstance(context); JSONObject body = new JSONObject(); try{ - body.put("album_id", this.album.id); - body.put("album_photo_id", this.albumPhotoId); + body.put("album_id", Integer.parseInt(this.album.id)); + body.put("album_photo_id", Integer.parseInt(this.albumPhotoId)); } catch (JSONException e) { e.printStackTrace();