Skip to content

Commit

Permalink
Add Uploader (#12)
Browse files Browse the repository at this point in the history
* android uploader start

* android uploader start

* remove secret, proper sig calculation

* remove secret, proper sig calculation

* cleanup

* cleanup

* rename

* rename

* update readme and comments

* comments

* fix spelling & comment out upload example
  • Loading branch information
kj13ennett authored Sep 27, 2019
1 parent 56fcdea commit f7213f3
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 4 deletions.
116 changes: 116 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,24 @@ Each successive call of `loadNextPageOfPhotos` will load the next page of photos

Once an album has loaded photos from the server, it will instantiate `PXLPhoto` objects that can be consumed by your UI. `PXLPhoto` exposes all of the data for a photo available through the Pixlee API and offers several image url sizes depending on your needs.

#### Uploading Photos

If you wish to build your application with photo uploading capabilities, you must initialize the `PXLClient` with an additional parameter - your Pixlee secret key. You must set the secret key along with the API key when you call the static method initialize:

```
#!java
PXLClient.initialize(<API KEY>, <SECRET KEY>);
```

Now wen you want to upload a photo in your application, simply call the `uploadImage` method of the PXLAlbum object you are using. This would look something like this:

```
#!java
album.uploadImage("test", "[email protected]", "testuser", "https://timedotcom.files.wordpress.com/2019/05/drake-nba-finals-warning.jpg", true);
```

### Analytics
#### Opened Widget
To fire an opened widget event, simply call the `openedWidget` method of the PXLAlbum or PXLPdpAlbum AFTER data has been returned from the first call of the `loadNextPageOfPhotos` method, and an "Opened Widget" event will be fired containing all of the necessary analytics information.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,9 @@ public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
*/
private void createAlbum() {
Context c = this.getApplicationContext();

PXLClient.initialize("196i8ZzIAhKU8dO2kDe");

album = new PXLAlbum("4503434", c);
PXLAlbumFilterOptions fo = new PXLAlbumFilterOptions();
fo.minTwitterFollowers = 0;
Expand Down Expand Up @@ -239,6 +241,11 @@ private void createAlbum() {
PXLAlbum.RequestHandlers rh = this;
album.loadNextPageOfPhotos(rh);

/* ~~~ content upload example ~~~
album.uploadImage("test", "[email protected]", "K.B.", "https://timedotcom.files.wordpress.com/2019/05/drake-nba-finals-warning.jpg", true);
*/
}

/***
Expand Down Expand Up @@ -303,6 +310,7 @@ private void updateDetailView(PXLPhoto photo) {
pixleeAnalytics.conversion(cartContents, "123", 4);
*/


this.populateDetailActions(photo);
}

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
classpath 'com.android.tools.build:gradle:3.5.0'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Thu May 16 17:06:08 EDT 2019
#Wed Sep 25 12:41:34 EDT 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
30 changes: 30 additions & 0 deletions pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLAlbum.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,36 @@ public boolean loadNextPageOfPhotos(final RequestHandlers handlers) {
return true;
}


/***
* Requests the next page of photos from the Pixlee album. Make sure to set perPage,
* sort order, and filter options before calling.
* @param title - title or caption of the photo being uploaded
* @param email - email address of the submitting user
* @param username - username of the submitting user
* @param photoURI - the URI of the photo being submitted (must be a public URI)
* @param approved - boolean specifying whether the photo should be marked as approved on upload
* @return true if the request was made, false if aborted before the attempt was made
*/
public boolean uploadImage(String title, String email, String username, String photoURI, Boolean approved) {
PXLClient pxlClient = PXLClient.getInstance(context);
JSONObject body = new JSONObject();

try{
body.put("album_id", Integer.parseInt(this.id));
body.put("title", title);
body.put("email", email);
body.put("username", username);
body.put("photo_uri", photoURI);
body.put("approved", approved);

} catch (JSONException e) {
e.printStackTrace();
}

return pxlClient.makePostCall("media", body);
}

/***
* Sets the amount of photos fetched per call of 'loadNextPageOfPhotos'. Will purge previously
* fetched photos. Call 'loadNextPageOfPhotos' after setting.
Expand Down
112 changes: 111 additions & 1 deletion pixleesdk/src/main/java/com/pixlee/pixleesdk/PXLClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.util.Base64;
import android.util.Log;

import com.android.volley.Request;
Expand All @@ -21,12 +22,15 @@
import org.json.JSONObject;
import org.json.JSONException;

import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.io.UnsupportedEncodingException;

import android.provider.Settings.Secure;

import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Mac;
import java.security.InvalidKeyException;
/***
* Manages the configuration of volley and calls to the api. Intended to be used as a singleton,
* so access should occur via the getInstance() method.
Expand All @@ -49,6 +53,7 @@ public class PXLClient {
private static final String url = "https://distillery.pixlee.com/api/v2";
private static final String analyticsUrl = "https://inbound-analytics.pixlee.com";
private static String apiKey = null;
private static String secretKey = null;
private static String android_id = null;

private PXLClient(Context context) {
Expand All @@ -72,6 +77,16 @@ public void putBitmap(String url, Bitmap bitmap) {
});
}

/***
* Must be called before use. Sets the api key.
* @param apiKey
* @param secretKey
*/
public static void initialize(String apiKey, String secretKey) {
PXLClient.apiKey = apiKey;
PXLClient.secretKey = secretKey;
}

/***
* Must be called before use. Sets the api key.
* @param apiKey
Expand Down Expand Up @@ -123,6 +138,18 @@ public ImageLoader getImageLoader() {
return mImageLoader;
}

public String computeHmac(String baseString, String secretKey)
throws NoSuchAlgorithmException, InvalidKeyException, IllegalStateException, UnsupportedEncodingException
{

SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(key);
byte[] bytes = mac.doFinal(baseString.getBytes());
return Base64.encodeToString(bytes, Base64.DEFAULT);
}


/***
* Makes a call to the Pixlee API. Appends api key to the request. Invokes the given callbacks
* on success/error.
Expand Down Expand Up @@ -161,6 +188,88 @@ public void onErrorResponse(VolleyError error) {
return true;
}

/***
* Makes a POST call to the Pixlee API. Appends api key to the request body and signs the request using the secret key.
* @param requestPath - path to hit (will be appended to the base Pixlee api endpoint)
* @param body - key/values to be passed in the POST body
* @return false if no api key or secret set yet, true otherwise
*/
public boolean makePostCall(final String requestPath, final JSONObject body) {
if (PXLClient.apiKey == null || PXLClient.secretKey == null) {
return false;
}
String paramString = String.format("%s=%s", KeyApiKey, apiKey);
String finalUrl = String.format("%s/%s?%s", url, requestPath, paramString);

Log.d(TAG, "POST");

Log.d(TAG, finalUrl);


final String requestBody = body.toString().replace("\\/", "/" );

StringRequest sr = new StringRequest(Request.Method.POST, finalUrl, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("POST CALL Body", requestBody);
Log.d("POST CALL ", response);
}
}, new Response.ErrorListener() {

@Override
public void onErrorResponse(VolleyError error) {
Log.w(TAG, "got an error response");
Log.w(TAG, error.toString());
}
}){
@Override
public String getBodyContentType() {
return "application/json; charset=utf-8";
}

@Override
public byte[] getBody() throws AuthFailureError {
try {
return requestBody == null ? null : requestBody.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8");
return null;
}
}

@Override
public Map getHeaders() throws AuthFailureError {
HashMap headers = new HashMap();
String signature = "";
try {
signature = computeHmac(requestBody, PXLClient.secretKey);

} catch (Exception e){
e.printStackTrace();
}
headers.put("Signature", signature);
headers.put("Content-Type", "application/json");
headers.put("Accept", "application/json");
headers.put("Accept-Encoding", "utf-8");
return headers;
}

@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String responseString = "";
if (response != null) {
responseString = String.valueOf(response.statusCode);
// can get more details such as response.headers
}
return Response.success(responseString, HttpHeaderParser.parseCacheHeaders(response));
}
};

sr.setShouldCache(false);
this.addToRequestQueue(sr);
return true;
}

/***
* Makes a call to the Pixlee Analytics API (limitless beyond). Appends api key, unique id and platform to the request body.
* on success/error.
Expand Down Expand Up @@ -230,4 +339,5 @@ protected Response<String> parseNetworkResponse(NetworkResponse response) {
this.addToRequestQueue(sr);
return true;
}

}

0 comments on commit f7213f3

Please sign in to comment.