package com.openfeint.api.resource; import java.util.Date; import java.util.List; import android.graphics.Bitmap; import com.openfeint.api.R; import com.openfeint.internal.APICallback; import com.openfeint.internal.AchievementUnlockCache; import com.openfeint.internal.OpenFeintInternal; import com.openfeint.internal.notifications.AchievementNotification; import com.openfeint.internal.request.BitmapRequest; import com.openfeint.internal.request.JSONRequest; import com.openfeint.internal.request.OrderedArgList; import com.openfeint.internal.resource.BooleanResourceProperty; import com.openfeint.internal.resource.DateResourceProperty; import com.openfeint.internal.resource.FloatResourceProperty; import com.openfeint.internal.resource.IntResourceProperty; import com.openfeint.internal.resource.Resource; import com.openfeint.internal.resource.ResourceClass; import com.openfeint.internal.resource.StringResourceProperty; /** * The resource class that represents an Achievement in OpenFeint. * You use this class to get a list of Achievements that your * application contains, to query whether the local user has unlocked * them, or to unlock them for the local user. * * @author Aurora Feint, Inc. */ public class Achievement extends Resource { /** * Create an Achievement object with the given resource id. This won't automatically * fill the other fields out - see Achievement.list() if you want to consult other * fields of the Achievement. * * @param resourceID the resource ID of the Achievement. */ public Achievement(String resourceID) { setResourceID(resourceID); } /** * The user-visible title of the Achievement. */ public String title; /** * The user-visible description of the Achievement. */ public String description; /** * How many Feint Points this Achievement awards. */ public int gamerscore; // unsigned /** * The URL of the Achievement icon. */ public String iconUrl; /** * Whether this Achievement is secret or not. */ public boolean isSecret; /** * Whether or not this Achievement has been unlocked by the local user. */ public boolean isUnlocked; /** * The incremental percentage of completion by the local user. The range is 0.0 to 100.0. */ public float percentComplete; /** * If this.isUnlocked, the date at which it was unlocked. */ public java.util.Date unlockDate; /** * The version of your application at which this achievement was introduced. */ public String endVersion; /** * The version of your application at which this achievement was removed. */ public String startVersion; /** * If you've specified an Achievement ordering on the Developer Dashboard, * this is the position in the list of this Achievement. */ public int position; // unsigned /** * A callback class you can extend for calling Achievement.list(). */ public abstract static class ListCB extends APICallback { /** * When Achievement.list() completes, this method will be called with * the list of Achievements for your application. * @param achievements the list of Achievements for your application. */ public abstract void onSuccess(final List<Achievement> achievements); } /** * Call this method to get a list of Achievements that are available * to your application. These are the Achievements that you've created * on the Developer Dashboard. * @param cb The callback object that will be given the list of Achievements. */ public static void list(final ListCB cb) { final String path = "/xp/games/" + OpenFeintInternal.getInstance().getAppID() + "/achievements"; JSONRequest req = new JSONRequest() { @Override public String method() { return "GET"; } @Override public String path() { return path; } @Override public void onSuccess(Object responseBody) { if (null != cb) { try { @SuppressWarnings("unchecked") List<Achievement> achievements = (List<Achievement>)responseBody; cb.onSuccess(achievements); } catch (Exception e) { onFailure(OpenFeintInternal.getRString(R.string.of_unexpected_response_format)); } } } @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 downloadIcon(). */ public abstract static class DownloadIconCB extends APICallback { /** * When downloadIcon() completes, this method will be called with * a Bitmap of the Achievement icon. * @param iconBitmap the Achievement icon, as a Bitmap. */ public abstract void onSuccess(final Bitmap iconBitmap); } /** * Call this method to download the icon of the given Achievement, in * android.graphics.Bitmap format. * @param cb The callback object that will be given the Bitmap of the icon. */ public void downloadIcon(final DownloadIconCB cb) { if (this.iconUrl == null) { if (null != cb) { cb.onFailure(OpenFeintInternal.getRString(R.string.of_null_icon_url)); } return; } BitmapRequest req = new BitmapRequest() { @Override public String method() { return "GET"; } @Override public String url() { return Achievement.this.iconUrl; } @Override public String path() { return ""; } @Override public void onSuccess(Bitmap responseBody) { if (null != cb) { cb.onSuccess(responseBody); } } @Override public void onFailure(String exceptionMessage) { if (null != cb) { cb.onFailure(exceptionMessage); } } }; req.launch(); } /** * A callback class you can extend for calling Achievement.unlock(). */ public abstract static class UnlockCB extends APICallback { /** * Called when the achievement has been unlocked. * @param newUnlock Will be true if this was a newly unlocked achievement for this user. */ public abstract void onSuccess(boolean newUnlock); } /** * Call this method to unlock this Achievement for the currently logged-in user. * @param cb an optional callback object that will be notified when Achievement * unlocking succeeds or fails. */ public void unlock(final UnlockCB cb) { UpdateProgressionCB upCB = null; if (cb != null) { upCB = new UpdateProgressionCB() { @Override public void onSuccess(boolean complete) { cb.onSuccess(complete); } @Override public void onFailure(String exceptionMessage) { cb.onFailure(exceptionMessage); } }; } updateProgression(100.0f, upCB); } /** * A callback class you can extend for calling Achievement.updateProgression(). */ public abstract static class UpdateProgressionCB extends APICallback { /** * Called when the achievement has been updated. NOTE: onFailure() will be called if * you attempt to update the progression with a lower percentComplete than previously existed. * @param complete Will be true if this update caused the achievement to unlock for this user. */ public abstract void onSuccess(boolean complete); } /** * Call this method to update the progression on this Achievement for the currently * logged-in user. * @param pctComplete the percentage completion, between 0.0f and 100.0f. * @param cb an optional callback object that will be notified when Achievement * updating succeeds or fails. Note that if you provide a pctComplete outside of the 0.0f..100.0f range, * it will be clamped, but if you try to update the progression value to a lower value, onFailure() will * be called instead of onSuccess(). */ public void updateProgression(float pctComplete, final UpdateProgressionCB cb) { if (pctComplete > 100.f) pctComplete = 100.f; if (pctComplete < 0.f) pctComplete = 0.f; final String resID = resourceID(); if (null == resID) { if (null != cb) { cb.onFailure(OpenFeintInternal.getRString(R.string.of_achievement_unlock_null)); } return; } if (AchievementUnlockCache.isUnlocked(resID)) { if (cb != null) { cb.onSuccess(false); } return; } final String path = "/xp/games/" + OpenFeintInternal.getInstance().getAppID() + "/achievements/" + resID + "/unlock"; OrderedArgList args = new OrderedArgList(); args.put("percent_complete", new Float(pctComplete).toString()); JSONRequest req = new JSONRequest(args) { @Override public boolean wantsLogin() { return true; } @Override public String method() { return "PUT"; } @Override public String path() { return path; } @Override protected void onResponse(int responseCode, Object responseBody) { if (responseCode >= 200 && responseCode < 300) { final Achievement achievement = (Achievement) responseBody; final int oldPercentComplete = (int)Achievement.this.percentComplete; Achievement.this.shallowCopy(achievement); final int newPercentComplete = (int)Achievement.this.percentComplete; if (201 == responseCode) { AchievementUnlockCache.markAsUnlocked(resID); AchievementNotification.showStatus(achievement); } else if (newPercentComplete > oldPercentComplete) { AchievementNotification.showStatus(achievement); } if (null != cb) { cb.onSuccess(201 == responseCode); } } else { onFailure(responseBody); } } @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 Achievement.load(). */ public abstract static class LoadCB extends APICallback { /** * when Achievement.load() completes, this will be called to let you know * that the fields in the Achievement object on which load() was called * are ready to be read. */ public abstract void onSuccess(); } /** * Call this method to fill out the fields of this Achievement object. If you've * created a Achievement with the (String resourceID) constructor and then call this * method, all the remaining Achievement fields will be filled out for the Achievement * represented by that resource ID. * @param cb The callback object that will be notified when load() completes. */ public void load(final LoadCB cb) { final String resID = resourceID(); if (null == resID) { if (null != cb) { cb.onFailure(OpenFeintInternal.getRString(R.string.of_achievement_load_null)); } return; } final String path = "/xp/games/" + OpenFeintInternal.getInstance().getAppID() + "/achievements/" + resID; JSONRequest req = new JSONRequest() { @Override public String method() { return "GET"; } @Override public String path() { return path; } @Override public void onSuccess(Object responseBody) { Achievement.this.shallowCopy((Achievement)responseBody); if (cb != null) { cb.onSuccess(); } } @Override public void onFailure(String exceptionMessage) { super.onFailure(exceptionMessage); if (cb != null) { cb.onFailure(exceptionMessage); } } }; req.launch(); } /** * This constructor is for use by the parser only. */ public Achievement() { } /** * This method is used internally by OpenFeint. */ public static ResourceClass getResourceClass() { ResourceClass klass = new ResourceClass (Achievement.class, "achievement") { public Resource factory () { return new Achievement (); } }; klass.mProperties.put("title", new StringResourceProperty() { public void set(Resource obj, String val) { ((Achievement)obj).title = val; } public String get(Resource obj) { return ((Achievement)obj).title; } }); klass.mProperties.put("description", new StringResourceProperty() { public void set(Resource obj, String val) { ((Achievement)obj).description = val; } public String get(Resource obj) { return ((Achievement)obj).description; } }); klass.mProperties.put("gamerscore", new IntResourceProperty() { public void set(Resource obj, int val) { ((Achievement)obj).gamerscore = val; } public int get(Resource obj) { return ((Achievement)obj).gamerscore; } }); klass.mProperties.put("icon_url", new StringResourceProperty() { public void set(Resource obj, String val) { ((Achievement)obj).iconUrl = val; } public String get(Resource obj) { return ((Achievement)obj).iconUrl; } }); klass.mProperties.put("is_secret", new BooleanResourceProperty() { public void set(Resource obj, boolean val) { ((Achievement)obj).isSecret = val; } public boolean get(Resource obj) { return ((Achievement)obj).isSecret; } }); klass.mProperties.put("is_unlocked", new BooleanResourceProperty() { public void set(Resource obj, boolean val) { ((Achievement)obj).isUnlocked = val; } public boolean get(Resource obj) { return ((Achievement)obj).isUnlocked; } }); klass.mProperties.put("percent_complete", new FloatResourceProperty() { public void set(Resource obj, float val) { ((Achievement)obj).percentComplete = val; } public float get(Resource obj) { return ((Achievement)obj).percentComplete; } }); klass.mProperties.put("unlocked_at", new DateResourceProperty() { public void set(Resource obj, Date val) { ((Achievement)obj).unlockDate = val; } public Date get(Resource obj) { return ((Achievement)obj).unlockDate; } }); klass.mProperties.put("position", new IntResourceProperty() { public void set(Resource obj, int val) { ((Achievement)obj).position = val; } public int get(Resource obj) { return ((Achievement)obj).position; } }); klass.mProperties.put("end_version", new StringResourceProperty() { public void set(Resource obj, String val) { ((Achievement)obj).endVersion = val; } public String get(Resource obj) { return ((Achievement)obj).endVersion; } }); klass.mProperties.put("start_version", new StringResourceProperty() { public void set(Resource obj, String val) { ((Achievement)obj).startVersion = val; } public String get(Resource obj) { return ((Achievement)obj).startVersion; } }); return klass; } }