package com.openfeint.api.resource;
import java.util.List;
import android.content.res.Resources;
import com.openfeint.api.Notification;
import com.openfeint.api.R;
import com.openfeint.internal.APICallback;
import com.openfeint.internal.OpenFeintInternal;
import com.openfeint.internal.notifications.SimpleNotification;
import com.openfeint.internal.request.BlobPostRequest;
import com.openfeint.internal.request.CompressedBlobDownloadRequest;
import com.openfeint.internal.request.CompressedBlobPostRequest;
import com.openfeint.internal.request.DownloadRequest;
import com.openfeint.internal.request.IRawRequestDelegate;
import com.openfeint.internal.request.JSONRequest;
import com.openfeint.internal.request.OrderedArgList;
import com.openfeint.internal.resource.BlobUploadParameters;
import com.openfeint.internal.resource.DoubleResourceProperty;
import com.openfeint.internal.resource.IntResourceProperty;
import com.openfeint.internal.resource.LongResourceProperty;
import com.openfeint.internal.resource.NestedResourceProperty;
import com.openfeint.internal.resource.Resource;
import com.openfeint.internal.resource.ResourceClass;
import com.openfeint.internal.resource.ScoreBlobDelegate;
import com.openfeint.internal.resource.StringResourceProperty;
/**
* The resource class that represents a Score posted to OpenFeint.
* This class is used to post high scores, and the list of current high scores
* for a given Leaderboard is contained in its highScores field.
*
* @author Aurora Feint, Inc.
*/
public class Score extends Resource {
/**
* When posting a score to a Leaderboard, use this constructor. No other fields need be
* filled out before calling submitTo(Leaderboard).
* @param score The user's score.
*/
public Score(long score) { this.score = score; }
/**
* When posting a score to a Leaderboard, and you want to have the Leaderboard display a
* different string than just the numeric score, use this constructor. No other fields
* need be filled out before calling submitTo(Leaderboard).
* @param score The user's score. (Ranking is calculated using this value!)
* @param displayText The string that should show on the leaderboard, if desired to be different than score.
*/
public Score(long score, String displayText) { this.score = score; this.displayText = displayText; }
/**
* The User who posted this score.
*/
public User user;
/**
* The numeric value of the score.
*/
public long score;
/**
* Approximate rank in the Leaderboard.
*/
public int rank;
/**
* The resource ID of the Leaderboard that this score came from.
*/
public int leaderboardId; // unsigned
/**
* The text displayed on the leaderboard. If this is empty or null,
* the text displayed on the leaderboard will be this.score.
*/
public String displayText;
/**
* A bit of custom data you can attach to a Score before submission,
* and ideally get it back later. This field is currently unsupported.
*/
public String customData;
/**
* The submitting User's latitude at the time they submitted this score.
*/
public double latitude;
/**
* The submitting User's longitude at the time they submitted this score.
*/
public double longitude;
/**
* A blob of data associated with this score. You can use this field to store replays
* associated with the high score, among other things. If you set this before calling
* {@link submitTo()}, this blob will be uploaded to the server. However, it is not
* downloaded by default - you need to call {@link downloadBlob()} if you want to
* retrieve the data.
* You can tell if a score has a blob to download by calling the hasBlob() method,
* regardless of whether the blob has been downloaded yet.
*/
public byte blob[];
// Internal fields:
/**
* If this score has a blob of data associated with it, like a replay
* or screenshot, this is the URL to that data blob. This field is
* currently unsupported.
*/
private String blobUrl;
/**
* If this score has a blob of data associated with it, like a replay
* or screenshot, these are the parameters used to upload the blob.
* This field is currently unsupported.
*/
private BlobUploadParameters blobUploadParameters;
/**
* This method can be called to determine whether a Score object retrieved from
* a Leaderboard method has a blob available for downloading. If it's true, you
* can call {@link downloadBlob()} to retrieve the blob, and store it in the {@link blob}
* member variable.
* @return whether this Score has an associated blob that can be downloaded.
*/
public boolean hasBlob() {
return blobUrl != null;
}
/**
* A callback class you can extend for calling Score.submitTo().
*/
public abstract static class SubmitToCB extends APICallback {
/**
* When Score.submitTo() completes, this method will be called to
* let you know if the Score submission succeeded.
* @param newHighScore Will be true if this was a new high Score for this user.
*/
public abstract void onSuccess(boolean newHighScore);
/**
* If you supplied a blob (via setting the {@link blob} member variable),
* and the score submitted was a new high score, then we'll attempt to upload the blob.
* This will be called if the blob upload succeeds. This will happen after the normal
* onSuccess/onFailure.
*/
public void onBlobUploadSuccess() {}
/**
* If you supplied a blob (via setting the {@link blob} member variable),
* and the score submitted was a new high score, then we'll attempt to upload the blob.
* This will be called if the blob upload fails. This will happen after the normal
* onSuccess/onFailure.
*/
public void onBlobUploadFailure(String exceptionMessage) {}
}
/**
* Call this method to submit a Score you created to a given Leaderboard.
* For example, to submit a local user score of 123 to the Leaderboard with
* the resource ID 456, and report to the SubmitToCB object cb, you would do:
* <pre>new Score(123).submitTo(new Leaderboard("456"), cb);</pre>
* @param cb The callback object that will be notified on request completion.
*/
public void submitTo(final Leaderboard leaderboard, final SubmitToCB cb) {
final String path = "/xp/games/" + OpenFeintInternal.getInstance().getAppID() + "/leaderboards/" + leaderboard.resourceID() + "/high_scores";
OrderedArgList args = new OrderedArgList();
args.put("high_score[score]", new Long(this.score).toString());
if (this.displayText != null) {
args.put("high_score[display_text]", this.displayText);
}
final boolean uploadBlob = (blob != null);
args.put("high_score[has_blob]", uploadBlob ? "1" : "0");
JSONRequest req = new JSONRequest(args) {
@Override public boolean wantsLogin() { return true; }
@Override public String method() { return "POST"; }
@Override public String path() { return path; }
@Override protected void onResponse(int responseCode, Object responseBody) {
if (201 == responseCode) {
Resources r = OpenFeintInternal.getInstance().getContext().getResources();
SimpleNotification.show(r.getString(R.string.of_score_submitted_notification), "@drawable/of_icon_highscore_notification", Notification.Category.HighScore, Notification.Type.Success);
if (cb != null) cb.onSuccess(true);
perhapsUploadBlob(uploadBlob, responseBody);
} else if (200 == responseCode) {
if (cb != null) cb.onSuccess(false);
} else {
onFailure(responseBody);
}
}
@SuppressWarnings("unchecked")
private final void perhapsUploadBlob(final boolean uploadBlob,
Object responseBody) {
if (uploadBlob && responseBody instanceof List<?>) {
List<Score> scores = (List<Score>)responseBody;
Score s = scores.get(0);
BlobPostRequest postRequest = new CompressedBlobPostRequest(s.blobUploadParameters, String.format("blob.%s.bin", s.resourceID()), Score.this.blob);
if (cb != null) {
postRequest.setDelegate(new IRawRequestDelegate() {
public void onResponse(int responseCode, String responseBody) {
if (200 <= responseCode && responseCode < 300) {
cb.onBlobUploadSuccess();
} else {
cb.onBlobUploadFailure(responseBody);
}
}
});
}
postRequest.launch();
}
}
@Override public void onFailure(String exceptionMessage) {
super.onFailure(exceptionMessage);
if (cb != null) {
cb.onFailure(exceptionMessage);
}
}
};
req.launch();
}
/**
* A callback class you can extend for calling Score.downloadBlob().
*/
public abstract static class DownloadBlobCB extends APICallback {
/**
* When downloadBlob() completes, this method will be called to
* let you know if the blob download succeeded.
*/
public abstract void onSuccess();
};
public void downloadBlob(final DownloadBlobCB cb) {
if (hasBlob()) {
DownloadRequest req = new CompressedBlobDownloadRequest() {
@Override public boolean signed() { return false; }
@Override public String url() { return Score.this.blobUrl; }
@Override public String path() { return ""; }
@Override protected void onSuccessDecompress(byte bodyData[]) {
Score.this.blob = bodyData;
if (cb != null) cb.onSuccess();
}
@Override public void onFailure(String exceptionMessage) {
super.onFailure(exceptionMessage);
if (cb != null) cb.onFailure(exceptionMessage);
}
};
req.launch();
} else if (cb != null) {
cb.onFailure(OpenFeintInternal.getRString(R.string.of_no_blob));
}
}
/**
* A delegate class you can install to be notified when the user downloads a blob
* from the Feint Dashboard. This will not be invoked if you
* manually call {@link downloadBlob}. Typically, you'll want to implement blobDownloadedForScore()
* to start your replay-viewing Activity, or whatever other method you want to use
* to display the contents of the blob.
*/
public static abstract class BlobDownloadedDelegate {
/**
* This method is called when the user downloads a blob from the Feint Dashboard.
* @param score The Score that was downloaded. When this method is called, the blob field
* will have already been downloaded.
*/
public void blobDownloadedForScore(Score score) { }
}
/**
* Installs a delegate as the handler to use when the user downloads a blob from the
* Feint Dashboard.
* @param delegate
*/
public static void setBlobDownloadedDelegate(BlobDownloadedDelegate delegate) {
ScoreBlobDelegate.sBlobDownloadedDelegate = delegate;
}
/**
* This constructor is for use by the parser only.
*/
public Score() { }
/**
* This method is used internally by OpenFeint.
*/
public static ResourceClass getResourceClass() {
ResourceClass klass = new ResourceClass (Score.class, "high_score") { public Resource factory () { return new Score (); } };
klass.mProperties.put("score", new LongResourceProperty() { public void set(Resource obj, long val) { ((Score)obj).score = val; } public long get(Resource obj) { return ((Score)obj).score; } });
klass.mProperties.put("rank", new IntResourceProperty() { public void set(Resource obj, int val) { ((Score)obj).rank = val; } public int get(Resource obj) { return ((Score)obj).rank; } });
klass.mProperties.put("leaderboard_id", new IntResourceProperty() { public void set(Resource obj, int val) { ((Score)obj).leaderboardId = val; } public int get(Resource obj) { return ((Score)obj).leaderboardId; } });
klass.mProperties.put("display_text", new StringResourceProperty() { public void set(Resource obj, String val) { ((Score)obj).displayText = val; } public String get(Resource obj) { return ((Score)obj).displayText; } });
klass.mProperties.put("custom_data", new StringResourceProperty() { public void set(Resource obj, String val) { ((Score)obj).customData = val; } public String get(Resource obj) { return ((Score)obj).customData; } });
klass.mProperties.put("lat", new DoubleResourceProperty() { public void set(Resource obj, double val) { ((Score)obj).latitude = val; } public double get(Resource obj) { return ((Score)obj).latitude; } });
klass.mProperties.put("lng", new DoubleResourceProperty() { public void set(Resource obj, double val) { ((Score)obj).longitude = val; } public double get(Resource obj) { return ((Score)obj).longitude; } });
klass.mProperties.put("user", new NestedResourceProperty(User.class) { public void set(Resource obj, Resource val) { ((Score)obj).user = (User)val; } public Resource get(Resource obj) { return ((Score)obj).user; } });
klass.mProperties.put("blob_url", new StringResourceProperty() { public void set(Resource obj, String val) { ((Score)obj).blobUrl = val; } public String get(Resource obj) { return ((Score)obj).blobUrl; } });
klass.mProperties.put("blob_upload_parameters", new NestedResourceProperty(BlobUploadParameters.class) { public void set(Resource obj, Resource val) { ((Score)obj).blobUploadParameters = (BlobUploadParameters)val; } public Resource get(Resource obj) { return ((Score)obj).blobUploadParameters; } });
return klass;
}
}